Page MenuHomeIn-Portal Phabricator

in-commerce
No OneTemporary

File Metadata

Created
Thu, Feb 27, 12:49 AM

in-commerce

This file is larger than 256 KB, so syntax highlighting was skipped.
Index: branches/5.2.x/constants.php
===================================================================
--- branches/5.2.x/constants.php (revision 14640)
+++ branches/5.2.x/constants.php (revision 14641)
@@ -1,84 +1,129 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
// order statuses
define('ORDER_STATUS_INCOMPLETE', 0);
define('ORDER_STATUS_PENDING', 1);
define('ORDER_STATUS_BACKORDERS', 2);
define('ORDER_STATUS_TOSHIP', 3);
define('ORDER_STATUS_PROCESSED', 4);
define('ORDER_STATUS_DENIED', 5);
define('ORDER_STATUS_ARCHIVED', 6);
/**
* ID of order, that 100% not in database
*
*/
define('FAKE_ORDER_ID', -1);
define('PRODUCT_TYPE_TANGIBLE', 1);
define('PRODUCT_TYPE_SUBSCRIPTION', 2);
define('PRODUCT_TYPE_SERVICE', 3);
define('PRODUCT_TYPE_DOWNLOADABLE', 4);
define('PRODUCT_TYPE_PACKAGE', 5);
- // payment gateway processing satuses
+ // payment gateway processing statuses
define('SHIPPING_CONTROL_DIRECT', 3);
define('SHIPPING_CONTROL_PREAUTH', 4);
// gift certificate statuses
define('gcENABLED', 1);
define('gcUSED', 2);
define('gcDISABLED', 0);
define('USPS_LABEL_FOLDER', WRITEABLE . '/user_files/labels/');
+ define('ORDER_SHIP_ALL_TOGETHER', 0);
+ define('ORDER_SHIP_BACKORDERS_SEPARATELY', 1);
+ define('ORDER_SHIP_BACKORDERS_UPON_AVAILABLE', 2);
+
+ define('ORDER_GROUP_SHIPPMENTS_AUTO', 0);
+ define('ORDER_GROUP_SHIPPMENTS_MANUAL', 1);
+
+ define('PRODUCT_SHIPPING_MODE_ANY_AND_SELECTED', 0);
+ define('PRODUCT_SHIPPING_MODE_SELECTED_ONLY', 1);
+
+ class OrderCheckoutError {
+ const STATE_CHANGED = 10;
+ const CHANGED_AFTER_LOGIN = 11;
+
+ const QTY_UNAVAILABLE = 20; // orditems:Quantity (users tries to order more, then in stock)
+ const QTY_OUT_OF_STOCK = 21; // orditems:Quantity (product was sold out, after it was added to cart, but before checkout)
+ const QTY_CHANGED_TO_MINIMAL = 22; // orditems:Quantity (qty change to minimal available by price brackets)
+
+ const COUPON_APPLIED = 30; // orditems:ItemData, general
+ const COUPON_REMOVED = 31; // orditems:ItemData, general
+ const COUPON_REMOVED_AUTOMATICALLY = 32; // when discount is more efficient, then coupon being applied
+ const COUPON_CODE_INVALID = 33; // general
+ const COUPON_CODE_EXPIRED = 34; // general
+
+ const GC_APPLIED = 40; // general
+ const GC_REMOVED = 41; // general
+ const GC_REMOVED_AUTOMATICALLY = 42; // general
+ const GC_CODE_INVALID = 43; // general
+ const GC_CODE_EXPIRED = 44; // general
+
+ // new, to integrate
+ const DISCOUNT_APPLIED = 50; // orditems:ItemData, general
+ const DISCOUNT_REMOVED = 51; // orditems:ItemData, general
+
+ const FIELD_UPDATE_SUCCESS = 60; // when order item field is changed + not discount/coupon related
+ const FIELD_UPDATE_ERROR = 61; // when order item field can't be changed + not discount/coupon related
+ }
+
+ class OrderCheckoutErrorType {
+ const PRODUCT = 1;
+ const COUPON = 2;
+ const GIFT_CERTIFICATE = 3;
+ const DISCOUNT = 4;
+ }
+
class ProductInventory {
const DISABLED = 0;
const BY_PRODUCT = 1;
const BY_OPTIONS = 2;
}
class ProductBackorder {
const AUTO = 2;
const ALWAYS = 1;
const NEVER = 0;
}
class OptionCombinationPriceType {
const EQUALS = 1;
const FLAT = 2;
const PECENT = 3;
}
class DiscountType {
const FLAT = 1;
const PERCENT = 2;
}
class DiscountItemType {
const PRODUCT = 1;
const CATEGORY = 2;
const WHOLE_ORDER = 0;
}
class CouponType {
const FLAT = 1;
const PERCENT = 2;
}
class CouponItemType {
const PRODUCT = 1;
const CATEGORY = 2;
const WHOLE_ORDER = 0;
}
Index: branches/5.2.x/units/helpers/order_helper.php
===================================================================
--- branches/5.2.x/units/helpers/order_helper.php (nonexistent)
+++ branches/5.2.x/units/helpers/order_helper.php (revision 14641)
@@ -0,0 +1,142 @@
+<?php
+
+ defined('FULL_PATH') or die('restricted access!');
+
+ class OrderHelper extends kHelper
+ {
+ /**
+ * Returns various information about given order
+ *
+ * @param OrdersItem $object
+ * @param string $currency
+ * @param bool $remove_errors
+ * @return Array
+ */
+ function getOrderInfo(&$object, $currency, $remove_errors = true)
+ {
+ $errors = $this->Application->RecallVar('checkout_errors');
+
+ $ret = Array (
+ 'order' => Array (
+ 'CouponId' => (int)$object->GetDBField('CouponId'),
+ 'CouponName' => (string)$object->GetDBField('CouponName'),
+ 'GiftCertificateId' => (int)$object->GetDBField('GiftCertificateDiscount'),
+ 'GiftCertificateDiscount' => $this->convertCurrency($object->GetDBField('GiftCertificateDiscount'), $currency),
+ 'DiscountTotal' => $this->convertCurrency($object->GetDBField('DiscountTotal'), $currency),
+ 'SubTotal' => $this->convertCurrency($object->GetDBField('SubTotal'), $currency),
+ ),
+ 'items' => Array (),
+ 'errors' => $errors ? unserialize($errors) : Array (),
+ );
+
+ $items =& $this->Application->recallObject('orditems', 'orditems_List', Array ('per_page' => -1));
+ /* @var $items kDBList */
+
+ $items->Query();
+ $items->GoFirst();
+
+ if ( $items->EOL() ) {
+ return $ret;
+ }
+
+ $product =& $this->Application->recallObject('p', null, Array ('skip_autoload' => true));
+ /* @var $product kCatDBItem */
+
+ $sql = $product->GetSelectSQL() . '
+ WHERE ' . $product->TableName . '.' . $product->IDField . ' IN (' . implode(',', $items->GetCol('ProductId')) . ')';
+ $products = $this->Conn->Query($sql, $product->IDField);
+
+ while ( !$items->EOL() ) {
+ // prepare product from order item
+ $product->LoadFromHash( $products[ $items->GetDBField('ProductId') ] );
+ $this->Application->SetVar('orditems_id', $items->GetID()); // for edit/delete links using GET
+
+ // weird code from orditems:PrintList
+ $this->Application->SetVar('p_id', $product->GetID());
+ $this->Application->SetVar('m_cat_id', $product->GetDBField('CategoryId'));
+
+ // collect order item info
+ $url_params = Array (
+ 'p_id' => $product->GetID(),
+ 'm_cat_id' => $product->GetDBField('CategoryId'),
+ 'pass' => 'm,p',
+ );
+
+ $product_url = $this->Application->HREF('__default__', '', $url_params);
+
+ $item_data = $items->GetDBField('ItemData');
+ $item_data = $item_data ? unserialize($item_data) : Array ();
+
+ $row_index = $items->GetDBField('ProductId') . ':' . $items->GetDBField('OptionsSalt') . ':' . $items->GetDBField('BackOrderFlag');
+
+ $ret['items'][$row_index] = Array (
+ 'product_url' => $product_url,
+ 'product_type' => $product->GetDBField('Type'),
+ 'options' => isset($item_data['Options']) ? $item_data['Options'] : false,
+ 'free_promo_shipping' => $this->eligibleForFreePromoShipping($items),
+
+ 'fields' => Array (
+ 'OrderItemId' => $items->GetDBField('OrderItemId'),
+ 'ProductName' => $items->GetDBField('ProductName'),
+ 'BackOrderFlag' => (int)$items->GetDBField('BackOrderFlag'),
+ 'FlatPrice' => $this->convertCurrency($items->GetDBField('FlatPrice'), $currency),
+ 'Price' => $this->convertCurrency($items->GetDBField('Price'), $currency),
+ 'Quantity' => (int)$items->GetDBField('Quantity'),
+ 'Virtual' => (int)$items->GetDBField('Virtual'),
+ 'Type' => (int)$product->GetDBField('Type'),
+
+ 'cust_Availability' => $product->GetDBField('cust_Availability'),
+ ),
+ );
+
+ $items->GoNext();
+ }
+
+ if ( $remove_errors ) {
+ $this->Application->RemoveVar('checkout_errors');
+ }
+
+ return $ret;
+ }
+
+ function convertCurrency($amount, $currency)
+ {
+ $converter =& $this->Application->recallObject('kCurrencyRates');
+ /* @var $converter kCurrencyRates */
+
+ // convert primary currency to selected (if they are the same, converter will just return)
+ return (float)$converter->Convert($amount, 'PRIMARY', $this->getISO($currency));
+ }
+
+ function getISO($currency)
+ {
+ if ($currency == 'selected') {
+ $iso = $this->Application->RecallVar('curr_iso');
+ }
+ elseif ($currency == 'primary' || $currency == '') {
+ $iso = $this->Application->GetPrimaryCurrency();
+ }
+ else { //explicit currency
+ $iso = strtoupper($currency);
+ }
+
+ return $iso;
+ }
+
+ /**
+ * Checks, that given order item is eligible for free promo shipping
+ *
+ * @param kDBItem|kDBList $order_item
+ * @return bool
+ */
+ function eligibleForFreePromoShipping(&$order_item)
+ {
+ if ( $order_item->GetDBField('Type') != PRODUCT_TYPE_TANGIBLE ) {
+ return false;
+ }
+
+ $free_shipping = $order_item->GetDBField('MinQtyFreePromoShipping');
+
+ return $free_shipping > 0 && $free_shipping <= $order_item->GetDBField('Quantity');
+ }
+ }
Property changes on: branches/5.2.x/units/helpers/order_helper.php
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+LF
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+Id
\ No newline at end of property
Index: branches/5.2.x/units/helpers/helpers_config.php
===================================================================
--- branches/5.2.x/units/helpers/helpers_config.php (nonexistent)
+++ branches/5.2.x/units/helpers/helpers_config.php (revision 14641)
@@ -0,0 +1,14 @@
+<?php
+
+defined('FULL_PATH') or die('restricted access!');
+
+ $config = Array (
+
+ 'Prefix' => 'in-commerce-helpers',
+
+ 'EventHandlerClass' => Array ('class' => 'kEventHandler', 'file' => '', 'build_event' => 'OnBuild'),
+
+ 'RegisterClasses' => Array (
+ Array ('pseudo' => 'OrderHelper', 'class' => 'OrderHelper', 'file' => 'order_helper.php', 'build_event' => '', 'require_classes' => 'kHelper'),
+ ),
+ );
Property changes on: branches/5.2.x/units/helpers/helpers_config.php
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+LF
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+Id
\ No newline at end of property
Index: branches/5.2.x/units/coupons/coupons_config.php
===================================================================
--- branches/5.2.x/units/coupons/coupons_config.php (revision 14640)
+++ branches/5.2.x/units/coupons/coupons_config.php (revision 14641)
@@ -1,187 +1,176 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
$config = Array(
'Prefix' => 'coup',
'ItemClass' => Array('class'=>'kDBItem','file'=>'','build_event'=>'OnItemBuild'),
'ListClass' => Array('class'=>'kDBList','file'=>'','build_event'=>'OnListBuild'),
'EventHandlerClass' => Array('class'=>'CouponsEventHandler','file'=>'coupons_event_handler.php','build_event'=>'OnBuild'),
'TagProcessorClass' => Array('class'=>'CouponsTagProcessor','file'=>'coupons_tag_processor.php','build_event'=>'OnBuild'),
'AutoLoad' => true,
- 'Hooks' => Array(
- Array(
- 'Mode' => hBEFORE,
- 'Conditional' => false,
- 'HookToPrefix' => 'ord',
- 'HookToSpecial' => '',
- 'HookToEvent' => Array( 'OnUpdateCart', 'OnCheckout' ),
- 'DoPrefix' => '',
- 'DoSpecial' => '',
- 'DoEvent' => 'OnApplyCoupon',
- ),
- ),
+
'QueryString' => Array(
1 => 'id',
2 => 'Page',
3 => 'PerPage',
4 => 'event',
5 => 'mode',
),
'IDField' => 'CouponId',
'StatusField' => Array('Status'),
'TitleField' => 'Name',
'TableName' => TABLE_PREFIX.'ProductsCoupons',
'SubItems' => Array('coupi'),
'TitlePresets' => Array(
'default' => Array( 'new_status_labels' => Array('coup'=>'!la_title_Adding_Coupon!'),
'edit_status_labels' => Array('coup'=>'!la_title_Editing_Coupon!'),
'new_titlefield' => Array('coup'=>'!la_title_New_Coupon!'),
),
'coupons_list'=>Array('prefixes' => Array('coup_List'),
'format' => "!la_title_Coupons!",
),
'coupons_edit'=>Array( 'prefixes' => Array('coup'),
'format' => "#coup_status# '#coup_titlefield#' - !la_title_General!",
),
'coupons_items'=>Array('prefixes' => Array('coup','coupi_List'),
'format' => "#coup_status# '#coup_titlefield#' - !la_title_CouponItems!",
),
'coupons_clone'=>Array('prefixes' => Array('coup'),
'format' => "!la_CloneCoupon!",
),
'coupon_selector' => Array('format' => '!la_title_CouponSelector!'),
),
'EditTabPresets' => Array (
'Default' => Array (
'general' => Array ('title' => 'la_tab_General', 't' => 'in-commerce/discounts/coupon_edit', 'priority' => 1),
'items' => Array ('title' => 'la_tab_CouponsItems', 't' => 'in-commerce/discounts/coupon_items', 'priority' => 2),
),
),
'PermSection' => Array('main' => 'in-commerce:coupons'),
'Sections' => Array(
'in-commerce:coupons' => Array(
'parent' => 'in-commerce:discounts_folder',
'icon' => 'discounts_coupons',
'label' => 'la_tab_Coupons',
'url' => Array('t' => 'in-commerce/discounts/coupons_list', 'pass' => 'm'),
'permissions' => Array('view', 'add', 'edit', 'delete', 'advanced:approve', 'advanced:decline'),
'priority' => 3.2, // <parent_priority>.<own_priority>, because this section replaces parent in tree
'type' => stTAB,
),
),
'ListSQLs' => Array( ''=>'SELECT %1$s.* %2$s FROM %1$s',
), // key - special, value - list select sql
'ItemSQLs' => Array( ''=>'SELECT * FROM %1$s',
),
'ListSortings' => Array (
'' => Array(
'Sorting' => Array('Name' => 'asc'),
)
),
'Fields' => Array(
'CouponId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'Status' => Array (
'type' => 'int', 'formatter' => 'kOptionsFormatter',
'options' => Array ( 1 => 'la_Enabled', 2 => 'la_Used', 0 => 'la_Disabled' ),
'use_phrases' => 1, 'not_null' => 1, 'default' => 1
),
'Name' => Array ( 'type' =>'string', 'required' => 1, 'default' => null, 'max_len' => 255),
'Code' => Array (
'type' => 'string', 'required' => 1, 'default' => null,
'max_len' => 255, 'unique' => Array ('Code'),
),
'Expiration' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => null,),
'GroupId' => Array ('type' => 'int', 'default' => null, ),
'Type' => Array (
'type' => 'int', 'formatter' => 'kOptionsFormatter', 'use_phrases' => 1,
'options' => Array ( 1 => 'la_Flat', 2 => 'la_Percent'/*, 3 => 'la_FreeShipping'*/),
'not_null' => 1, 'default' => 1,
),
'Amount' => Array ('type' => 'double', 'default' => null),
'LastUsedBy' => Array (
'type' => 'int', 'formatter' => 'kLEFTFormatter',
'error_msgs' => Array ('invalid_option' => '!la_error_UserNotFound!'),
'options' => Array (USER_ROOT => 'root', USER_GUEST => 'Guest'),
'left_sql' => 'SELECT %s FROM ' . TABLE_PREFIX . 'PortalUser
WHERE `%s` = \'%s\'','left_key_field'=>'PortalUserId',
'left_title_field' => 'Login', 'required' => 0, 'default' => null,
),
'LastUsedOn' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => NULL),
'NumberOfUses' => Array ('type' => 'int', 'default' => 1),
),
'VirtualFields' => Array (
'CouponCount' => Array ('type' => 'int', 'min_value_inc' => 1, 'default' => 1),
'DefaultExpiration' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => NULL),
),
'Grids' => Array(
'Default' => Array(
'Icons' => Array(
'default' => 'icon16_item.png',
0 => 'icon16_disabled.png',
1 => 'icon16_item.png',
2 => 'icon16_pending.png',
'module' => 'core',
),
'Fields' => Array(
'CouponId' => Array ('title'=>'column:la_fld_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 60, ),
'Name' => Array('filter_block' => 'grid_like_filter', 'width' => 150, ),
'Code' => Array('title'=>'column:la_fld_CouponCode', 'filter_block' => 'grid_like_filter', 'width' => 100, ),
'Expiration' => Array('filter_block' => 'grid_date_range_filter', 'width' => 145, ),
'Type' => Array('filter_block' => 'grid_options_filter', 'width' => 100, ),
'Status' => Array('filter_block' => 'grid_options_filter', 'width' => 100, ),
'Amount' => Array('filter_block' => 'grid_float_range_filter', 'width' => 100, ),
'LastUsedBy' => Array('filter_block' => 'grid_like_filter', 'width' => 140, ),
'LastUsedOn' => Array('filter_block' => 'grid_date_range_filter', 'width' => 140, ),
'NumberOfUses' => Array('filter_block' => 'grid_range_filter', 'width' => 130, ),
),
),
'Radio' => Array(
'Icons' => Array(
'default' => 'icon16_item.png',
0 => 'icon16_disabled.png',
1 => 'icon16_item.png',
2 => 'icon16_pending.png',
'module' => 'core',
),
'Selector' => 'radio',
'Fields' => Array(
'CouponId' => Array ('title'=>'column:la_fld_Id', 'data_block' => 'grid_radio_td', 'filter_block' => 'grid_range_filter', 'width' => 60, ),
'Name' => Array('filter_block' => 'grid_like_filter', 'width' => 150, ),
'Code' => Array('title'=>'column:la_fld_CouponCode', 'filter_block' => 'grid_like_filter', 'width' => 100, ),
'Expiration' => Array('filter_block' => 'grid_date_range_filter', 'width' => 145, ),
'Type' => Array('filter_block' => 'grid_options_filter', 'width' => 100, ),
'Status' => Array('filter_block' => 'grid_options_filter', 'width' => 100, ),
'Amount' => Array('filter_block' => 'grid_float_range_filter', 'width' => 100, ),
'LastUsedBy' => Array('filter_block' => 'grid_like_filter', 'width' => 140, ),
'LastUsedOn' => Array('filter_block' => 'grid_date_range_filter', 'width' => 140, ),
'NumberOfUses' => Array('filter_block' => 'grid_range_filter', 'width' => 130, ),
),
),
),
);
\ No newline at end of file
Index: branches/5.2.x/units/coupons/coupons_event_handler.php
===================================================================
--- branches/5.2.x/units/coupons/coupons_event_handler.php (revision 14640)
+++ branches/5.2.x/units/coupons/coupons_event_handler.php (revision 14641)
@@ -1,281 +1,230 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class CouponsEventHandler extends kDBEventHandler {
/**
* Allows to override standart permission mapping
*
*/
function mapPermissions()
{
parent::mapPermissions();
$permissions = Array(
'OnItemBuild' => Array('self' => true),
'OnApplyClone' => Array('self' => 'add'),
'OnPrepareClone' => Array('self' => 'view'),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
/**
* Prepares coupon cloning
*
* @param kEvent $event
*/
function OnPrepareClone(&$event)
{
$this->StoreSelectedIDs($event);
$event->CallSubEvent('OnNew');
$object =& $event->getObject();
/* @var $object kDBItem */
$this->setCloningRequired($object);
$clone_count = $this->Application->RecallVar('CoupLastCloneCount');
if ( is_numeric($clone_count) && $clone_count > 0 ) {
$object->SetDBField('CouponCount', $clone_count);
}
$expire_days = $this->Application->ConfigValue('Comm_DefaultCouponDuration');
$default_expiration = strtotime('+' . $expire_days . ' days');
$object->SetDBField('DefaultExpiration_date', $default_expiration);
$object->SetDBField('DefaultExpiration_time', $default_expiration);
}
/**
* Occurs before an item has been cloned
* Id of newly created item is passed as event' 'id' param
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeClone(&$event)
{
parent::OnBeforeClone($event);
-
+
$object =& $event->getObject();
/* @var $object kDBItem */
-
+
$this->SetNewCode($object);
-
+
$object->SetDBField('LastUsedBy', NULL);
$object->SetDBField('LastUsedOn', NULL);
$object->SetDBField('NumberOfUses', NULL);
-
+
$expiration = $this->Application->GetVar('clone_coupon_expiration');
$object->SetDBField('Expiration_date', $expiration);
$object->SetDBField('Expiration_time', $expiration);
}
-
+
function OnApplyClone(&$event)
- {
+ {
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
$event->status = kEvent::erFAIL;
return;
}
-
+
$object =& $event->getObject( Array ('skip_autoload' => true) );
/* @var $object kDBItem */
$this->setCloningRequired($object);
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
list($id, $field_values) = each($items_info);
$object->SetFieldsFromHash($field_values);
$object->setID($id);
if ( !$object->Validate() ) {
$event->status = kEvent::erFAIL;
return ;
}
-
+
$temp =& $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
/* @var $temp kTempTablesHandler */
-
+
$original_coupon_ids = $this->getSelectedIDs($event, true);
$clone_count = $object->GetDBField('CouponCount');
- $this->Application->StoreVar('CoupLastCloneCount', $clone_count);
+ $this->Application->StoreVar('CoupLastCloneCount', $clone_count);
$this->Application->SetVar('clone_coupon_expiration', $object->GetDBField('DefaultExpiration'));
-
+
for ($i = 0; $i < $clone_count; $i++) {
$temp->CloneItems($event->Prefix, $event->Special, $original_coupon_ids);
}
- $this->finalizePopup($event);
+ $event->SetRedirectParam('opener', 'u');
}
- function setCloningRequired(&$object)
+ /**
+ * Sets fields required during coupon cloning
+ *
+ * @param kDBItem $object
+ * @return void
+ * @access protected
+ */
+ protected function setCloningRequired(&$object)
{
$this->RemoveRequiredFields($object);
$object->setRequired('CouponCount');
$object->setRequired('DefaultExpiration');
}
function SetNewCode(&$item)
{
do{
- $new_code = $this->RandomCouponCode(10);
+ $new_code = $this->RandomCouponCode();
$exists = $this->Conn->GetOne('SELECT COUNT(*) FROM '.TABLE_PREFIX.'ProductsCoupons WHERE Code='.$this->Conn->qstr($new_code));
if ($exists){
$new_code = false;
}
} while (!$new_code);
$item->SetDBField('Code', $new_code);
}
- function RandomCouponCode($size)
+ function RandomCouponCode()
{
- $rand_code = "";
- for ($i=0; $i<10; $i++){
- $is_letter = rand(0,1);
- if ($is_letter){
- $rand_char = chr(rand(65,90));
- }else{
- $rand_char = rand(0,9);
- }
- $rand_code .= $rand_char;
- }
- return $rand_code;
- }
+ $rand_code = '';
- /**
- * Enter description here...
- *
- * @param kEvent $event
- */
- function OnApplyCoupon(&$event)
- {
- $code = $this->Application->GetVar('coupon_code');
- if ($code == '') {
- return ;
- }
-
- $object =& $event->getObject(Array('skip_autoload' => true));
- $object->Load($code, 'Code');
-
- if (!$object->isLoaded()) {
- $event->status = kEvent::erFAIL;
- $this->Application->SetVar('set_checkout_error', 4);
- $event->redirect = false; // check!!!
- return ;
+ for ($i = 0; $i < 10; $i++) {
+ $is_letter = rand(0, 1);
+ $rand_code .= ($is_letter ? chr(rand(65, 90)) : rand(0, 9));
}
- $expire_date = $object->GetDBField('Expiration');
- $number_of_use = $object->GetDBField('NumberOfUses');
- if( $object->GetDBField('Status') != 1 || ($expire_date && $expire_date < adodb_mktime()) ||
- (isset($number_of_use) && $number_of_use <= 0))
- {
- $event->status = kEvent::erFAIL;
- $this->Application->SetVar('set_checkout_error', 5);
- $event->redirect->false;
- return ;
- }
-
- $last_used = adodb_mktime();
- $object->SetDBField('LastUsedBy', $this->Application->RecallVar('user_id'));
- $object->SetDBField('LastUsedOn_date', $last_used);
- $object->SetDBField('LastUsedOn_time', $last_used);
- if(isset($number_of_use))
- {
- $object->SetDBField('NumberOfUses', $number_of_use - 1);
- if($number_of_use == 1)
- {
- $object->SetDBField('Status', 2);
- }
- }
- $object->Update();
-
- $this->Application->setUnitOption('ord', 'AutoLoad', true);
- $order =& $this->Application->recallObject('ord');
- $order->SetDBField('CouponId', $object->GetDBField('CouponId'));
- $order->Update();
-
- $this->Application->SetVar('set_checkout_error', 10);
+ return $rand_code;
}
/**
* Prepare temp tables for creating new item
* but does not create it. Actual create is
* done in OnPreSaveCreated
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreCreate(&$event)
{
parent::OnPreCreate($event);
$object =& $event->getObject();
/* @var $object kDBItem */
$exp_date = adodb_mktime();
$default_duration = $this->Application->ConfigValue('Comm_DefaultCouponDuration');
if ( $default_duration && $default_duration > 0 ) {
$exp_date += (int)$default_duration * 86400;
}
$object->SetDBField('Expiration_date', $exp_date);
}
/**
* Occurs before updating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemUpdate(&$event)
{
parent::OnBeforeItemUpdate($event);
$this->itemChanged($event);
}
/**
* Occurs before creating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemCreate(&$event)
{
parent::OnBeforeItemCreate($event);
$this->itemChanged($event);
}
/**
* Occurs before item is changed
*
* @param kEvent $event
*/
function itemChanged(&$event)
{
$object =& $event->getObject();
/* @var $object kDBItem */
$object->SetDBField('Amount', abs($object->GetDBField('Amount')));
}
}
\ No newline at end of file
Index: branches/5.2.x/units/orders/orders_event_handler.php
===================================================================
--- branches/5.2.x/units/orders/orders_event_handler.php (revision 14640)
+++ branches/5.2.x/units/orders/orders_event_handler.php (revision 14641)
@@ -1,3474 +1,3673 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class OrdersEventHandler extends kDBEventHandler
{
/**
* Checks user permission to execute given $event
*
* @param kEvent $event
* @return bool
* @access public
*/
public function CheckPermission(&$event)
{
if (!$this->Application->isAdminUser) {
if ($event->Name == 'OnCreate') {
// user can't initiate custom order creation directly
return false;
}
$user_id = $this->Application->RecallVar('user_id');
$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
if ($items_info) {
// when POST is present, then check when is beeing submitted
$order_session_id = $this->Application->RecallVar($event->getPrefixSpecial(true).'_id');
$order_dummy =& $this->Application->recallObject($event->Prefix.'.-item', null, Array('skip_autoload' => true));
foreach ($items_info as $id => $field_values) {
if ($order_session_id != $id) {
// user is trying update not his order, even order from other guest
return false;
}
$order_dummy->Load($id);
// session_id matches order_id from submit
if ($order_dummy->GetDBField('PortalUserId') != $user_id) {
// user performs event on other user order
return false;
}
$status_field = array_shift($this->Application->getUnitOption($event->Prefix, 'StatusField'));
if (isset($field_values[$status_field]) && $order_dummy->GetDBField($status_field) != $field_values[$status_field]) {
// user can't change status by himself
return false;
}
if ($order_dummy->GetDBField($status_field) != ORDER_STATUS_INCOMPLETE) {
// user can't edit orders being processed
return false;
}
if ($event->Name == 'OnUpdate') {
// all checks were ok -> it's user's order -> allow to modify
return true;
}
}
}
}
if ($event->Name == 'OnQuietPreSave') {
$section = $event->getSection();
if ($this->isNewItemCreate($event)) {
return $this->Application->CheckPermission($section.'.add', 1);
}
else {
return $this->Application->CheckPermission($section.'.add', 1) || $this->Application->CheckPermission($section.'.edit', 1);
}
}
return parent::CheckPermission($event);
}
/**
* Allows to override standart permission mapping
*
*/
function mapPermissions()
{
parent::mapPermissions();
$permissions = Array(
// admin
'OnRecalculateItems' => Array('self' => 'add|edit'),
'OnResetToUser' => Array('self' => 'add|edit'),
'OnResetToBilling' => Array('self' => 'add|edit'),
'OnResetToShipping' => Array('self' => 'add|edit'),
'OnMassOrderApprove' => Array('self' => 'advanced:approve'),
'OnMassOrderDeny' => Array('self' => 'advanced:deny'),
'OnMassOrderArchive' => Array('self' => 'advanced:archive'),
'OnMassPlaceOrder' => Array('self' => 'advanced:place'),
'OnMassOrderProcess' => Array('self' => 'advanced:process'),
'OnMassOrderShip' => Array('self' => 'advanced:ship'),
'OnResetToPending' => Array('self' => 'advanced:reset_to_pending'),
'OnLoadSelected' => Array('self' => 'view'), // print in this case
'OnGoToOrder' => Array('self' => 'view'),
// front-end
'OnViewCart' => Array('self' => true),
'OnAddToCart' => Array('self' => true),
'OnRemoveFromCart' => Array('self' => true),
'OnUpdateCart' => Array('self' => true),
+ 'OnUpdateCartJSON' => Array('self' => true),
'OnUpdateItemOptions' => Array('self' => true),
'OnCleanupCart' => Array('self' => true),
'OnContinueShopping' => Array('self' => true),
'OnCheckout' => Array('self' => true),
'OnSelectAddress' => Array('self' => true),
'OnProceedToBilling' => Array('self' => true),
'OnProceedToPreview' => Array('self' => true),
'OnCompleteOrder' => Array('self' => true),
+ 'OnCombinedPlaceOrder' => Array('self' => true),
'OnRemoveCoupon' => Array('self' => true),
'OnRemoveGiftCertificate' => Array('self' => true),
'OnCancelRecurring' => Array('self' => true),
'OnAddVirtualProductToCart' => Array('self' => true),
'OnItemBuild' => Array('self' => true),
'OnDownloadLabel' => Array('self' => true, 'subitem' => true),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
function mapEvents()
{
parent::mapEvents();
$common_events = Array(
'OnResetToUser' => 'OnResetAddress',
'OnResetToBilling' => 'OnResetAddress',
'OnResetToShipping' => 'OnResetAddress',
'OnMassOrderProcess' => 'MassInventoryAction',
'OnMassOrderApprove' => 'MassInventoryAction',
'OnMassOrderDeny' => 'MassInventoryAction',
'OnMassOrderArchive' => 'MassInventoryAction',
'OnMassOrderShip' => 'MassInventoryAction',
'OnOrderProcess' => 'InventoryAction',
'OnOrderApprove' => 'InventoryAction',
'OnOrderDeny' => 'InventoryAction',
'OnOrderArchive' => 'InventoryAction',
'OnOrderShip' => 'InventoryAction',
);
$this->eventMethods = array_merge($this->eventMethods, $common_events);
}
/* ======================== FRONT ONLY ======================== */
function OnQuietPreSave(&$event)
{
$object =& $event->getObject();
+ /* @var $object kDBItem */
+
$object->IgnoreValidation = true;
$event->CallSubEvent('OnPreSave');
$object->IgnoreValidation = false;
}
/**
* Sets new address to order
*
* @param kEvent $event
*/
function OnSelectAddress(&$event)
{
if ($this->Application->isAdminUser) {
return ;
}
$object =& $event->getObject();
+ /* @var $object kDBItem */
$shipping_address_id = $this->Application->GetVar('shipping_address_id');
$billing_address_id = $this->Application->GetVar('billing_address_id');
if ($shipping_address_id || $billing_address_id) {
$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$address =& $this->Application->recallObject('addr.-item','addr', Array('skip_autoload' => true));
$addr_list =& $this->Application->recallObject('addr', 'addr_List', Array('per_page'=>-1, 'skip_counting'=>true) );
$addr_list->Query();
}
if ($shipping_address_id > 0) {
$addr_list->CopyAddress($shipping_address_id, 'Shipping');
$address->Load($shipping_address_id);
$address->MarkAddress('Shipping');
$cs_helper->PopulateStates($event, 'ShippingState', 'ShippingCountry');
$object->setRequired('ShippingState', false);
}
elseif ($shipping_address_id == -1) {
$object->ResetAddress('Shipping');
}
if ($billing_address_id > 0) {
$addr_list->CopyAddress($billing_address_id, 'Billing');
$address->Load($billing_address_id);
$address->MarkAddress('Billing');
$cs_helper->PopulateStates($event, 'BillingState', 'BillingCountry');
$object->setRequired('BillingState', false);
}
elseif ($billing_address_id == -1) {
$object->ResetAddress('Billing');
}
$event->redirect = false;
$object->IgnoreValidation = true;
$this->RecalculateTax($event);
$object->Update();
}
/**
* Updates order with registred user id
*
* @param kEvent $event
*/
function OnUserCreate(&$event)
{
if( !($event->MasterEvent->status == kEvent::erSUCCESS) ) return false;
$ses_id = $this->Application->RecallVar('front_order_id');
if($ses_id)
{
$this->updateUserID($ses_id, $event);
$this->Application->RemoveVar('front_order_id');
}
}
/**
* Enter description here...
*
* @param unknown_type $event
* @return unknown
*/
function OnUserLogin(&$event)
{
if( !($event->MasterEvent->status == kEvent::erSUCCESS) ) {
return false;
}
$ses_id = $this->Application->RecallVar('ord_id');
if ($ses_id) $this->updateUserID($ses_id, $event);
$user_id = $this->Application->RecallVar('user_id');
$affiliate_id = $this->isAffiliate($user_id);
if($affiliate_id) $this->Application->setVisitField('AffiliateId', $affiliate_id);
$event->CallSubEvent('OnRecalculateItems');
}
function updateUserID($order_id, &$event)
{
$table = $this->Application->getUnitOption($event->Prefix,'TableName');
$id_field = $this->Application->getUnitOption($event->Prefix,'IDField');
$user_id = $this->Application->RecallVar('user_id');
$this->Conn->Query('UPDATE '.$table.' SET PortalUserId = '.$user_id.' WHERE '.$id_field.' = '.$order_id);
$affiliate_id = $this->isAffiliate($user_id);
if($affiliate_id)
{
$this->Conn->Query('UPDATE '.$table.' SET AffiliateId = '.$affiliate_id.' WHERE '.$id_field.' = '.$order_id);
}
}
function isAffiliate($user_id)
{
$affiliate_user =& $this->Application->recallObject('affil.-item', null, Array('skip_autoload' => true) );
$affiliate_user->Load($user_id, 'PortalUserId');
return $affiliate_user->isLoaded() ? $affiliate_user->GetDBField('AffiliateId') : 0;
}
/**
* Charge order
*
* @param OrdersItem $order
* @return Array
*/
function ChargeOrder(&$order)
{
$gw_data = $order->getGatewayData();
$this->Application->registerClass( $gw_data['ClassName'], GW_CLASS_PATH.'/'.$gw_data['ClassFile'] );
$gateway_object =& $this->Application->recallObject( $gw_data['ClassName'] );
+ /* @var $gateway_object kGWBase */
$payment_result = $gateway_object->DirectPayment($order->GetFieldValues(), $gw_data['gw_params']);
$sql = 'UPDATE %s SET GWResult1 = %s WHERE %s = %s';
$sql = sprintf($sql, $order->TableName, $this->Conn->qstr($gateway_object->getGWResponce()), $order->IDField, $order->GetID() );
$this->Conn->Query($sql);
$order->SetDBField('GWResult1', $gateway_object->getGWResponce() );
return array('result'=>$payment_result, 'data'=>$gateway_object->parsed_responce, 'gw_data' => $gw_data, 'error_msg'=>$gateway_object->getErrorMsg());
}
function OrderEmailParams(&$order)
{
$billing_email = $order->GetDBField('BillingEmail');
$user_email = $this->Conn->GetOne(' SELECT Email FROM '.$this->Application->getUnitOption('u', 'TableName').'
WHERE PortalUserId = '.$order->GetDBField('PortalUserId'));
$email_params = Array();
$email_params['_user_email'] = $user_email; //for use when shipping vs user is required in InvetnoryAction
$email_params['to_email'] = $billing_email ? $billing_email : $user_email;
$email_params['to_name'] = $order->GetDBField('BillingTo');
return $email_params;
}
function PrepareCoupons(&$event, &$order)
{
$order_items =& $this->Application->recallObject('orditems.-inv','orditems_List',Array('skip_counting'=>true,'per_page'=>-1) );
+ /* @var $order_items kDBList */
+
$order_items->linkToParent($order->Special);
$order_items->Query();
$order_items->GoFirst();
$assigned_coupons = array();
$coup_handler =& $this->Application->recallObject('coup_EventHandler');
foreach($order_items->Records as $product_item)
{
if ($product_item['ItemData']) {
$item_data = unserialize($product_item['ItemData']);
if (isset($item_data['AssignedCoupon']) && $item_data['AssignedCoupon']) {
$coupon_id = $item_data['AssignedCoupon'];
// clone coupon, get new coupon ID
$coupon =& $this->Application->recallObject('coup',null,array('skip_autload' => true));
/* @var $coupon kDBItem */
$coupon->Load($coupon_id);
if (!$coupon->isLoaded()) continue;
$coup_handler->SetNewCode($coupon);
$coupon->NameCopy();
$coupon->SetDBField('Name', $coupon->GetDBField('Name').' (Order #'.$order->GetField('OrderNumber').')');
$coupon->Create();
// add coupon code to array
array_push($assigned_coupons, $coupon->GetDBField('Code'));
}
}
}
/* @var $order OrdersItem */
if ($assigned_coupons) {
$comments = $order->GetDBField('AdminComment');
if ($comments) $comments .= "\r\n";
$comments .= "Issued coupon(s): ". join(',', $assigned_coupons);
$order->SetDBField('AdminComment', $comments);
$order->Update();
}
if ($assigned_coupons) $this->Application->SetVar('order_coupons', join(',', $assigned_coupons));
}
/**
* Completes order if possible
*
* @param kEvent $event
* @return bool
*/
function OnCompleteOrder(&$event)
{
$this->LockTables($event);
if (!$this->CheckQuantites($event)) return;
$this->ReserveItems($event);
$order =& $event->getObject();
$charge_result = $this->ChargeOrder($order);
if (!$charge_result['result']) {
$this->FreeItems($event);
$this->Application->StoreVar('gw_error', $charge_result['error_msg']);
//$this->Application->StoreVar('gw_error', getArrayValue($charge_result, 'data', 'responce_reason_text') );
$event->redirect = $this->Application->GetVar('failure_template');
$event->SetRedirectParam('m_cat_id', 0);
if ($event->Special == 'recurring') { // if we set failed status for other than recurring special the redirect will not occur
$event->status = kEvent::erFAIL;
}
return false;
}
// call CompleteOrder events for items in order BEFORE SplitOrder (because ApproveEvents are called there)
$order_items =& $this->Application->recallObject('orditems.-inv','orditems_List',Array('skip_counting'=>true,'per_page'=>-1) );
$order_items->linkToParent($order->Special);
$order_items->Query(true);
$order_items->GoFirst();
foreach($order_items->Records as $product_item)
{
if (!$product_item['ProductId']) continue; // product may have been deleted
$this->raiseProductEvent('CompleteOrder', $product_item['ProductId'], $product_item);
}
$shipping_control = getArrayValue($charge_result, 'gw_data', 'gw_params', 'shipping_control');
if ($event->Special != 'recurring') {
if ($shipping_control && $shipping_control != SHIPPING_CONTROL_PREAUTH ) {
// we have to do it here, because the coupons are used in the e-mails
$this->PrepareCoupons($event, $order);
}
$email_event_user =& $this->Application->EmailEventUser('ORDER.SUBMIT', $order->GetDBField('PortalUserId'), $this->OrderEmailParams($order));
$email_event_admin =& $this->Application->EmailEventAdmin('ORDER.SUBMIT');
}
if ($shipping_control === false || $shipping_control == SHIPPING_CONTROL_PREAUTH ) {
$order->SetDBField('Status', ORDER_STATUS_PENDING);
$order->Update();
}
else {
$this->SplitOrder($event, $order);
}
if (!$this->Application->isAdminUser) {
// for tracking code
$this->Application->StoreVar('last_order_amount', $order->GetDBField('TotalAmount'));
$this->Application->StoreVar('last_order_number', $order->GetDBField('OrderNumber'));
$this->Application->StoreVar('last_order_customer', $order->GetDBField('BillingTo'));
$this->Application->StoreVar('last_order_user', $order->GetDBField('Username'));
$event->redirect = $this->Application->GetVar('success_template');
$event->SetRedirectParam('m_cat_id', 0);
}
else
{
// $event->CallSubEvent('OnSave');
}
$order_id = $order->GetId();
$order_idfield = $this->Application->getUnitOption('ord','IDField');
$order_table = $this->Application->getUnitOption('ord','TableName');
$original_amount = $order->GetDBField('SubTotal') + $order->GetDBField('ShippingCost') + $order->GetDBField('VAT') + $order->GetDBField('ProcessingFee') + $order->GetDBField('InsuranceFee') - $order->GetDBField('GiftCertificateDiscount');
$sql = 'UPDATE '.$order_table.'
SET OriginalAmount = '.$original_amount.'
WHERE '.$order_idfield.' = '.$order_id;
$this->Conn->Query($sql);
$this->Application->StoreVar('front_order_id', $order_id);
$this->Application->RemoveVar('ord_id');
}
/**
* Set billing address same as shipping
*
* @param kEvent $event
*/
function setBillingAddress(&$event)
{
$object =& $event->getObject();
if ($object->HasTangibleItems()) {
if ($this->Application->GetVar('same_address')) {
// copy shipping address to billing
$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
list($id, $field_values) = each($items_info);
$address_fields = Array('To', 'Company', 'Phone', 'Fax', 'Email', 'Address1', 'Address2', 'City', 'State', 'Zip', 'Country');
foreach ($address_fields as $address_field) {
$items_info[$id]['Billing'.$address_field] = $object->GetDBField('Shipping'.$address_field);
}
$this->Application->SetVar($event->getPrefixSpecial(true), $items_info);
}
}
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnProceedToPreview(&$event)
{
$this->setBillingAddress($event);
$event->CallSubEvent('OnUpdate');
$event->redirect = $this->Application->GetVar('preview_template');
}
function OnViewCart(&$event)
{
$this->StoreContinueShoppingLink();
$event->redirect = $this->Application->GetVar('viewcart_template');
}
function OnContinueShopping(&$event)
{
$env = $this->Application->GetVar('continue_shopping_template');
if (!$env || $env == '__default__') {
$env = $this->Application->RecallVar('continue_shopping');
}
if (!$env) {
$env = 'in-commerce/index';
}
$event->redirect = $env;
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnCheckout(&$event)
{
$this->OnUpdateCart($event);
- if ($event->getEventParam('RecalculateChangedCart'))
- {
- $event->SetRedirectParam('checkout_error', $event->getRedirectParam('checkout_error'));
- }
- else
- {
+ if ( !$event->getEventParam('RecalculateChangedCart') ) {
$object =& $event->getObject();
- if(!$object->HasTangibleItems())
- {
+ /* @var $object OrdersItem */
+
+ if ( !$object->HasTangibleItems() ) {
$object->SetDBField('ShippingTo', '');
$object->SetDBField('ShippingCompany', '');
$object->SetDBField('ShippingPhone', '');
$object->SetDBField('ShippingFax', '');
$object->SetDBField('ShippingEmail', '');
$object->SetDBField('ShippingAddress1', '');
$object->SetDBField('ShippingAddress2', '');
$object->SetDBField('ShippingCity', '');
$object->SetDBField('ShippingState', '');
$object->SetDBField('ShippingZip', '');
$object->SetDBField('ShippingCountry', '');
$object->SetDBField('ShippingType', 0);
$object->SetDBField('ShippingCost', 0);
$object->SetDBField('ShippingCustomerAccount', '');
$object->SetDBField('ShippingTracking', '');
$object->SetDBField('ShippingDate', 0);
$object->SetDBField('ShippingOption', 0);
$object->SetDBField('ShippingInfo', '');
$object->Update();
}
$event->redirect = $this->Application->GetVar('next_step_template');
$order_id = $this->Application->GetVar('order_id');
- if($order_id !== false) $event->SetRedirectParam('ord_id', $order_id);
+
+ if ( $order_id !== false ) {
+ $event->SetRedirectParam('ord_id', $order_id);
+ }
}
}
/**
* Redirect user to Billing checkout step
*
* @param kEvent $event
*/
function OnProceedToBilling(&$event)
{
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
if ($items_info) {
list($id, $field_values) = each($items_info);
$object =& $event->getObject();
$payment_type_id = $object->GetDBField('PaymentType');
if (!$payment_type_id) {
$default_type = $this->_getDefaultPaymentType();
if ($default_type) {
$field_values['PaymentType'] = $default_type;
$items_info[$id] = $field_values;
$this->Application->SetVar( $event->getPrefixSpecial(true), $items_info );
}
}
}
$event->CallSubEvent('OnUpdate');
$event->redirect = $this->Application->GetVar('next_step_template');
}
function OnCancelRecurring(&$event)
{
$order =& $event->GetObject();
$order->SetDBField('IsRecurringBilling', 0);
$order->Update();
if ($this->Application->GetVar('cancelrecurring_ok_template'))
{
$event->redirect = $this->Application->GetVar('cancelrecurring_ok_template');
}
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnAfterItemUpdate(&$event)
{
+ parent::OnAfterItemUpdate($event);
+
$object =& $event->getObject();
+ /* @var $object OrdersItem */
$cvv2 = $object->GetDBField('PaymentCVV2');
- if($cvv2 !== false) $this->Application->StoreVar('CVV2Code', $cvv2);
+
+ if ( $cvv2 !== false ) {
+ $this->Application->StoreVar('CVV2Code', $cvv2);
+ }
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnUpdate(&$event)
{
$this->setBillingAddress($event);
parent::OnUpdate($event);
if ($this->Application->isAdminUser) {
return true;
}
else {
$event->setRedirectParams(Array('opener' => 's'), true);
}
if ($event->status == kEvent::erSUCCESS) {
$this->createMissingAddresses($event);
}
else {
// strange: recalculate total amount on error
$object =& $event->getObject();
/* @var $object kDBItem */
$object->SetDBField('TotalAmount', $object->getTotalAmount());
}
}
/**
* Creates new address
*
* @param kEvent $event
*/
function createMissingAddresses(&$event)
{
if (!$this->Application->LoggedIn()) {
return false;
}
$object =& $event->getObject();
$addr_list =& $this->Application->recallObject('addr', 'addr_List', Array('per_page'=>-1, 'skip_counting'=>true) );
$addr_list->Query();
$address_dummy =& $this->Application->recallObject('addr.-item', null, Array('skip_autoload' => true));
$address_prefixes = Array('Billing', 'Shipping');
$address_fields = Array('To','Company','Phone','Fax','Email','Address1','Address2','City','State','Zip','Country');
foreach ($address_prefixes as $address_prefix) {
$address_id = $this->Application->GetVar(strtolower($address_prefix).'_address_id');
if (!$this->Application->GetVar('check_'.strtolower($address_prefix).'_address')) {
// form type doesn't match check type, e.g. shipping check on billing form
continue;
}
if ($address_id > 0) {
$address_dummy->Load($address_id);
}
else {
$address_dummy->SetDBField('PortalUserId', $this->Application->RecallVar('user_id') );
}
foreach ($address_fields as $address_field) {
$address_dummy->SetDBField($address_field, $object->GetDBField($address_prefix.$address_field));
}
$address_dummy->MarkAddress($address_prefix, false);
$ret = ($address_id > 0) ? $address_dummy->Update() : $address_dummy->Create();
}
}
function OnUpdateCart(&$event)
{
$this->Application->HandleEvent($items_event, 'orditems:OnUpdate');
return $event->CallSubEvent('OnRecalculateItems');
}
/**
+ * Updates cart and returns various info in JSON format
+ *
+ * @param kEvent $event
+ */
+ function OnUpdateCartJSON(&$event)
+ {
+ if ( $this->Application->GetVar('ajax') != 'yes' ) {
+ return;
+ }
+
+ $object =& $event->getObject();
+ /* @var $object kDBItem */
+
+ // 1. delete given order item by id
+ $delete_id = $this->Application->GetVar('delete_id');
+
+ if ( $delete_id !== false ) {
+ $sql = 'DELETE FROM ' . TABLE_PREFIX . 'OrderItems
+ WHERE OrderId = ' . $object->GetID() . ' AND OrderItemId = ' . (int)$delete_id;
+ $this->Conn->Query($sql);
+ }
+
+ // 2. remove coupon
+ $remove = $this->Application->GetVar('remove');
+
+ if ( $remove == 'coupon' ) {
+ $this->RemoveCoupon($object);
+ $object->setCheckoutError(OrderCheckoutErrorType::COUPON, OrderCheckoutError::COUPON_REMOVED);
+ }
+ elseif ( $remove == 'gift_certificate' ) {
+ $this->RemoveGiftCertificate($object);
+ $object->setCheckoutError(OrderCheckoutErrorType::GIFT_CERTIFICATE, OrderCheckoutError::GC_REMOVED);
+ }
+
+ // 3. update product quantities and recalculate all discounts
+ $this->Application->HandleEvent($items_event, 'orditems:OnUpdate');
+ $event->CallSubEvent('OnRecalculateItems');
+
+ // 4. remove "orditems" object of kDBItem class, since getOrderInfo uses kDBList object under same prefix
+ $this->Application->removeObject('orditems');
+
+ $order_helper =& $this->Application->recallObject('OrderHelper');
+ /* @var $order_helper OrderHelper */
+
+ $event->status = kEvent::erSTOP;
+ $currency = $this->Application->GetVar('currency', 'selected');
+
+ echo json_encode( $order_helper->getOrderInfo($object, $currency) );
+ }
+
+ /**
* Adds item to cart
*
* @param kEvent $event
*/
function OnAddToCart(&$event)
{
$this->StoreContinueShoppingLink();
$qty = $this->Application->GetVar('qty');
$options = $this->Application->GetVar('options');
// multiple or options add
$items = Array();
if (is_array($qty)) {
foreach ($qty as $item_id => $combinations)
{
if (is_array($combinations)) {
foreach ($combinations as $comb_id => $comb_qty) {
if ($comb_qty == 0) continue;
$items[] = array('item_id' => $item_id, 'qty' => $comb_qty, 'comb' => $comb_id);
}
}
else {
$items[] = array('item_id' => $item_id, 'qty' => $combinations);
}
}
}
if (!$items) {
if (!$qty || is_array($qty)) $qty = 1;
$item_id = $this->Application->GetVar('p_id');
if (!$item_id) return ;
$items = array(array('item_id' => $item_id, 'qty' => $qty));
}
// remember item data passed to event when called
$default_item_data = $event->getEventParam('ItemData');
$default_item_data = $default_item_data ? unserialize($default_item_data) : Array();
foreach ($items as $an_item) {
$item_id = $an_item['item_id'];
$qty = $an_item['qty'];
$comb = getArrayValue($an_item, 'comb');
$item_data = $default_item_data;
$product =& $this->Application->recallObject('p', null, Array('skip_autoload' => true));
$product->Load($item_id);
$event->setEventParam('ItemData', null);
if ($product->GetDBField('AssignedCoupon')) {
$item_data['AssignedCoupon'] = $product->GetDBField('AssignedCoupon');
}
// 1. store options information OR
if ($comb) {
$combination = $this->Conn->GetOne('SELECT Combination FROM '.TABLE_PREFIX.'ProductOptionCombinations WHERE CombinationId = '.$comb);
$item_data['Options'] = unserialize($combination);
}
elseif (is_array($options)) {
$item_data['Options'] = $options[$item_id];
}
// 2. store subscription information OR
if( $product->GetDBField('Type') == 2 ) // subscriptions
{
$item_data = $this->BuildSubscriptionItemData($item_id, $item_data);
}
// 3. store package information
if( $product->GetDBField('Type') == 5 ) // package
{
$package_content_ids = $product->GetPackageContentIds();
$product_package_item =& $this->Application->recallObject('p.-packageitem');
$package_item_data = array();
foreach ($package_content_ids as $package_item_id){
$product_package_item->Load($package_item_id);
$package_item_data[$package_item_id] = array();
if( $product_package_item->GetDBField('Type') == 2 ) // subscriptions
{
$package_item_data[$package_item_id] = $this->BuildSubscriptionItemData($package_item_id, $item_data);
}
}
$item_data['PackageContent'] = $product->GetPackageContentIds();
$item_data['PackageItemsItemData'] = $package_item_data;
}
$event->setEventParam('ItemData', serialize($item_data));
// 1 for PacakgeNum when in admin - temporary solution to overcome splitting into separate sub-orders
// of orders with items added through admin when approving them
$this->AddItemToOrder($event, $item_id, $qty, $this->Application->isAdminUser ? 1 : null);
}
if ($event->status == kEvent::erSUCCESS && !$event->redirect) {
$event->SetRedirectParam('pass', 'm');
$event->SetRedirectParam('pass_category', 0); //otherwise mod-rewrite shop-cart URL will include category
$event->redirect = true;
}
else {
if ($this->Application->isAdminUser) {
$event->SetRedirectParam('opener', 'u');
}
}
}
/**
* Check if required options are selected & selected option combination is in stock
*
* @param kEvent $event
* @param Array $options
* @param int $product_id
* @param int $qty
* @param int $selection_mode
* @return bool
*/
function CheckOptions(&$event, &$options, $product_id, $qty, $selection_mode)
{
// 1. check for required options
$selection_filter = $selection_mode == 1 ? ' AND OptionType IN (1,3,6) ' : '';
$req_options = $this->Conn->GetCol('SELECT ProductOptionId FROM '.TABLE_PREFIX.'ProductOptions WHERE ProductId = '.$product_id.' AND Required = 1 '.$selection_filter);
$result = true;
foreach ($req_options as $opt_id) {
if (!getArrayValue($options, $opt_id)) {
$this->Application->SetVar('opt_error', 1); //let the template know we have an error
$result = false;
}
}
// 2. check for option combinations in stock
$comb_salt = $this->OptionsSalt($options, true);
if ($comb_salt) {
// such option combination is defined explicitly
$poc_table = $this->Application->getUnitOption('poc', 'TableName');
$sql = 'SELECT Availability
FROM '.$poc_table.'
WHERE CombinationCRC = '.$comb_salt;
$comb_availble = $this->Conn->GetOne($sql);
// 2.1. check if Availability flag is set, then
if ($comb_availble == 1) {
// 2.2. check for quantity in stock
$table = Array();
$table['poc'] = $this->Application->getUnitOption('poc', 'TableName');
$table['p'] = $this->Application->getUnitOption('p', 'TableName');
$table['oi'] = $this->TablePrefix($event).'OrderItems';
$object =& $event->getObject();
$ord_id = $object->GetID();
// 2.3. check if some amount of same combination & product are not already in shopping cart
$sql = 'SELECT '.
$table['p'].'.InventoryStatus,'.
$table['p'].'.BackOrder,
IF('.$table['p'].'.InventoryStatus = 2, '.$table['poc'].'.QtyInStock, '.$table['p'].'.QtyInStock) AS QtyInStock,
IF('.$table['oi'].'.OrderItemId IS NULL, 0, '.$table['oi'].'.Quantity) AS Quantity
FROM '.$table['p'].'
LEFT JOIN '.$table['poc'].' ON
'.$table['p'].'.ProductId = '.$table['poc'].'.ProductId
LEFT JOIN '.$table['oi'].' ON
('.$table['oi'].'.OrderId = '.$ord_id.') AND
('.$table['oi'].'.OptionsSalt = '.$comb_salt.') AND
('.$table['oi'].'.ProductId = '.$product_id.') AND
('.$table['oi'].'.BackOrderFlag = 0)
WHERE '.$table['poc'].'.CombinationCRC = '.$comb_salt;
$product_info = $this->Conn->GetRow($sql);
if ($product_info['InventoryStatus']) {
$backordering = $this->Application->ConfigValue('Comm_Enable_Backordering');
if (!$backordering || $product_info['BackOrder'] == 0) {
// backordering is not enabled generally or for this product directly, then check quantities in stock
if ($qty + $product_info['Quantity'] > $product_info['QtyInStock']) {
$this->Application->SetVar('opt_error', 2);
$result = false;
}
}
}
}
elseif ($comb_availble !== false) {
$this->Application->SetVar('opt_error', 2);
$result = false;
}
}
if ($result) {
$event->status = kEvent::erSUCCESS;
$event->redirect = $this->Application->isAdminUser ? true : $this->Application->GetVar('shop_cart_template');
}
else {
$event->status = kEvent::erFAIL;
}
return $result;
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnUpdateItemOptions(&$event)
{
$opt_data = $this->Application->GetVar('options');
$options = getArrayValue($opt_data, $this->Application->GetVar('p_id'));
if (!$options) {
$qty_data = $this->Application->GetVar('qty');
$comb_id = key(getArrayValue($qty_data, $this->Application->GetVar('p_id')));
$options = unserialize($this->Conn->GetOne('SELECT Combination FROM '.TABLE_PREFIX.'ProductOptionCombinations WHERE CombinationId = '.$comb_id));
}
if (!$options) return;
$ord_item =& $this->Application->recallObject('orditems.-opt', null, Array ('skip_autoload' => true));
/* @var $ord_item kDBItem */
$ord_item->Load($this->Application->GetVar('orditems_id'));
// assuming that quantity cannot be changed during order item editing
if (!$this->CheckOptions($event, $options, $ord_item->GetDBField('ProductId'), 0, $ord_item->GetDBField('OptionsSelectionMode'))) return;
$item_data = unserialize($ord_item->GetDBField('ItemData'));
$item_data['Options'] = $options;
$ord_item->SetDBField('ItemData', serialize($item_data));
$ord_item->SetDBField('OptionsSalt', $this->OptionsSalt($options));
$ord_item->Update();
$event->CallSubEvent('OnRecalculateItems');
if ($event->status == kEvent::erSUCCESS && $this->Application->isAdminUser) {
$event->SetRedirectParam('opener', 'u');
}
}
function BuildSubscriptionItemData($item_id, $item_data)
{
$products_table = $this->Application->getUnitOption('p', 'TableName');
$products_idfield = $this->Application->getUnitOption('p', 'IDField');
$sql = 'SELECT AccessGroupId FROM %s WHERE %s = %s';
$item_data['PortalGroupId'] = $this->Conn->GetOne( sprintf($sql, $products_table, $products_idfield, $item_id) );
$pricing_table = $this->Application->getUnitOption('pr', 'TableName');
$pricing_idfield = $this->Application->getUnitOption('pr', 'IDField');
/* TODO check on implementation
$sql = 'SELECT AccessDuration, AccessUnit, DurationType, AccessExpiration FROM %s WHERE %s = %s';
*/
$sql = 'SELECT * FROM %s WHERE %s = %s';
$pricing_id = $this->GetPricingId($item_id, $item_data);
$item_data['PricingId'] = $pricing_id;
$pricing_info = $this->Conn->GetRow( sprintf($sql, $pricing_table, $pricing_idfield, $pricing_id ) );
$unit_secs = Array(1 => 1, 2 => 60, 3 => 3600, 4 => 86400, 5 => 604800, 6 => 2592000, 7 => 31536000);
/* TODO check on implementation (code from customization healtheconomics.org)
$item_data['DurationType'] = $pricing_info['DurationType'];
$item_data['AccessExpiration'] = $pricing_info['AccessExpiration'];
*/
$item_data['Duration'] = $pricing_info['AccessDuration'] * $unit_secs[ $pricing_info['AccessUnit'] ];
return $item_data;
}
+ /**
+ * Enter description here...
+ *
+ * @param kEvent $event
+ */
+ function OnApplyCoupon(&$event)
+ {
+ $code = $this->Application->GetVar('coupon_code');
+
+ if ($code == '') {
+ return ;
+ }
+
+ $object =& $event->getObject();
+ /* @var $object OrdersItem */
+
+ $coupon =& $this->Application->recallObject('coup', null, Array ('skip_autoload' => true));
+ /* @var $coupon kDBItem */
+
+ $coupon->Load($code, 'Code');
+
+ if ( !$coupon->isLoaded() ) {
+ $event->status = kEvent::erFAIL;
+ $object->setCheckoutError(OrderCheckoutErrorType::COUPON, OrderCheckoutError::COUPON_CODE_INVALID);
+ $event->redirect = false; // check!!!
+
+ return ;
+ }
+
+ $expire_date = $coupon->GetDBField('Expiration');
+ $number_of_use = $coupon->GetDBField('NumberOfUses');
+ if ( $coupon->GetDBField('Status') != 1 || ($expire_date && $expire_date < adodb_mktime()) ||
+ (isset($number_of_use) && $number_of_use <= 0))
+ {
+ $event->status = kEvent::erFAIL;
+ $object->setCheckoutError(OrderCheckoutErrorType::COUPON, OrderCheckoutError::COUPON_CODE_EXPIRED);
+ $event->redirect = false;
+
+ return ;
+ }
+
+ $last_used = adodb_mktime();
+ $coupon->SetDBField('LastUsedBy', $this->Application->RecallVar('user_id'));
+ $coupon->SetDBField('LastUsedOn_date', $last_used);
+ $coupon->SetDBField('LastUsedOn_time', $last_used);
+
+
+ if ( isset($number_of_use) ) {
+ $coupon->SetDBField('NumberOfUses', $number_of_use - 1);
+
+ if ($number_of_use == 1) {
+ $coupon->SetDBField('Status', 2);
+ }
+ }
+
+ $coupon->Update();
+
+ $this->Application->setUnitOption('ord', 'AutoLoad', true);
+ $order =& $this->Application->recallObject('ord');
+ /* @var $order OrdersItem */
+
+ $order->SetDBField('CouponId', $coupon->GetDBField('CouponId'));
+ $order->SetDBField('CouponName', $coupon->GetDBField('Name')); // calculated field
+
+ $order->Update();
+
+ $object->setCheckoutError(OrderCheckoutErrorType::COUPON, OrderCheckoutError::COUPON_APPLIED);
+// OnApplyCoupon is called as hook for OnUpdateCart/OnCheckout, which calls OnRecalcualate themself
+ }
+
+ /**
+ * Removes coupon from order
+ *
+ * @param kEvent $event
+ * @deprecated
+ */
function OnRemoveCoupon(&$event)
{
$object =& $event->getObject();
+ /* @var $object OrdersItem */
+
$this->RemoveCoupon($object);
+ $object->setCheckoutError(OrderCheckoutErrorType::COUPON, OrderCheckoutError::COUPON_REMOVED);
+
$event->CallSubEvent('OnRecalculateItems');
- $event->SetRedirectParam('checkout_error', 7);
}
+ /**
+ * Removes coupon from a given order
+ *
+ * @param OrdersItem $object
+ */
function RemoveCoupon(&$object)
{
- $coupon_id = $object->GetDBField('CouponId');
$coupon =& $this->Application->recallObject('coup', null, Array('skip_autoload' => true));
- $res = $coupon->Load($coupon_id);
- $uses = $coupon->GetDBField('NumberOfUses');
+ /* @var $coupon kDBItem */
- if($res && isset($uses))
- {
- $coupon->SetDBField('NumberOfUses', $uses + 1);
- $coupon->SetDBField('Status', 1);
+ $coupon->Load( $object->GetDBField('CouponId') );
+
+ if ( $coupon->isLoaded() ) {
+ $coupon->SetDBField('NumberOfUses', $coupon->GetDBField('NumberOfUses') + 1);
+ $coupon->SetDBField('Status', STATUS_ACTIVE);
$coupon->Update();
}
+
$object->SetDBField('CouponId', 0);
+ $object->SetDBField('CouponName', ''); // calculated field
$object->SetDBField('CouponDiscount', 0);
-
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnAddVirtualProductToCart(&$event)
{
$l_info = $this->Application->GetVar('l');
if($l_info)
{
foreach($l_info as $link_id => $link_info) {}
$item_data['LinkId'] = $link_id;
$item_data['ListingTypeId'] = $link_info['ListingTypeId'];
}
else
{
$link_id = $this->Application->GetVar('l_id');
$sql = 'SELECT ResourceId FROM '.$this->Application->getUnitOption('l', 'TableName').'
WHERE LinkId = '.$link_id;
$sql = 'SELECT ListingTypeId FROM '.$this->Application->getUnitOption('ls', 'TableName').'
WHERE ItemResourceId = '.$this->Conn->GetOne($sql);
$item_data['LinkId'] = $link_id;
$item_data['ListingTypeId'] = $this->Conn->GetOne($sql);
}
$sql = 'SELECT VirtualProductId FROM '.$this->Application->getUnitOption('lst', 'TableName').'
WHERE ListingTypeId = '.$item_data['ListingTypeId'];
$item_id = $this->Conn->GetOne($sql);
$event->setEventParam('ItemData', serialize($item_data));
$this->AddItemToOrder($event, $item_id);
$event->redirect = $this->Application->GetVar('shop_cart_template');
// don't pass unused info to shopping cart, brokes old mod-rewrites
$event->SetRedirectParam('pass', 'm'); // not to pass link id
$event->SetRedirectParam('m_cat_id', 0); // not to pass link id
}
function OnRemoveFromCart(&$event)
{
$ord_item_id = $this->Application->GetVar('orditems_id');
$ord_id = $this->getPassedId($event);
$this->Conn->Query('DELETE FROM '.TABLE_PREFIX.'OrderItems WHERE OrderId = '.$ord_id.' AND OrderItemId = '.$ord_item_id);
$this->OnRecalculateItems($event);
}
function OnCleanupCart(&$event)
{
$object =& $event->getObject();
$sql = 'DELETE FROM '.TABLE_PREFIX.'OrderItems
WHERE OrderId = '.$this->getPassedID($event);
$this->Conn->Query($sql);
$this->RemoveCoupon($object);
$this->RemoveGiftCertificate($object);
$this->OnRecalculateItems($event);
}
/**
* Returns order id from session or last used
*
* @param kEvent $event
* @return int
*/
function getPassedId(&$event)
{
$event->setEventParam('raise_warnings', 0);
$passed = parent::getPassedID($event);
if ($this->Application->isAdminUser) {
// work as usual in admin
return $passed;
}
if ($event->Special == 'last') {
// return last order id (for using on thank you page)
$order_id = $this->Application->RecallVar('front_order_id');
return $order_id > 0 ? $order_id : FAKE_ORDER_ID; // FAKE_ORDER_ID helps to keep parent filter for order items set in "kDBList::linkToParent"
}
$ses_id = $this->Application->RecallVar( $event->getPrefixSpecial(true) . '_id' );
if ($passed && ($passed != $ses_id)) {
// order id given in url doesn't match our current order id
$sql = 'SELECT PortalUserId
FROM ' . TABLE_PREFIX . 'Orders
WHERE OrderId = ' . $passed;
$user_id = $this->Conn->GetOne($sql);
if ($user_id == $this->Application->RecallVar('user_id')) {
// current user is owner of order with given id -> allow him to view order details
return $passed;
}
else {
// current user is not owner of given order -> hacking attempt
$this->Application->SetVar($event->getPrefixSpecial().'_id', 0);
return 0;
}
}
else {
// not passed or equals to ses_id
return $ses_id > 0 ? $ses_id : FAKE_ORDER_ID; // FAKE_ORDER_ID helps to keep parent filter for order items set in "kDBList::linkToParent"
}
return $passed;
}
/**
* Load item if id is available
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function LoadItem(&$event)
{
$id = $this->getPassedID($event);
if ( $id == FAKE_ORDER_ID ) {
// if we already know, that there is no such order,
// then don't run database query, that will confirm that
$object =& $event->getObject();
/* @var $object kDBItem */
$object->Clear($id);
return ;
}
parent::LoadItem($event);
}
/**
* Creates new shopping cart
*
* @param kEvent $event
*/
function _createNewCart(&$event)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
/* @var $object kDBItem */
$this->setNextOrderNumber($event);
$object->SetDBField('Status', ORDER_STATUS_INCOMPLETE);
$object->SetDBField('VisitId', $this->Application->RecallVar('visit_id') );
// get user
$user_id = $this->Application->RecallVar('user_id');
if (!$user_id) {
$user_id = USER_GUEST;
}
$object->SetDBField('PortalUserId', $user_id);
// get affiliate
$affiliate_id = $this->isAffiliate($user_id);
if ($affiliate_id) {
$object->SetDBField('AffiliateId', $affiliate_id);
}
else {
$affiliate_storage_method = $this->Application->ConfigValue('Comm_AffiliateStorageMethod');
$object->SetDBField('AffiliateId', $affiliate_storage_method == 1 ? (int)$this->Application->RecallVar('affiliate_id') : (int)$this->Application->GetVar('affiliate_id') );
}
// get payment type
$default_type = $this->_getDefaultPaymentType();
if ($default_type) {
$object->SetDBField('PaymentType', $default_type);
}
$created = $object->Create();
if ($created) {
$id = $object->GetID();
$this->Application->SetVar($event->getPrefixSpecial(true) . '_id', $id);
$this->Application->StoreVar($event->getPrefixSpecial(true) . '_id', $id);
return $id;
}
return 0;
}
/**
* Returns default payment type for order
*
* @return int
*/
function _getDefaultPaymentType()
{
$default_type = $this->Application->siteDomainField('PrimaryPaymentTypeId');
if (!$default_type) {
$sql = 'SELECT PaymentTypeId
FROM ' . TABLE_PREFIX . 'PaymentTypes
WHERE IsPrimary = 1';
$default_type = $this->Conn->GetOne($sql);
}
return $default_type;
}
function StoreContinueShoppingLink()
{
$this->Application->StoreVar('continue_shopping', 'external:'.PROTOCOL.SERVER_NAME.$this->Application->RecallVar('last_url'));
}
/**
* Sets required fields for order, based on current checkout step
* !!! Do not use switch here, since all cases may be on the same form simultaniously
*
* @param kEvent $event
*/
function SetStepRequiredFields(&$event)
{
$order =& $event->getObject();
/* @var $order OrdersItem */
$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
if ($items_info) {
// updated address available from SUBMIT -> use it
list($id, $field_values) = each($items_info);
}
else {
// no updated address -> use current address
$field_values = Array (
'ShippingCountry' => $order->GetDBField('ShippingCountry'),
'BillingCountry' => $order->GetDBField('BillingCountry'),
'PaymentType' => $order->GetDBField('PaymentType'),
);
}
// shipping address required fields
if ($this->Application->GetVar('check_shipping_address')) {
$has_tangibles = $order->HasTangibleItems();
$req_fields = array('ShippingTo', 'ShippingAddress1', 'ShippingCity', 'ShippingZip', 'ShippingCountry', 'ShippingPhone');
$order->setRequired($req_fields, $has_tangibles);
$order->setRequired('ShippingState', $cs_helper->CountryHasStates( $field_values['ShippingCountry'] ));
}
// billing address required fields
if ($this->Application->GetVar('check_billing_address')) {
$req_fields = array('BillingTo', 'BillingAddress1', 'BillingCity', 'BillingZip', 'BillingCountry', 'BillingPhone');
$order->setRequired($req_fields);
$order->setRequired('BillingState', $cs_helper->CountryHasStates( $field_values['BillingCountry'] ));
}
$check_cc = $this->Application->GetVar('check_credit_card');
$ord_event = $this->Application->GetVar($event->getPrefixSpecial().'_event');
if (($ord_event !== 'OnProceedToPreview') && !$this->Application->isAdmin) {
// don't check credit card when going from "billing info" to "order preview" step
$check_cc = 0;
}
if ($check_cc && ($field_values['PaymentType'] == $order->GetDBField('PaymentType'))) {
// cc check required AND payment type was not changed during SUBMIT
if ($this->Application->isAdminUser) {
$req_fields = array('PaymentCardType', 'PaymentAccount', 'PaymentNameOnCard', 'PaymentCCExpDate');
}
else {
$req_fields = array('PaymentCardType', 'PaymentAccount', 'PaymentNameOnCard', 'PaymentCCExpDate', 'PaymentCVV2');
}
$order->setRequired($req_fields);
}
}
/**
* Set's order's user_id to user from session or Guest otherwise
*
* @param kEvent $event
*/
function CheckUser(&$event)
{
if ($this->Application->isAdminUser || defined('GW_NOTIFY')) {
// don't check for user in order while processing payment
// gateways, because they can do cross-domain ssl redirects
return;
}
$order =& $event->GetObject();
$ses_user = $this->Application->RecallVar('user_id');
if ($order->GetDBField('PortalUserId') != $ses_user) {
if ($ses_user == 0) {
$ses_user = USER_GUEST;
}
$order->SetDBField('PortalUserId', $ses_user);
// since CheckUser is called in OnBeforeItemUpdate, we don't need to call udpate here, just set the field
}
}
/* ======================== ADMIN ONLY ======================== */
/**
* Prepare temp tables for creating new item
* but does not create it. Actual create is
* done in OnPreSaveCreated
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreCreate(&$event)
{
parent::OnPreCreate($event);
$object =& $event->getObject();
/* @var $object kDBItem */
$this->setNextOrderNumber($event);
$object->SetDBField('OrderIP', $_SERVER['REMOTE_ADDR']);
$order_type = $this->getTypeBySpecial( $this->Application->GetVar('order_type') );
$object->SetDBField('Status', $order_type);
}
/**
* When cloning orders set new order number to them
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeClone(&$event)
{
parent::OnBeforeClone($event);
$object =& $event->getObject();
if (substr($event->Special, 0, 9) == 'recurring') {
$object->SetDBField('SubNumber', $object->getNextSubNumber());
$object->SetDBField('OriginalAmount', 0); // needed in this case ?
}
else {
$this->setNextOrderNumber($event);
$object->SetDBField('OriginalAmount', 0);
}
$object->SetDBField('OrderDate', adodb_mktime());
$object->UpdateFormattersSubFields();
$object->SetDBField('GWResult1', '');
$object->SetDBField('GWResult2', '');
}
function OnReserveItems(&$event)
{
$order_items =& $this->Application->recallObject('orditems.-inv','orditems_List',Array('skip_counting'=>true,'per_page'=>-1) );
$order_items->linkToParent('-inv');
// force re-query, since we are updateing through orditem ITEM, not the list, and
// OnReserveItems may be called 2 times when fullfilling backorders through product edit - first time
// from FullFillBackorders and second time from OnOrderProcess
$order_items->Query(true);
$order_items->GoFirst();
// query all combinations used in this order
$product_object =& $this->Application->recallObject('p', null, Array('skip_autoload' => true));
$product_object->SwitchToLive();
$order_item =& $this->Application->recallObject('orditems.-item', null, Array('skip_autoload' => true));
$combination_item =& $this->Application->recallObject('poc.-item', null, Array('skip_autoload' => true));
$combinations = $this->queryCombinations($order_items);
$event->status = kEvent::erSUCCESS;
while (!$order_items->EOL()) {
$rec = $order_items->getCurrentRecord();
$product_object->Load( $rec['ProductId'] );
if (!$product_object->GetDBField('InventoryStatus')) {
$order_items->GoNext();
continue;
}
$inv_object =& $this->getInventoryObject($product_object, $combination_item, $combinations[ $rec['ProductId'].'_'.$rec['OptionsSalt'] ]);
$lack = $rec['Quantity'] - $rec['QuantityReserved'];
if ($lack > 0) {
// reserve lack or what is available (in case if we need to reserve anything, by Alex)
$to_reserve = min($lack, $inv_object->GetDBField('QtyInStock') - $product_object->GetDBField('QtyInStockMin'));
if ($to_reserve < $lack) $event->status = kEvent::erFAIL; // if we can't reserve the full lack
//reserve in order
$order_item->SetDBFieldsFromHash($rec);
$order_item->SetDBField('QuantityReserved', $rec['QuantityReserved'] + $to_reserve);
$order_item->SetId($rec['OrderItemId']);
$order_item->Update();
//update product - increase reserved, decrease in stock
$inv_object->SetDBField('QtyReserved', $inv_object->GetDBField('QtyReserved') + $to_reserve);
$inv_object->SetDBField('QtyInStock', $inv_object->GetDBField('QtyInStock') - $to_reserve);
$inv_object->SetDBField('QtyBackOrdered', $inv_object->GetDBField('QtyBackOrdered') - $to_reserve);
$inv_object->Update();
if ($product_object->GetDBField('InventoryStatus') == 2) {
// inventory by options, then restore changed combination values back to common $combinations array !!!
$combinations[ $rec['ProductId'].'_'.$rec['OptionsSalt'] ] = $inv_object->GetFieldValues();
}
}
$order_items->GoNext();
}
return true;
}
function OnOrderPrint(&$event)
{
$event->setRedirectParams(Array('opener'=>'s'), true);
}
/**
* Processes order each tab info resetting to other tab info / to user info
*
* @param kEvent $event
* @access public
*/
function OnResetAddress(&$event)
{
$to_tab = $this->Application->GetVar('to_tab');
$from_tab = substr($event->Name,strlen('OnResetTo'));
// load values from db
$object =& $event->getObject();
// update values from submit
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
if($items_info) $field_values = array_shift($items_info);
$object->SetFieldsFromHash($field_values);
$this->DoResetAddress($object, $from_tab, $to_tab);
$object->Update();
$event->redirect = false;
}
/**
* Processes item selection from popup item selector
*
* @todo Is this called ? (by Alex)
* @param kEvent $event
*/
function OnProcessSelected(&$event)
{
$selected_ids = $this->Application->GetVar('selected_ids');
$product_ids = $selected_ids['p'];
if ($product_ids) {
$product_ids = explode(',', $product_ids);
// !!! LOOK OUT - Adding items to Order in admin is handled in order_ITEMS_event_handler !!!
foreach ($product_ids as $product_id) {
$this->AddItemToOrder($event, $product_id);
}
}
- $this->finalizePopup($event);
+ $event->SetRedirectParam('opener', 'u');
}
function OnMassPlaceOrder(&$event)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
$ids = $this->StoreSelectedIDs($event);
if($ids)
{
foreach($ids as $id)
{
$object->Load($id);
$this->DoPlaceOrder($event);
}
}
$event->status = kEvent::erSUCCESS;
}
/**
* Universal
* Checks if QtyInStock is enough to fullfill backorder (Qty - QtyReserved in order)
*
* @param int $ord_id
* @return bool
*/
function ReadyToProcess($ord_id)
{
$poc_table = $this->Application->getUnitOption('poc', 'TableName');
$query = ' SELECT SUM(IF( IF('.TABLE_PREFIX.'Products.InventoryStatus = 2, '.$poc_table.'.QtyInStock, '.TABLE_PREFIX.'Products.QtyInStock) - '.TABLE_PREFIX.'Products.QtyInStockMin >= ('.TABLE_PREFIX.'OrderItems.Quantity - '.TABLE_PREFIX.'OrderItems.QuantityReserved), 0, 1))
FROM '.TABLE_PREFIX.'OrderItems
LEFT JOIN '.TABLE_PREFIX.'Products ON '.TABLE_PREFIX.'Products.ProductId = '.TABLE_PREFIX.'OrderItems.ProductId
LEFT JOIN '.$poc_table.' ON ('.$poc_table.'.CombinationCRC = '.TABLE_PREFIX.'OrderItems.OptionsSalt) AND ('.$poc_table.'.ProductId = '.TABLE_PREFIX.'OrderItems.ProductId)
WHERE OrderId = '.$ord_id.'
GROUP BY OrderId';
// IF (IF(InventoryStatus = 2, poc.QtyInStock, p.QtyInStock) - QtyInStockMin >= (Quantity - QuantityReserved), 0, 1
return ($this->Conn->GetOne($query) == 0);
}
/**
* Return all option combinations used in order
*
* @param kDBList $order_items
* @return Array
*/
function queryCombinations(&$order_items)
{
// 1. collect combination crc used in order
$combinations = Array();
while (!$order_items->EOL()) {
$row = $order_items->getCurrentRecord();
if ($row['OptionsSalt'] == 0) {
$order_items->GoNext();
continue;
}
$combinations[] = '(poc.ProductId = '.$row['ProductId'].') AND (poc.CombinationCRC = '.$row['OptionsSalt'].')';
$order_items->GoNext();
}
$order_items->GoFirst();
$combinations = array_unique($combinations); // if same combination+product found as backorder & normal order item
if ($combinations) {
// 2. query data about combinations
$poc_table = $this->Application->getUnitOption('poc', 'TableName');
$sql = 'SELECT CONCAT(poc.ProductId, "_", poc.CombinationCRC) AS CombinationKey, poc.*
FROM '.$poc_table.' poc
WHERE ('.implode(') OR (', $combinations).')';
return $this->Conn->Query($sql, 'CombinationKey');
}
return Array();
}
/**
* Returns object to perform inventory actions on
*
* @param ProductsItem $product current product object in order
* @param kDBItem $combination combination dummy object
* @param Array $combination_data pre-queried combination data
* @return kDBItem
*/
function &getInventoryObject(&$product, &$combination, $combination_data)
{
if ($product->GetDBField('InventoryStatus') == 2) {
// inventory by option combinations
$combination->SetDBFieldsFromHash($combination_data);
$combination->setID($combination_data['CombinationId']);
$change_item =& $combination;
}
else {
// inventory by product ifself
$change_item =& $product;
}
return $change_item;
}
/**
* Approve order ("Pending" tab)
*
* @param kDBList $order_items
* @return int new status of order if any
*/
function approveOrder(&$order_items)
{
$product_object =& $this->Application->recallObject('p', null, Array('skip_autoload' => true));
$order_item =& $this->Application->recallObject('orditems.-item', null, Array('skip_autoload' => true));
$combination_item =& $this->Application->recallObject('poc.-item', null, Array('skip_autoload' => true));
$combinations = $this->queryCombinations($order_items);
while (!$order_items->EOL()) {
$rec = $order_items->getCurrentRecord();
$order_item->SetDBFieldsFromHash($rec);
$order_item->SetId($rec['OrderItemId']);
$order_item->SetDBField('QuantityReserved', 0);
$order_item->Update();
$product_object->Load( $rec['ProductId'] );
if (!$product_object->GetDBField('InventoryStatus')) {
// if no inventory info is collected, then skip this order item
$order_items->GoNext();
continue;
}
$inv_object =& $this->getInventoryObject($product_object, $combination_item, $combinations[ $rec['ProductId'].'_'.$rec['OptionsSalt'] ]);
// decrease QtyReserved by amount of product used in order
$inv_object->SetDBField('QtyReserved', $inv_object->GetDBField('QtyReserved') - $rec['Quantity']);
$inv_object->Update();
if ($product_object->GetDBField('InventoryStatus') == 2) {
// inventory by options, then restore changed combination values back to common $combinations array !!!
$combinations[ $rec['ProductId'].'_'.$rec['OptionsSalt'] ] = $inv_object->GetFieldValues();
}
$order_items->GoNext();
}
return true;
}
function restoreOrder(&$order_items)
{
$product_object =& $this->Application->recallObject('p', null, Array('skip_autoload' => true));
$product_object->SwitchToLive();
$order_item =& $this->Application->recallObject('orditems.-item', null, Array('skip_autoload' => true));
$combination_item =& $this->Application->recallObject('poc.-item', null, Array('skip_autoload' => true));
$combinations = $this->queryCombinations($order_items);
while( !$order_items->EOL() )
{
$rec = $order_items->getCurrentRecord();
$product_object->Load( $rec['ProductId'] );
if (!$product_object->GetDBField('InventoryStatus')) {
// if no inventory info is collected, then skip this order item
$order_items->GoNext();
continue;
}
$inv_object =& $this->getInventoryObject($product_object, $combination_item, $combinations[ $rec['ProductId'].'_'.$rec['OptionsSalt'] ]);
// cancelling backorderd qty if any
$lack = $rec['Quantity'] - $rec['QuantityReserved'];
if ($lack > 0 && $rec['BackOrderFlag'] > 0) { // lack should have been recorded as QtyBackOrdered
$inv_object->SetDBField('QtyBackOrdered', $inv_object->GetDBField('QtyBackOrdered') - $lack);
}
// canceling reservation in stock
$inv_object->SetDBField('QtyReserved', $inv_object->GetDBField('QtyReserved') - $rec['QuantityReserved']);
// putting remaining freed qty back to stock
$inv_object->SetDBField('QtyInStock', $inv_object->GetDBField('QtyInStock') + $rec['QuantityReserved']);
$inv_object->Update();
$product_h =& $this->Application->recallObject('p_EventHandler');
if ($product_object->GetDBField('InventoryStatus') == 2) {
// inventory by options, then restore changed combination values back to common $combinations array !!!
$combinations[ $rec['ProductId'].'_'.$rec['OptionsSalt'] ] = $inv_object->GetFieldValues();
// using freed qty to fullfill possible backorders
$product_h->FullfillBackOrders($product_object, $inv_object->GetID());
}
else {
// using freed qty to fullfill possible backorders
$product_h->FullfillBackOrders($product_object, 0);
}
$order_item->SetDBFieldsFromHash($rec);
$order_item->SetId($rec['OrderItemId']);
$order_item->SetDBField('QuantityReserved', 0);
$order_item->Update();
$order_items->GoNext();
}
return true;
}
/**
* Approve order + special processing
*
* @param kEvent $event
*/
function MassInventoryAction(&$event)
{
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
$event->status = kEvent::erFAIL;
return;
}
// process order products
$object =& $this->Application->recallObject($event->Prefix.'.-inv', null, Array('skip_autoload' => true));
$ids = $this->StoreSelectedIDs($event);
if($ids)
{
foreach($ids as $id)
{
$object->Load($id);
$this->InventoryAction($event);
}
}
}
function InventoryAction(&$event)
{
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 1)) {
$event->status = kEvent::erFAIL;
return;
}
$event_status_map = Array(
'OnMassOrderApprove' => ORDER_STATUS_TOSHIP,
'OnOrderApprove' => ORDER_STATUS_TOSHIP,
'OnMassOrderDeny' => ORDER_STATUS_DENIED,
'OnOrderDeny' => ORDER_STATUS_DENIED,
'OnMassOrderArchive' => ORDER_STATUS_ARCHIVED,
'OnOrderArchive' => ORDER_STATUS_ARCHIVED,
'OnMassOrderShip' => ORDER_STATUS_PROCESSED,
'OnOrderShip' => ORDER_STATUS_PROCESSED,
'OnMassOrderProcess' => ORDER_STATUS_TOSHIP,
'OnOrderProcess' => ORDER_STATUS_TOSHIP,
);
$order_items =& $this->Application->recallObject('orditems.-inv','orditems_List',Array('skip_counting'=>true,'per_page'=>-1) );
$order_items->linkToParent('-inv');
$order_items->Query();
$order_items->GoFirst();
$object =& $this->Application->recallObject($event->Prefix.'.-inv');
/* @var $object OrdersItem */
if ($object->GetDBField('OnHold')) {
// any actions have no effect while on hold
return ;
}
// preparing new status, but not setting it yet
$object->SetDBField('Status', $event_status_map[$event->Name]);
$set_new_status = false;
$event->status = kEvent::erSUCCESS;
$email_params = $this->OrderEmailParams($object);
switch ($event->Name) {
case 'OnMassOrderApprove':
case 'OnOrderApprove':
$set_new_status = false; //on succsessfull approve order will be split and new orders will have new statuses
if ($object->GetDBField('ChargeOnNextApprove')) {
$charge_info = $this->ChargeOrder($object);
if (!$charge_info['result']) {
break;
}
// removing ChargeOnNextApprove
$object->SetDBField('ChargeOnNextApprove', 0);
$sql = 'UPDATE '.$object->TableName.' SET ChargeOnNextApprove = 0 WHERE '.$object->IDField.' = '.$object->GetID();
$this->Conn->Query($sql);
}
// charge user for order in case if we user 2step charging (e.g. AUTH_ONLY + PRIOR_AUTH_CAPTURE)
$gw_data = $object->getGatewayData();
$this->Application->registerClass( $gw_data['ClassName'], GW_CLASS_PATH.'/'.$gw_data['ClassFile'] );
$gateway_object =& $this->Application->recallObject( $gw_data['ClassName'] );
$charge_result = $gateway_object->Charge($object->GetFieldValues(), $gw_data['gw_params']);
$sql = 'UPDATE %s SET GWResult2 = %s WHERE %s = %s';
$sql = sprintf($sql, $object->TableName, $this->Conn->qstr($gateway_object->getGWResponce()), $object->IDField, $object->GetID() );
$this->Conn->Query($sql);
$object->SetDBField('GWResult2', $gateway_object->getGWResponce() );
if ($charge_result) {
$product_object =& $this->Application->recallObject('p', null, Array('skip_autoload' => true));
foreach ($order_items->Records as $product_item) {
if (!$product_item['ProductId']) {
// product may have been deleted
continue;
}
$product_object->Load($product_item['ProductId']);
$hits = floor( $product_object->GetDBField('Hits') ) + 1;
$sql = 'SELECT MAX(Hits) FROM '.$this->Application->getUnitOption('p', 'TableName').'
WHERE FLOOR(Hits) = '.$hits;
$hits = ( $res = $this->Conn->GetOne($sql) ) ? $res + 0.000001 : $hits;
$product_object->SetDBField('Hits', $hits);
$product_object->Update();
/*$sql = 'UPDATE '.$this->Application->getUnitOption('p', 'TableName').'
SET Hits = Hits + '.$product_item['Quantity'].'
WHERE ProductId = '.$product_item['ProductId'];
$this->Conn->Query($sql);*/
}
$this->PrepareCoupons($event, $object);
$this->SplitOrder($event, $object);
if ($object->GetDBField('IsRecurringBilling') != 1) {
$email_event_user =& $this->Application->EmailEventUser('ORDER.APPROVE', $object->GetDBField('PortalUserId'), $email_params);
// Mask credit card with XXXX
if ($this->Application->ConfigValue('Comm_MaskProcessedCreditCards')) {
$this->maskCreditCard($object, 'PaymentAccount');
$set_new_status = 1;
}
}
}
break;
case 'OnMassOrderDeny':
case 'OnOrderDeny':
foreach ($order_items->Records as $product_item) {
if (!$product_item['ProductId']) {
// product may have been deleted
continue;
}
$this->raiseProductEvent('Deny', $product_item['ProductId'], $product_item);
}
$email_event_user =& $this->Application->EmailEventUser('ORDER.DENY', $object->GetDBField('PortalUserId'), $email_params);
if ($event->Name == 'OnMassOrderDeny' || $event->Name == 'OnOrderDeny') {
// inform payment gateway that order was declined
$gw_data = $object->getGatewayData();
$this->Application->registerClass( $gw_data['ClassName'], GW_CLASS_PATH.'/'.$gw_data['ClassFile'] );
$gateway_object =& $this->Application->recallObject( $gw_data['ClassName'] );
$gateway_object->OrderDeclined($object->GetFieldValues(), $gw_data['gw_params']);
}
// !!! LOOK HERE !!!
// !!!! no break !!!! here on purpose!!!
case 'OnMassOrderArchive':
case 'OnOrderArchive':
// it's critical to update status BEFORE processing items because
// FullfillBackorders could be called during processing and in case
// of order denial/archive fullfill could reserve the qtys back for current backorder
$object->Update();
$this->restoreOrder($order_items);
$set_new_status = false; // already set
break;
case 'OnMassOrderShip':
case 'OnOrderShip':
$ret = Array ();
$shipping_info = $object->GetDBField('ShippingInfo');
if ($shipping_info) {
$quote_engine_collector =& $this->Application->recallObject('ShippingQuoteCollector');
/* @var $quote_engine_collector ShippingQuoteCollector */
$shipping_info = unserialize($shipping_info);
$sqe_class_name = $quote_engine_collector->GetClassByType($shipping_info, 1);
}
// try to create usps order
if (($object->GetDBField('ShippingType') == 0) && ($sqe_class_name !== false)) {
$shipping_quote_engine =& $this->Application->recallObject($sqe_class_name);
/* @var $shipping_quote_engine ShippingQuoteEngine */
$ret = $shipping_quote_engine->MakeOrder($object);
}
if ( !array_key_exists('error_number', $ret) ) {
$set_new_status = $this->approveOrder($order_items);
// $set_new_status = $this->shipOrder($order_items);
$object->SetDBField('ShippingDate', adodb_mktime());
$object->UpdateFormattersSubFields();
$shipping_email = $object->GetDBField('ShippingEmail');
$email_params['to_email'] = $shipping_email ? $shipping_email : $email_params['_user_email'];
$email_event_user =& $this->Application->EmailEventUser('ORDER.SHIP', $object->GetDBField('PortalUserId'), $email_params);
// inform payment gateway that order was shipped
$gw_data = $object->getGatewayData();
$this->Application->registerClass( $gw_data['ClassName'], GW_CLASS_PATH.'/'.$gw_data['ClassFile'] );
$gateway_object =& $this->Application->recallObject( $gw_data['ClassName'] );
$gateway_object->OrderShipped($object->GetFieldValues(), $gw_data['gw_params']);
}
else {
$sqe_errors = $this->Application->RecallVar('sqe_errors');
$sqe_errors = $sqe_errors ? unserialize($sqe_errors) : Array ();
$sqe_errors[ $object->GetField('OrderNumber') ] = $ret['error_description'];
$this->Application->StoreVar('sqe_errors', serialize($sqe_errors));
}
break;
case 'OnMassOrderProcess':
case 'OnOrderProcess':
if ($this->ReadyToProcess($object->GetID())) {
$event->CallSubEvent('OnReserveItems');
if ($event->status == kEvent::erSUCCESS) $set_new_status = true;
$email_event_user =& $this->Application->EmailEventUser('BACKORDER.PROCESS', $object->GetDBField('PortalUserId'), $email_params);
} else {
$event->status = kEvent::erFAIL;
}
break;
}
if ($set_new_status) {
$object->Update();
}
}
/**
* Hides last 4 digits from credit card number
*
* @param OrdersItem $object
* @param string $field
*/
function maskCreditCard(&$object, $field)
{
$value = $object->GetDBField($field);
$value = preg_replace('/'.substr($value, -4).'$/', str_repeat('X', 4), $value);
$object->SetDBField($field, $value);
}
/**
* Set next available order number
*
* @param kEvent $event
*/
function setNextOrderNumber(&$event)
{
$object =& $event->getObject();
/* @var $object OrdersItem */
$sql = 'SELECT MAX(Number)
FROM ' . $this->Application->GetLiveName($object->TableName);
$next_order_number = $this->Conn->GetOne($sql) + 1;
$next_order_number = max($next_order_number, $this->Application->ConfigValue('Comm_Next_Order_Number'));
$this->Application->SetConfigValue('Comm_Next_Order_Number', $next_order_number + 1);
$object->SetDBField('Number', $next_order_number);
$object->SetDBField('SubNumber', 0);
// set virtual field too
$number_format = (int)$this->Application->ConfigValue('Comm_Order_Number_Format_P');
$sub_number_format = (int)$this->Application->ConfigValue('Comm_Order_Number_Format_S');
$order_number = sprintf('%0' . $number_format . 'd', $next_order_number) . '-' . str_repeat('0', $sub_number_format);
$object->SetDBField('OrderNumber', $order_number);
}
/**
* Set's new order address based on another address from order (e.g. billing from shipping)
*
* @param unknown_type $object
* @param unknown_type $from
* @param unknown_type $to
*/
function DoResetAddress(&$object, $from, $to)
{
$fields = Array('To','Company','Phone','Fax','Email','Address1','Address2','City','State','Zip','Country');
if ($from == 'User') {
// skip theese fields when coping from user, because they are not present in user profile
$tmp_fields = array_flip($fields);
// unset($tmp_fields['Company'], $tmp_fields['Fax'], $tmp_fields['Address2']);
$fields = array_flip($tmp_fields);
}
// apply modification
foreach ($fields as $field_name) {
$object->SetDBField($to.$field_name, $object->GetDBField($from.$field_name));
}
}
/**
* Set's additional view filters set from "Orders" => "Search" tab
*
* @param kEvent $event
*/
function AddFilters(&$event)
{
parent::AddFilters($event);
if($event->Special != 'search') return true;
$search_filter = $this->Application->RecallVar('ord.search_search_filter');
if(!$search_filter) return false;
$search_filter = unserialize($search_filter);
$event->setPseudoClass('_List');
$object =& $event->getObject();
foreach($search_filter as $filter_name => $filter_params)
{
$filter_type = $filter_params['type'] == 'where' ? kDBList::WHERE_FILTER : kDBList::HAVING_FILTER;
$object->addFilter($filter_name, $filter_params['value'], $filter_type, kDBList::FLT_VIEW);
}
}
/**
* Set's status incomplete to all cloned orders
*
* @param kEvent $event
*/
function OnAfterClone(&$event)
{
$id = $event->getEventParam('id');
$table = $this->Application->getUnitOption($event->Prefix,'TableName');
$id_field = $this->Application->getUnitOption($event->Prefix,'IDField');
// set cloned order status to Incomplete
$sql = 'UPDATE '.$table.' SET Status = 0 WHERE '.$id_field.' = '.$id;
$this->Conn->Query($sql);
}
/* ======================== COMMON CODE ======================== */
/**
* Split one timestamp field into 2 virtual fields
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemLoad(&$event)
{
parent::OnAfterItemLoad($event);
$object =& $event->getObject();
/* @var $object kDBItem */
// get user fields
$user_id = $object->GetDBField('PortalUserId');
if ( $user_id ) {
$user_info = $this->Conn->GetRow('SELECT *, CONCAT(FirstName,\' \',LastName) AS UserTo FROM '.TABLE_PREFIX.'PortalUser WHERE PortalUserId = '.$user_id);
$fields = Array(
'UserTo'=>'UserTo','UserPhone'=>'Phone','UserFax'=>'Fax','UserEmail'=>'Email',
'UserAddress1'=>'Street','UserAddress2'=>'Street2','UserCity'=>'City','UserState'=>'State',
'UserZip'=>'Zip','UserCountry'=>'Country','UserCompany'=>'Company'
);
foreach ($fields as $object_field => $user_field) {
$object->SetDBField($object_field, $user_info[$user_field]);
}
}
$object->SetDBField('PaymentCVV2', $this->Application->RecallVar('CVV2Code'));
$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$cs_helper->PopulateStates($event, 'ShippingState', 'ShippingCountry');
$cs_helper->PopulateStates($event, 'BillingState', 'BillingCountry');
$this->SetStepRequiredFields($event);
// needed in OnAfterItemUpdate
$this->Application->SetVar('OriginalShippingOption', $object->GetDBField('ShippingOption'));
}
/**
* Processes states
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemCreate(&$event)
{
parent::OnBeforeItemCreate($event);
$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$cs_helper->PopulateStates($event, 'ShippingState', 'ShippingCountry');
$cs_helper->PopulateStates($event, 'BillingState', 'BillingCountry');
}
/**
* Processes states
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemUpdate(&$event)
{
parent::OnBeforeItemUpdate($event);
$object =& $event->getObject();
/* @var $object OrdersItem */
$old_payment_type = $object->GetOriginalField('PaymentType');
$new_payment_type = $object->GetDBField('PaymentType');
if ( $new_payment_type != $old_payment_type ) {
// payment type changed -> check that it's allowed
$available_payment_types = $this->Application->siteDomainField('PaymentTypes');
if ( $available_payment_types ) {
if ( strpos($available_payment_types, '|' . $new_payment_type . '|') === false ) {
// payment type isn't allowed in site domain
$object->SetDBField('PaymentType', $old_payment_type);
}
}
}
$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$cs_helper->PopulateStates($event, 'ShippingState', 'ShippingCountry');
$cs_helper->PopulateStates($event, 'BillingState', 'BillingCountry');
if ( $object->HasTangibleItems() ) {
$cs_helper->CheckStateField($event, 'ShippingState', 'ShippingCountry', false);
}
$cs_helper->CheckStateField($event, 'BillingState', 'BillingCountry', false);
if ( $object->GetDBField('Status') > ORDER_STATUS_PENDING ) {
return ;
}
$this->CheckUser($event);
if ( !$object->GetDBField('OrderIP') ) {
$object->SetDBField('OrderIP', $_SERVER['REMOTE_ADDR']);
}
$shipping_option = $this->Application->GetVar('OriginalShippingOption');
$new_shipping_option = $object->GetDBField('ShippingOption');
if ( $shipping_option != $new_shipping_option ) {
$this->UpdateShippingOption($event);
}
else {
$this->UpdateShippingTypes($event);
}
$this->RecalculateProcessingFee($event);
$this->UpdateShippingTotal($event);
$this->RecalculateGift($event);
}
/**
* Apply any custom changes to list's sql query
*
* @param kEvent $event
* @return void
* @access protected
* @see kDBEventHandler::OnListBuild()
*/
protected function SetCustomQuery(&$event)
{
$object =& $event->getObject();
$types = $event->getEventParam('types');
if($types == 'myorders' || $types == 'myrecentorders')
{
$user_id = $this->Application->RecallVar('user_id');
$object->addFilter('myitems_user1','%1$s.PortalUserId = '.$user_id);
$object->addFilter('myitems_user2','%1$s.PortalUserId > 0');
$object->addFilter('Status','%1$s.Status != 0');
}
else if ($event->Special == 'returns') {
// $object->addFilter('returns_filter',TABLE_PREFIX.'Orders.Status = '.ORDER_STATUS_PROCESSED.' AND (
// SELECT SUM(ReturnType)
// FROM '.TABLE_PREFIX.'OrderItems oi
// WHERE oi.OrderId = '.TABLE_PREFIX.'Orders.OrderId
// ) > 0');
$object->addFilter('returns_filter',TABLE_PREFIX.'Orders.Status = '.ORDER_STATUS_PROCESSED.' AND '.TABLE_PREFIX.'Orders.ReturnTotal > 0');
}
else if ($event->Special == 'user') {
$user_id = $this->Application->GetVar('u_id');
$object->addFilter('user_filter','%1$s.PortalUserId = '.$user_id);
}
else {
$special = $event->Special ? $event->Special : $this->Application->GetVar('order_type');
if ($special != 'search') {
// don't filter out orders by special in case of search tab
$object->addFilter( 'status_filter', '%1$s.Status='.$this->getTypeBySpecial($special) );
}
if ( $event->getEventParam('selected_only') ) {
$ids = $this->StoreSelectedIDs($event);
$object->addFilter( 'selected_filter', '%1$s.OrderId IN ('.implode(',', $ids).')');
}
}
}
function getTypeBySpecial($special)
{
$special2type = Array('incomplete'=>0,'pending'=>1,'backorders'=>2,'toship'=>3,'processed'=>4,'denied'=>5,'archived'=>6);
return $special2type[$special];
}
function getSpecialByType($type)
{
$type2special = Array(0=>'incomplete',1=>'pending',2=>'backorders',3=>'toship',4=>'processed',5=>'denied',6=>'archived');
return $type2special[$type];
}
function LockTables(&$event)
{
$read = Array();
$write_lock = '';
$read_lock = '';
$write = Array('Orders','OrderItems','Products');
foreach ($write as $tbl) {
$write_lock .= TABLE_PREFIX.$tbl.' WRITE,';
}
foreach ($read as $tbl) {
$read_lock .= TABLE_PREFIX.$tbl.' READ,';
}
$write_lock = rtrim($write_lock, ',');
$read_lock = rtrim($read_lock, ',');
$lock = trim($read_lock.','.$write_lock, ',');
//$this->Conn->Query('LOCK TABLES '.$lock);
}
/**
* Checks shopping cart products quantities
*
* @param kEvent $event
* @return bool
*/
function CheckQuantites(&$event)
{
- if ($this->OnRecalculateItems($event)) { // if something has changed in the order
- if ($this->Application->isAdminUser) {
- if ($this->UseTempTables($event)) {
+ if ( $this->OnRecalculateItems($event) ) { // if something has changed in the order
+ if ( $this->Application->isAdminUser ) {
+ if ( $this->UseTempTables($event) ) {
$event->redirect = 'in-commerce/orders/orders_edit_items';
}
}
else {
$event->redirect = $this->Application->GetVar('viewcart_template');
}
return false;
}
return true;
}
function DoPlaceOrder(&$event)
{
$order =& $event->getObject();
$table_prefix = $this->TablePrefix($event);
$this->LockTables($event);
if (!$this->CheckQuantites($event)) return false;
//everything is fine - we could reserve items
$this->ReserveItems($event);
$this->SplitOrder($event, $order);
return true;
}
function &queryOrderItems(&$event, $table_prefix)
{
$order =& $event->getObject();
$ord_id = $order->GetId();
// TABLE_PREFIX and $table_prefix are NOT the same !!!
$poc_table = $this->Application->getUnitOption('poc', 'TableName');
$query = ' SELECT
BackOrderFlag, '.
$table_prefix.'OrderItems.OrderItemId, '.
$table_prefix.'OrderItems.Quantity, '.
$table_prefix.'OrderItems.QuantityReserved,
IF('.TABLE_PREFIX.'Products.InventoryStatus = 2, '.$poc_table.'.QtyInStock, '.TABLE_PREFIX.'Products.QtyInStock) AS QtyInStock, '.
TABLE_PREFIX.'Products.QtyInStockMin, '.
$table_prefix.'OrderItems.ProductId, '.
TABLE_PREFIX.'Products.InventoryStatus,'.
$table_prefix.'OrderItems.OptionsSalt AS CombinationCRC
FROM '.$table_prefix.'OrderItems
LEFT JOIN '.TABLE_PREFIX.'Products ON '.TABLE_PREFIX.'Products.ProductId = '.$table_prefix.'OrderItems.ProductId
LEFT JOIN '.$poc_table.' ON ('.$poc_table.'.CombinationCRC = '.$table_prefix.'OrderItems.OptionsSalt) AND ('.$poc_table.'.ProductId = '.$table_prefix.'OrderItems.ProductId)
WHERE OrderId = '.$ord_id.' AND '.TABLE_PREFIX.'Products.Type = 1
ORDER BY BackOrderFlag ASC';
$items = $this->Conn->Query($query);
return $items;
}
function ReserveItems(&$event)
{
$table_prefix = $this->TablePrefix($event);
$items =& $this->queryOrderItems($event, $table_prefix);
foreach ($items as $an_item) {
if (!$an_item['InventoryStatus']) {
$to_reserve = $an_item['Quantity'] - $an_item['QuantityReserved'];
}
else {
if ($an_item['BackOrderFlag'] > 0) { // we don't need to reserve if it's backordered item
$to_reserve = 0;
}
else {
$to_reserve = min($an_item['Quantity']-$an_item['QuantityReserved'], $an_item['QtyInStock']-$an_item['QtyInStockMin']); //it should be equal, but just in case
}
$to_backorder = $an_item['BackOrderFlag'] > 0 ? $an_item['Quantity']-$an_item['QuantityReserved'] : 0;
}
if ($to_backorder < 0) $to_backorder = 0; //just in case
$query = ' UPDATE '.$table_prefix.'OrderItems
SET QuantityReserved = IF(QuantityReserved IS NULL, '.$to_reserve.', QuantityReserved + '.$to_reserve.')
WHERE OrderItemId = '.$an_item['OrderItemId'];
$this->Conn->Query($query);
if (!$an_item['InventoryStatus']) continue;
$update_clause = ' QtyInStock = QtyInStock - '.$to_reserve.',
QtyReserved = QtyReserved + '.$to_reserve.',
QtyBackOrdered = QtyBackOrdered + '.$to_backorder;
if ($an_item['InventoryStatus'] == 1) {
// inventory by product, then update it's quantities
$query = ' UPDATE '.TABLE_PREFIX.'Products
SET '.$update_clause.'
WHERE ProductId = '.$an_item['ProductId'];
}
else {
// inventory = 2 -> by product option combinations
$poc_idfield = $this->Application->getUnitOption('poc', 'IDField');
$poc_table = $this->Application->getUnitOption('poc', 'TableName');
$query = ' UPDATE '.$poc_table.'
SET '.$update_clause.'
WHERE (ProductId = '.$an_item['ProductId'].') AND (CombinationCRC = '.$an_item['CombinationCRC'].')';
}
$this->Conn->Query($query);
}
}
function FreeItems(&$event)
{
$table_prefix = $this->TablePrefix($event);
$items =& $this->queryOrderItems($event, $table_prefix);
foreach ($items as $an_item) {
$to_free = $an_item['QuantityReserved'];
if ($an_item['InventoryStatus']) {
if ($an_item['BackOrderFlag'] > 0) { // we don't need to free if it's backordered item
$to_free = 0;
}
// what's not reserved goes to backorder in stock for orderitems marked with BackOrderFlag
$to_backorder_free = $an_item['BackOrderFlag'] > 0 ? $an_item['Quantity'] - $an_item['QuantityReserved'] : 0;
if ($to_backorder_free < 0) $to_backorder_free = 0; //just in case
$update_clause = ' QtyInStock = QtyInStock + '.$to_free.',
QtyReserved = QtyReserved - '.$to_free.',
QtyBackOrdered = QtyBackOrdered - '.$to_backorder_free;
if ($an_item['InventoryStatus'] == 1) {
// inventory by product
$query = ' UPDATE '.TABLE_PREFIX.'Products
SET '.$update_clause.'
WHERE ProductId = '.$an_item['ProductId'];
}
else {
// inventory by option combinations
$poc_idfield = $this->Application->getUnitOption('poc', 'IDField');
$poc_table = $this->Application->getUnitOption('poc', 'TableName');
$query = ' UPDATE '.$poc_table.'
SET '.$update_clause.'
WHERE (ProductId = '.$an_item['ProductId'].') AND (CombinationCRC = '.$an_item['CombinationCRC'].')';
}
$this->Conn->Query($query);
}
$query = ' UPDATE '.$table_prefix.'OrderItems
SET QuantityReserved = IF(QuantityReserved IS NULL, 0, QuantityReserved - '.$to_free.')
WHERE OrderItemId = '.$an_item['OrderItemId'];
$this->Conn->Query($query);
}
}
/**
* Enter description here...
*
* @param kEvent $event
* @param OrdersItem $object
*/
function SplitOrder(&$event, &$object)
{
$affiliate_event = new kEvent('affil:OnOrderApprove');
$affiliate_event->setEventParam('Order_PrefixSpecial', $object->getPrefixSpecial() );
$this->Application->HandleEvent($affiliate_event);
$table_prefix = $this->TablePrefix($event);
$order =& $object;
$ord_id = $order->GetId();
$shipping_option = $order->GetDBField('ShippingOption');
- $backorder_select = $shipping_option == 0 ? '0 As BackOrderFlag' : 'BackOrderFlag';
+ $backorder_select = $shipping_option == 0 ? '0' : 'oi.BackOrderFlag';
// setting PackageNum to 0 for Non-tangible items, for tangibles first package num is always 1
- $query = ' SELECT OrderItemId
- FROM '.$table_prefix.'OrderItems
- LEFT JOIN '.TABLE_PREFIX.'Products
- ON '.TABLE_PREFIX.'Products.ProductId = '.$table_prefix.'OrderItems.ProductId
- WHERE '.TABLE_PREFIX.'Products.Type > 1 AND OrderId = '.$ord_id;
+ $query = ' SELECT oi.OrderItemId
+ FROM ' . $table_prefix . 'OrderItems oi
+ LEFT JOIN ' . TABLE_PREFIX . 'Products p ON p.ProductId = oi.ProductId
+ WHERE p.Type > 1 AND oi.OrderId = ' . $ord_id;
$non_tangibles = $this->Conn->GetCol($query);
+
if ($non_tangibles) {
- $query = 'UPDATE '.$table_prefix.'OrderItems SET PackageNum = 0 WHERE OrderItemId IN ('.implode(',', $non_tangibles).')';
+ $query = ' UPDATE ' . $table_prefix . 'OrderItems
+ SET PackageNum = 0
+ WHERE OrderItemId IN (' . implode(',', $non_tangibles) . ')';
$this->Conn->Query($query);
}
// grouping_data:
// 0 => Product Type
// 1 => if NOT tangibale and NOT downloadable - OrderItemId,
// 2 => ProductId
// 3 => Shipping PackageNum
$query = 'SELECT
- '.$backorder_select.',
+ '.$backorder_select.' AS BackOrderFlagCalc,
PackageNum,
ProductName,
ShippingTypeId,
CONCAT('.TABLE_PREFIX.'Products.Type,
"_",
IF ('.TABLE_PREFIX.'Products.Type NOT IN ('.PRODUCT_TYPE_DOWNLOADABLE.','.PRODUCT_TYPE_TANGIBLE.'),
CONCAT(OrderItemId, "_", '.TABLE_PREFIX.'Products.ProductId),
""),
"_",
PackageNum
) AS Grouping,
SUM(Quantity) AS TotalItems,
SUM('.$table_prefix.'OrderItems.Weight*Quantity) AS TotalWeight,
SUM(Price * Quantity) AS TotalAmount,
SUM(QuantityReserved) AS TotalReserved,
'.TABLE_PREFIX.'Products.Type AS ProductType
FROM '.$table_prefix.'OrderItems
LEFT JOIN '.TABLE_PREFIX.'Products
ON '.TABLE_PREFIX.'Products.ProductId = '.$table_prefix.'OrderItems.ProductId
WHERE OrderId = '.$ord_id.'
- GROUP BY BackOrderFlag, Grouping
- ORDER BY BackOrderFlag ASC, PackageNum ASC, ProductType ASC';
+ GROUP BY BackOrderFlagCalc, Grouping
+ ORDER BY BackOrderFlagCalc ASC, PackageNum ASC, ProductType ASC';
$sub_orders = $this->Conn->Query($query);
$processed_sub_orders = Array();
// in case of recurring billing this will not be 0 as usual
//$first_sub_number = ($event->Special == 'recurring') ? $object->getNextSubNumber() - 1 : 0;
$first_sub_number = $object->GetDBField('SubNumber');
$next_sub_number = $first_sub_number;
$group = 1;
$order_has_gift = $order->GetDBField('GiftCertificateDiscount') > 0 ? 1 : 0;
$skip_types = Array (PRODUCT_TYPE_TANGIBLE, PRODUCT_TYPE_DOWNLOADABLE);
foreach ($sub_orders as $sub_order_data) {
$sub_order =& $this->Application->recallObject('ord.-sub'.$next_sub_number, 'ord');
/* @var $sub_order OrdersItem */
if ($this->UseTempTables($event) && $next_sub_number == 0) {
$sub_order =& $order;
}
$sub_order->SetDBFieldsFromHash($order->GetFieldValues());
$sub_order->SetDBField('SubNumber', $next_sub_number);
$sub_order->SetDBField('SubTotal', $sub_order_data['TotalAmount']);
$grouping_data = explode('_', $sub_order_data['Grouping']);
$named_grouping_data['Type'] = $grouping_data[0];
if (!in_array($named_grouping_data['Type'], $skip_types)) {
$named_grouping_data['OrderItemId'] = $grouping_data[1];
$named_grouping_data['ProductId'] = $grouping_data[2];
$named_grouping_data['PackageNum'] = $grouping_data[3];
}
else {
$named_grouping_data['PackageNum'] = $grouping_data[2];
}
if ($named_grouping_data['Type'] == PRODUCT_TYPE_TANGIBLE) {
$sub_order->SetDBField('ShippingCost', getArrayValue( unserialize($order->GetDBField('ShippingInfo')), $sub_order_data['PackageNum'], 'TotalCost') );
$sub_order->SetDBField('InsuranceFee', getArrayValue( unserialize($order->GetDBField('ShippingInfo')), $sub_order_data['PackageNum'], 'InsuranceFee') );
$sub_order->SetDBField('ShippingInfo', serialize(Array(1 => getArrayValue( unserialize($order->GetDBField('ShippingInfo')), $sub_order_data['PackageNum']))));
}
else {
$sub_order->SetDBField('ShippingCost', 0);
$sub_order->SetDBField('InsuranceFee', 0);
$sub_order->SetDBField('ShippingInfo', ''); //otherwise orders w/o shipping wills still have shipping info!
}
$amount_percent = $sub_order->getTotalAmount() * 100 / $order->getTotalAmount();
// proportional affiliate commission splitting
if ($order->GetDBField('AffiliateCommission') > 0) {
$sub_order->SetDBField('AffiliateCommission', $order->GetDBField('AffiliateCommission') * $amount_percent / 100 );
}
$amount_percent = ($sub_order->GetDBField('SubTotal') + $sub_order->GetDBField('ShippingCost')) * 100 / ($order->GetDBField('SubTotal') + $order->GetDBField('ShippingCost'));
if ($order->GetDBField('ProcessingFee') > 0) {
$sub_order->SetDBField('ProcessingFee', round($order->GetDBField('ProcessingFee') * $amount_percent / 100, 2));
}
$sub_order->RecalculateTax();
$original_amount = $sub_order->GetDBField('SubTotal') + $sub_order->GetDBField('ShippingCost') + $sub_order->GetDBField('VAT') + $sub_order->GetDBField('ProcessingFee') + $sub_order->GetDBField('InsuranceFee') - $sub_order->GetDBField('GiftCertificateDiscount');
$sub_order->SetDBField('OriginalAmount', $original_amount);
- if ($named_grouping_data['Type'] == 1 && ($sub_order_data['BackOrderFlag'] > 0
+ if ($named_grouping_data['Type'] == 1 && ($sub_order_data['BackOrderFlagCalc'] > 0
||
($sub_order_data['TotalItems'] != $sub_order_data['TotalReserved'])) ) {
$sub_order->SetDBField('Status', ORDER_STATUS_BACKORDERS);
if ($event->Special != 'recurring') { // just in case if admin uses tangible backordered products in recurring orders
$email_event_user =& $this->Application->EmailEventUser('BACKORDER.ADD', $sub_order->GetDBField('PortalUserId'), $this->OrderEmailParams($sub_order));
$email_event_admin =& $this->Application->EmailEventAdmin('BACKORDER.ADD');
}
}
else {
switch ($named_grouping_data['Type']) {
case PRODUCT_TYPE_DOWNLOADABLE:
$sql = 'SELECT oi.*
FROM '.TABLE_PREFIX.'OrderItems oi
LEFT JOIN '.TABLE_PREFIX.'Products p ON p.ProductId = oi.ProductId
WHERE (OrderId = %s) AND (p.Type = '.PRODUCT_TYPE_DOWNLOADABLE.')';
$downl_products = $this->Conn->Query( sprintf($sql, $ord_id) );
$product_ids = Array();
foreach ($downl_products as $downl_product) {
$this->raiseProductEvent('Approve', $downl_product['ProductId'], $downl_product, $next_sub_number);
$product_ids[] = $downl_product['ProductId'];
}
break;
case PRODUCT_TYPE_TANGIBLE:
- $sql = 'SELECT '.$backorder_select.', oi.*
+ $sql = 'SELECT '.$backorder_select.' AS BackOrderFlagCalc, oi.*
FROM '.TABLE_PREFIX.'OrderItems oi
LEFT JOIN '.TABLE_PREFIX.'Products p ON p.ProductId = oi.ProductId
- WHERE (OrderId = %s) AND (BackOrderFlag = 0) AND (p.Type = '.PRODUCT_TYPE_TANGIBLE.')';
+ WHERE (OrderId = %s) AND (BackOrderFlagCalc = 0) AND (p.Type = '.PRODUCT_TYPE_TANGIBLE.')';
$products = $this->Conn->Query( sprintf($sql, $ord_id) );
foreach ($products as $product) {
$this->raiseProductEvent('Approve', $product['ProductId'], $product, $next_sub_number);
}
break;
default:
$order_item_fields = $this->Conn->GetRow('SELECT * FROM '.TABLE_PREFIX.'OrderItems WHERE OrderItemId = '.$named_grouping_data['OrderItemId']);
$this->raiseProductEvent('Approve', $named_grouping_data['ProductId'], $order_item_fields, $next_sub_number);
break;
}
$sub_order->SetDBField('Status', $named_grouping_data['Type'] == PRODUCT_TYPE_TANGIBLE ? ORDER_STATUS_TOSHIP : ORDER_STATUS_PROCESSED);
}
if ($next_sub_number == $first_sub_number) {
$sub_order->SetId($order->GetId());
$sub_order->Update();
}
else {
$sub_order->Create();
}
switch ($named_grouping_data['Type']) {
case PRODUCT_TYPE_TANGIBLE:
$query = 'UPDATE '.$table_prefix.'OrderItems SET OrderId = %s WHERE OrderId = %s AND PackageNum = %s';
$query = sprintf($query, $sub_order->GetId(), $ord_id, $sub_order_data['PackageNum']);
break;
case PRODUCT_TYPE_DOWNLOADABLE:
$query = 'UPDATE '.$table_prefix.'OrderItems SET OrderId = %s WHERE OrderId = %s AND ProductId IN (%s)';
$query = sprintf($query, $sub_order->GetId(), $ord_id, implode(',', $product_ids) );
break;
default:
$query = 'UPDATE '.$table_prefix.'OrderItems SET OrderId = %s WHERE OrderId = %s AND OrderItemId = %s';
$query = sprintf($query, $sub_order->GetId(), $ord_id, $named_grouping_data['OrderItemId']);
break;
}
$this->Conn->Query($query);
if ($order_has_gift) {
// gift certificate can be applied only after items are assigned to suborder
$sub_order->RecalculateGift($event);
$original_amount = $sub_order->GetDBField('SubTotal') + $sub_order->GetDBField('ShippingCost') + $sub_order->GetDBField('VAT') + $sub_order->GetDBField('ProcessingFee') + $sub_order->GetDBField('InsuranceFee') - $sub_order->GetDBField('GiftCertificateDiscount');
$sub_order->SetDBField('OriginalAmount', $original_amount);
$sub_order->Update();
}
$processed_sub_orders[] = $sub_order->GetID();
$next_sub_number++;
$group++;
}
foreach ($processed_sub_orders as $sub_id) {
// update DiscountTotal field
$sql = 'SELECT SUM(ROUND(FlatPrice-Price,2)*Quantity) FROM '.$table_prefix.'OrderItems WHERE OrderId = '.$sub_id;
$discount_total = $this->Conn->GetOne($sql);
$sql = 'UPDATE '.$sub_order->TableName.'
SET DiscountTotal = '.$this->Conn->qstr($discount_total).'
WHERE OrderId = '.$sub_id;
$this->Conn->Query($sql);
}
}
/**
* Call products linked event when spefcfic action is made to product in order
*
* @param string $event_type type of event to get from product ProcessingData = {Approve,Deny,CompleteOrder}
* @param int $product_id ID of product to gather processing data from
* @param Array $order_item_fields OrderItems table record fields (with needed product & order in it)
*/
function raiseProductEvent($event_type, $product_id, $order_item_fields, $next_sub_number=null)
{
$sql = 'SELECT ProcessingData
FROM '.TABLE_PREFIX.'Products
WHERE ProductId = '.$product_id;
$processing_data = $this->Conn->GetOne($sql);
if ($processing_data) {
$processing_data = unserialize($processing_data);
$event_key = getArrayValue($processing_data, $event_type.'Event');
// if requested type of event is defined for product, only then process it
if ($event_key) {
$event = new kEvent($event_key);
$event->setEventParam('field_values', $order_item_fields);
$event->setEventParam('next_sub_number', $next_sub_number);
$this->Application->HandleEvent($event);
}
}
}
function OptionsSalt($options, $comb_only=false)
{
$helper =& $this->Application->recallObject('kProductOptionsHelper');
return $helper->OptionsSalt($options, $comb_only);
}
/**
* Enter description here...
*
* @param kEvent $event
* @param int $item_id
*/
function AddItemToOrder(&$event, $item_id, $qty = null, $package_num = null)
{
if (!isset($qty)) {
$qty = 1;
}
// Loading product to add
$product =& $this->Application->recallObject('p.toadd', null, Array('skip_autoload' => true));
/* @var $product kDBItem */
$product->Load($item_id);
$object =& $this->Application->recallObject('orditems.-item', null, Array('skip_autoload' => true));
/* @var $object kDBItem */
$order =& $this->Application->recallObject('ord');
/* @var $order kDBItem */
if (!$order->isLoaded() && !$this->Application->isAdmin) {
// no order was created before -> create one now
if ($this->_createNewCart($event)) {
$this->LoadItem($event);
}
}
if (!$order->isLoaded()) {
// was unable to create new order
return false;
}
$manager =& $this->Application->recallObject('OrderManager');
/* @var $manager OrderManager */
$manager->setOrder($order);
$manager->addProduct($product, $event->getEventParam('ItemData'), $qty, $package_num);
$this->Application->HandleEvent($ord_event, 'ord:OnRecalculateItems');
-
- /*if ($ord_event->getEventParam('RecalculateChangedCart') && !$this->Application->isAdmin) {
- $event->SetRedirectParam('checkout_error', $ord_event->getRedirectParam('checkout_error'));
- }*/
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function UpdateShippingTotal(&$event)
{
- if ($this->Application->GetVar('ebay_notification') == 1) {
+ if ( $this->Application->GetVar('ebay_notification') == 1 ) {
// TODO: get rid of this "if"
- return ;
+ return;
}
- $object =& $event->getObject();
- $ord_id = $object->GetId();
- $shipping_option = $object->GetDBField('ShippingOption');
- $backorder_select = $shipping_option == 0 ? '0 As BackOrderFlag' : 'BackOrderFlag';
+ $object =& $event->getObject();
+ /* @var $object OrdersItem */
- $table_prefix = $this->TablePrefix($event);
+ $shipping_total = $insurance_fee = 0;
+ $shipping_info = $object->GetDBField('ShippingInfo') ? unserialize($object->GetDBField('ShippingInfo')) : false;
- $shipping_info = $object->GetDBField('ShippingInfo') ? unserialize( $object->GetDBField('ShippingInfo') ) : false;
- $shipping_total = 0;
- $insurance_fee = 0;
- if( is_array($shipping_info) )
- {
- foreach ($shipping_info as $a_shipping)
- {
- // $id_elements = explode('_', $a_shipping['ShippingTypeId']);
+ if ( is_array($shipping_info) ) {
+ foreach ($shipping_info as $a_shipping) {
+// $id_elements = explode('_', $a_shipping['ShippingTypeId']);
$shipping_total += $a_shipping['TotalCost'];
$insurance_fee += $a_shipping['InsuranceFee'];
}
}
+
$object->SetDBField('ShippingCost', $shipping_total);
$object->SetDBField('InsuranceFee', $insurance_fee);
// no need to update, it will be called in calling method
$this->RecalculateTax($event);
}
/**
- * Recompile shopping cart, splitting or grouping orders and backorders depending on total quantityes.
+ * Recompile shopping cart, splitting or grouping orders and backorders depending on total quantities.
* First it counts total qty for each ProductId, and then creates order for available items
* and backorder for others. It also updates the sub-total for the order
*
* @param kEvent $event
* @return bool Returns true if items splitting/grouping were changed
*/
function OnRecalculateItems(&$event)
{
if (is_object($event->MasterEvent) && ($event->MasterEvent->status != kEvent::erSUCCESS)) {
// e.g. master order update failed, don't recalculate order products
return ;
}
- if($checkout_error = $this->Application->GetVar('set_checkout_error'))
- {
- $event->SetRedirectParam('checkout_error', $checkout_error);
- }
-
$order =& $event->getObject();
/* @var $order OrdersItem */
if ( !$order->isLoaded() ) {
$this->LoadItem($event); // try to load
}
$ord_id = (int)$order->GetID();
if ( !$order->isLoaded() ) return; //order has not been created yet
if( $order->GetDBField('Status') != ORDER_STATUS_INCOMPLETE )
{
return;
}
$manager =& $this->Application->recallObject('OrderManager');
/* @var $manager OrderManager */
$manager->setOrder($order);
$result = $manager->calculate();
- if ( $manager->getError() ) {
- $event->SetRedirectParam('checkout_error', $manager->getError());
- }
-
- if ($order->GetDBField('CouponId') && $order->GetDBField('CouponDiscount') == 0) {
+ if ( $order->GetDBField('CouponId') && $order->GetDBField('CouponDiscount') == 0 ) {
$this->RemoveCoupon($order);
- $event->SetRedirectParam('checkout_error', 8);
+ $order->setCheckoutError(OrderCheckoutErrorType::COUPON, OrderCheckoutError::COUPON_REMOVED_AUTOMATICALLY);
}
- if ($result) {
+ if ( $result ) {
$this->UpdateShippingOption($event);
}
$this->UpdateShippingTotal($event);
$this->RecalculateProcessingFee($event);
$this->RecalculateTax($event);
$this->RecalculateGift($event);
- if ($event->Name != 'OnAfterItemUpdate') {
+ if ( $event->Name != 'OnAfterItemUpdate' ) {
$order->Update();
}
$event->setEventParam('RecalculateChangedCart', $result);
- if (is_object($event->MasterEvent)) {
+ if ( is_object($event->MasterEvent) ) {
$event->MasterEvent->setEventParam('RecalculateChangedCart', $result);
}
- if ($result && ($event->getEventParam('checkout_error') === false)) {
- $event->SetRedirectParam('checkout_error', 1);
- }
+ /*if ( $result && !getArrayValue($event->redirect_params, 'checkout_error') ) {
+ $event->SetRedirectParam('checkout_error', OrderCheckoutError::STATE_CHANGED);
+ }*/
- if ($result && is_object($event->MasterEvent) && $event->MasterEvent->Name == 'OnUserLogin')
- {
- if( ($shop_cart_template = $this->Application->GetVar('shop_cart_template'))
- && is_object($event->MasterEvent->MasterEvent) )
- {
- $event->MasterEvent->MasterEvent->SetRedirectParam('checkout_error', 9);
+ if ( $result && is_object($event->MasterEvent) && $event->MasterEvent->Name == 'OnUserLogin' ) {
+ $shop_cart_template = $this->Application->GetVar('shop_cart_template');
+
+ if ( $shop_cart_template && is_object($event->MasterEvent->MasterEvent) ) {
+// $event->MasterEvent->MasterEvent->SetRedirectParam('checkout_error', OrderCheckoutError::CHANGED_AFTER_LOGIN);
$event->MasterEvent->MasterEvent->redirect = $shop_cart_template;
}
}
return $result;
}
/* function GetShippingCost($user_country_id, $user_state_id, $user_zip, $weight, $items, $amount, $shipping_type)
{
$this->Application->recallObject('ShippingQuoteEngine');
$shipping_h =& $this->Application->recallObject('CustomShippingQuoteEngine');
$query = $shipping_h->QueryShippingCost($user_country_id, $user_state_id, $user_zip, $weight, $items, $amount, $shipping_type);
$cost = $this->Conn->GetRow($query);
return $cost['TotalCost'];
}*/
/**
* Return product pricing id for given product, if not passed - return primary pricing ID
*
* @param int $product_id ProductId
* @return float
*/
function GetPricingId($product_id, $item_data) {
if (!is_array($item_data)) {
$item_data = unserialize($item_data);
}
$price_id = getArrayValue($item_data, 'PricingId');
if (!$price_id) {
$price_id = $this->Application->GetVar('pr_id');
}
if (!$price_id){
$price_id = $this->Conn->GetOne('SELECT PriceId FROM '.TABLE_PREFIX.'ProductsPricing WHERE ProductId='.$product_id.' AND IsPrimary=1');
}
return $price_id;
}
function UpdateShippingOption(&$event)
{
$object =& $event->getObject();
$shipping_option = $object->GetDBField('ShippingOption');
if($shipping_option == '') return;
$table_prefix = $this->TablePrefix($event);
if ($shipping_option == 1 || $shipping_option == 0) { // backorder separately
$query = 'UPDATE '.$table_prefix.'OrderItems SET BackOrderFlag = 1 WHERE OrderId = '.$object->GetId().' AND BackOrderFlag > 1';
$this->Conn->Query($query);
}
if ($shipping_option == 2) {
$query = 'SELECT * FROM '.$table_prefix.'OrderItems WHERE OrderId = '.$object->GetId().' AND BackOrderFlag >= 1 ORDER By ProductName asc';
$items = $this->Conn->Query($query);
$backorder_flag = 2;
foreach ($items as $an_item) {
$query = 'UPDATE '.$table_prefix.'OrderItems SET BackOrderFlag = '.$backorder_flag.' WHERE OrderItemId = '.$an_item['OrderItemId'];
$this->Conn->Query($query);
$backorder_flag++;
}
}
}
/**
* Updates shipping types
*
* @param kEvent $event
* @return bool
*/
function UpdateShippingTypes(&$event)
{
$object =& $event->getObject();
/* @var $object OrdersItem */
$ord_id = $object->GetID();
$order_info = $this->Application->GetVar('ord');
$shipping_ids = getArrayValue($order_info, $ord_id, 'ShippingTypeId');
if (!$shipping_ids) {
return;
}
$ret = true;
$shipping_types = Array();
$last_shippings = unserialize( $this->Application->RecallVar('LastShippings') );
$template = $this->Application->GetVar('t');
$shipping_templates = Array ('in-commerce/checkout/shipping', 'in-commerce/orders/orders_edit_shipping');
$quote_engine_collector =& $this->Application->recallObject('ShippingQuoteCollector');
/* @var $quote_engine_collector ShippingQuoteCollector */
foreach ($shipping_ids as $package => $id) {
// try to validate
$shipping_types[$package] = $last_shippings[$package][$id];
$sqe_class_name = $quote_engine_collector->GetClassByType($shipping_types, $package);
if (($object->GetDBField('ShippingType') == 0) && ($sqe_class_name !== false) && in_array($template, $shipping_templates)) {
$shipping_quote_engine =& $this->Application->recallObject($sqe_class_name);
/* @var $shipping_quote_engine ShippingQuoteEngine */
// USPS related part
// TODO: remove USPS condition from here
// set first of found shippings just to check if any errors are returned
$current_usps_shipping_types = unserialize($this->Application->RecallVar('current_usps_shipping_types'));
$object->SetDBField('ShippingInfo', serialize( Array($package => $current_usps_shipping_types[$id])) );
$sqe_data = $shipping_quote_engine->MakeOrder($object, true);
if ( $sqe_data ) {
if ( !isset($sqe_data['error_number']) ) {
// update only international shipping
if ( $object->GetDBField('ShippingCountry') != 'USA') {
$shipping_types[$package]['TotalCost'] = $sqe_data['Postage'];
}
}
else {
$ret = false;
$this->Application->StoreVar('sqe_error', $sqe_data['error_description']);
}
}
$object->SetDBField('ShippingInfo', '');
}
}
$object->SetDBField('ShippingInfo', serialize($shipping_types));
return $ret;
}
/*function shipOrder(&$order_items)
{
$product_object =& $this->Application->recallObject('p', null, Array('skip_autoload' => true));
$order_item =& $this->Application->recallObject('orditems.-item');
while( !$order_items->EOL() )
{
$rec = $order_items->getCurrentRecord();
$order_item->SetDBFieldsFromHash($rec);
$order_item->SetId($rec['OrderItemId']);
$order_item->SetDBField('QuantityReserved', 0);
$order_item->Update();
$order_items->GoNext();
}
return true;
}*/
function RecalculateTax(&$event)
{
$object =& $event->getObject();
/* @var $object OrdersItem */
if ($object->GetDBField('Status') > ORDER_STATUS_PENDING) {
return;
}
$object->RecalculateTax();
}
function RecalculateProcessingFee(&$event)
{
$object =& $event->getObject();
// Do not reset processing fee while orders are being split (see SplitOrder)
if (preg_match("/^-sub/", $object->Special)) return;
if ($object->GetDBField('Status') > ORDER_STATUS_PENDING) return; //no changes for orders other than incomple or pending
$pt = $object->GetDBField('PaymentType');
$processing_fee = $this->Conn->GetOne('SELECT ProcessingFee FROM '.$this->Application->getUnitOption('pt', 'TableName').' WHERE PaymentTypeId = '.$pt);
$object->SetDBField( 'ProcessingFee', $processing_fee );
$this->UpdateTotals($event);
}
function UpdateTotals(&$event)
{
$object =& $event->getObject();
/* @var $object OrdersItem */
$object->UpdateTotals();
}
/*function CalculateDiscount(&$event)
{
$object =& $event->getObject();
$coupon =& $this->Application->recallObject('coup', null, Array('skip_autoload' => true));
if(!$coupon->Load( $object->GetDBField('CouponId'), 'CouponId' ))
{
return false;
}
$sql = 'SELECT Price * Quantity AS Amount, ProductId FROM '.$this->Application->getUnitOption('orditems', 'TableName').'
WHERE OrderId = '.$object->GetDBField('OrderId');
$orditems = $this->Conn->GetCol($sql, 'ProductId');
$sql = 'SELECT coupi.ItemType, p.ProductId FROM '.$this->Application->getUnitOption('coupi', 'TableName').' coupi
LEFT JOIN '.$this->Application->getUnitOption('p', 'TableName').' p
ON coupi.ItemResourceId = p.ResourceId
WHERE CouponId = '.$object->GetDBField('CouponId');
$discounts = $this->Conn->GetCol($sql, 'ProductId');
$discount_amount = 0;
foreach($orditems as $product_id => $amount)
{
if(isset($discounts[$product_id]) || array_search('0', $discounts, true) !== false)
{
switch($coupon->GetDBField('Type'))
{
case 1:
$discount_amount += $coupon->GetDBField('Amount') < $amount ? $coupon->GetDBField('Amount') : $amount;
break;
case 2:
$discount_amount += $amount * $coupon->GetDBField('Amount') / 100;
break;
default:
}
break;
}
}
$object->SetDBField('CouponDiscount', $discount_amount);
return $discount_amount;
}*/
/**
* Jumps to selected order in order's list from search tab
*
* @param kEvent $event
*/
function OnGoToOrder(&$event)
{
$id = array_shift( $this->StoreSelectedIDs($event) );
$idfield = $this->Application->getUnitOption($event->Prefix,'IDField');
$table = $this->Application->getUnitOption($event->Prefix,'TableName');
$sql = 'SELECT Status FROM %s WHERE %s = %s';
$order_status = $this->Conn->GetOne( sprintf($sql, $table, $idfield, $id) );
$prefix_special = $event->Prefix.'.'.$this->getSpecialByType($order_status);
$orders_list =& $this->Application->recallObject($prefix_special, $event->Prefix.'_List', Array('per_page'=>-1) );
$orders_list->Query();
foreach($orders_list->Records as $row_num => $record)
{
if( $record[$idfield] == $id ) break;
}
$per_page = $this->getPerPage( new kEvent($prefix_special.':OnDummy') );
$page = ceil( ($row_num+1) / $per_page );
$this->Application->StoreVar($prefix_special.'_Page', $page);
$event->redirect = 'in-commerce/orders/orders_'.$this->getSpecialByType($order_status).'_list';
}
/**
* Reset's any selected order state to pending
*
* @param unknown_type $event
*/
function OnResetToPending(&$event)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
if($items_info)
{
foreach($items_info as $id => $field_values)
{
$object->Load($id);
$object->SetDBField('Status', ORDER_STATUS_PENDING);
if( $object->Update() )
{
$event->status=kEvent::erSUCCESS;
}
else
{
$event->status=kEvent::erFAIL;
$event->redirect=false;
break;
}
}
}
}
/**
* Creates list from items selected in grid
*
* @param kEvent $event
*/
function OnLoadSelected(&$event)
{
$event->setPseudoClass('_List');
$object =& $event->getObject( Array('selected_only' => true) );
$event->redirect = false;
}
/**
* Return orders list, that will expire in time specified
*
* @param int $pre_expiration timestamp
* @return Array
*/
function getRecurringOrders($pre_expiration)
{
$ord_table = $this->Application->getUnitOption('ord', 'TableName');
$ord_idfield = $this->Application->getUnitOption('ord', 'IDField');
$processing_allowed = Array(ORDER_STATUS_PROCESSED, ORDER_STATUS_ARCHIVED);
$sql = 'SELECT '.$ord_idfield.', PortalUserId, GroupId, NextCharge
FROM '.$ord_table.'
WHERE (IsRecurringBilling = 1) AND (NextCharge < '.$pre_expiration.') AND Status IN ('.implode(',', $processing_allowed).')';
return $this->Conn->Query($sql, $ord_idfield);
}
/**
* Regular event: checks what orders should expire and renew automatically (if such flag set)
*
* @param kEvent $event
*/
function OnCheckRecurringOrders(&$event)
{
$skip_clause = Array();
$ord_table = $this->Application->getUnitOption($event->Prefix, 'TableName');
$ord_idfield = $this->Application->getUnitOption($event->Prefix, 'IDField');
$pre_expiration = adodb_mktime() + $this->Application->ConfigValue('Comm_RecurringChargeInverval') * 3600 * 24;
$to_charge = $this->getRecurringOrders($pre_expiration);
if ($to_charge) {
$order_ids = Array();
foreach ($to_charge as $order_id => $record) {
// skip virtual users (e.g. root, guest, etc.) & invalid subscriptions (with no group specified, no next charge, but Recurring flag set)
if (!$record['PortalUserId'] || !$record['GroupId'] || !$record['NextCharge']) continue;
$order_ids[] = $order_id;
// prevent duplicate user+group pairs
$skip_clause[ 'PortalUserId = '.$record['PortalUserId'].' AND GroupId = '.$record['GroupId'] ] = $order_id;
}
// process only valid orders
$temp_handler =& $this->Application->recallObject($event->Prefix.'_TempHandler', 'kTempTablesHandler');
$cloned_order_ids = $temp_handler->CloneItems($event->Prefix, 'recurring', $order_ids);
$order =& $this->Application->recallObject($event->Prefix.'.recurring', null, Array('skip_autoload' => true));
foreach ($cloned_order_ids as $order_id) {
$order->Load($order_id);
$this->Application->HandleEvent($complete_event, $event->Prefix.'.recurring:OnCompleteOrder' );
if ($complete_event->status == kEvent::erSUCCESS) {
//send recurring ok email
$email_event_user =& $this->Application->EmailEventUser('ORDER.RECURRING.PROCESSED', $order->GetDBField('PortalUserId'), $this->OrderEmailParams($order));
$email_event_admin =& $this->Application->EmailEventAdmin('ORDER.RECURRING.PROCESSED');
}
else {
//send Recurring failed event
$order->SetDBField('Status', ORDER_STATUS_DENIED);
$order->Update();
$email_event_user =& $this->Application->EmailEventUser('ORDER.RECURRING.DENIED', $order->GetDBField('PortalUserId'), $this->OrderEmailParams($order));
$email_event_admin =& $this->Application->EmailEventAdmin('ORDER.RECURRING.DENIED');
}
}
// remove recurring flag from all orders found, not to select them next time script runs
$sql = 'UPDATE '.$ord_table.'
SET IsRecurringBilling = 0
WHERE '.$ord_idfield.' IN ('.implode(',', array_keys($to_charge)).')';
$this->Conn->Query($sql);
}
if ( !is_object($event->MasterEvent) ) {
// not called as hook
return ;
}
$pre_expiration = adodb_mktime() + $this->Application->ConfigValue('User_MembershipExpirationReminder') * 3600 * 24;
$to_charge = $this->getRecurringOrders($pre_expiration);
foreach ($to_charge as $order_id => $record) {
// skip virtual users (e.g. root, guest, etc.) & invalid subscriptions (with no group specified, no next charge, but Recurring flag set)
if (!$record['PortalUserId'] || !$record['GroupId'] || !$record['NextCharge']) continue;
// prevent duplicate user+group pairs
$skip_clause[ 'PortalUserId = '.$record['PortalUserId'].' AND GroupId = '.$record['GroupId'] ] = $order_id;
}
$skip_clause = array_flip($skip_clause);
$event->MasterEvent->setEventParam('skip_clause', $skip_clause);
}
function OnGeneratePDF(&$event)
{
$this->OnLoadSelected($event);
$this->Application->InitParser();
$o = $this->Application->ParseBlock(array('name'=>'in-commerce/orders/orders_pdf'));
$file_helper =& $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
$file_helper->CheckFolder(EXPORT_PATH);
$htmlFile = EXPORT_PATH . '/tmp.html';
$fh = fopen($htmlFile, 'w');
fwrite($fh, $o);
fclose($fh);
// return;
// require_once (FULL_PATH.'html2pdf/PDFEncryptor.php');
// Full path to the file to be converted
// $htmlFile = dirname(__FILE__) . '/test.html';
// The default domain for images that use a relative path
// (you'll need to change the paths in the test.html page
// to an image on your server)
$defaultDomain = DOMAIN;
// Full path to the PDF we are creating
$pdfFile = EXPORT_PATH . '/tmp.pdf';
// Remove old one, just to make sure we are making it afresh
@unlink($pdfFile);
$pdf_helper =& $this->Application->recallObject('kPDFHelper');
$pdf_helper->FileToFile($htmlFile, $pdfFile);
return ;
// DOM PDF VERSION
/*require_once(FULL_PATH.'/dompdf/dompdf_config.inc.php');
$dompdf = new DOMPDF();
$dompdf->load_html_file($htmlFile);
if ( isset($base_path) ) {
$dompdf->set_base_path($base_path);
}
$dompdf->set_paper($paper, $orientation);
$dompdf->render();
file_put_contents($pdfFile, $dompdf->output());
return ;*/
// Instnatiate the class with our variables
require_once (FULL_PATH.'/html2pdf/HTML_ToPDF.php');
$pdf = new HTML_ToPDF($htmlFile, $defaultDomain, $pdfFile);
$pdf->setHtml2Ps('/usr/bin/html2ps');
$pdf->setPs2Pdf('/usr/bin/ps2pdf');
$pdf->setGetUrl('/usr/local/bin/curl -i');
// Set headers/footers
$pdf->setHeader('color', 'black');
$pdf->setFooter('left', '');
$pdf->setFooter('right', '$D');
$pdf->setDefaultPath(BASE_PATH.'/kernel/admin_templates/');
$result = $pdf->convert();
// Check if the result was an error
if (PEAR::isError($result)) {
$this->Application->ApplicationDie($result->getMessage());
}
else {
$download_url = rtrim($this->Application->BaseURL(), '/') . EXPORT_BASE_PATH . '/tmp.pdf';
echo "PDF file created successfully: $result";
echo '<br />Click <a href="' . $download_url . '">here</a> to view the PDF file.';
}
}
function OnAfterConfigRead(&$event)
{
parent::OnAfterConfigRead($event);
if (defined('IS_INSTALL') && IS_INSTALL) {
return ;
}
$order_number = (int)$this->Application->ConfigValue('Comm_Order_Number_Format_P');
$order_sub_number = (int)$this->Application->ConfigValue('Comm_Order_Number_Format_S');
$calc_fields = $this->Application->getUnitOption($event->Prefix, 'CalculatedFields');
foreach ($calc_fields as $special => $fields) {
$calc_fields[$special]['OrderNumber'] = str_replace('6', $order_number, $calc_fields[$special]['OrderNumber']);
$calc_fields[$special]['OrderNumber'] = str_replace('3', $order_sub_number, $calc_fields[$special]['OrderNumber']);
}
$this->Application->setUnitOption($event->Prefix, 'CalculatedFields', $calc_fields);
$fields = $this->Application->getUnitOption($event->Prefix, 'Fields');
$fields['Number']['format'] = str_replace('%06d', '%0'.$order_number.'d', $fields['Number']['format']);
$fields['SubNumber']['format'] = str_replace('%03d', '%0'.$order_sub_number.'d', $fields['SubNumber']['format']);
$site_helper =& $this->Application->recallObject('SiteHelper');
/* @var $site_helper SiteHelper */
$fields['BillingCountry']['default'] = $site_helper->getDefaultCountry('Billing');
$fields['ShippingCountry']['default'] = $site_helper->getDefaultCountry('Shipping');
if (!$this->Application->isAdminUser) {
$user_groups = explode(',', $this->Application->RecallVar('UserGroups'));
$default_group = $this->Application->ConfigValue('User_LoggedInGroup');
if (!in_array($default_group, $user_groups)){
$user_groups[] = $default_group;
}
$sql_part = '';
// limit payment types by domain
$payment_types = $this->Application->siteDomainField('PaymentTypes');
if (strlen($payment_types)) {
$payment_types = explode('|', substr($payment_types, 1, -1));
$sql_part .= ' AND PaymentTypeId IN (' . implode(',', $payment_types) . ')';
}
// limit payment types by user group
$sql_part .= ' AND (PortalGroups LIKE "%%,'.implode(',%%" OR PortalGroups LIKE "%%,', $user_groups).',%%")';
$fields['PaymentType']['options_sql'] = str_replace(
'ORDER BY ',
$sql_part . ' ORDER BY ',
$fields['PaymentType']['options_sql']
);
}
$this->Application->setUnitOption($event->Prefix, 'Fields', $fields);
}
/**
* Allows configuring export options
*
* @param kEvent $event
*/
function OnBeforeExportBegin(&$event)
{
$options = $event->getEventParam('options') ;
$items_list =& $this->Application->recallObject($event->Prefix.'.'.$this->Application->RecallVar('export_oroginal_special'), $event->Prefix.'_List');
$items_list->SetPerPage(-1);
if ($options['export_ids'] != '') {
$items_list->AddFilter('export_ids', $items_list->TableName.'.'.$items_list->IDField.' IN ('.implode(',',$options['export_ids']).')');
}
$options['ForceCountSQL'] = $items_list->getCountSQL( $items_list->GetSelectSQL(true,false) );
$options['ForceSelectSQL'] = $items_list->GetSelectSQL();
$event->setEventParam('options',$options);
$object =& $this->Application->recallObject($event->Prefix.'.export');
/* @var $object kDBItem */
$object->SetField('Number', 999999);
$object->SetField('SubNumber', 999);
}
/**
* Returns specific to each item type columns only
*
* @param kEvent $event
* @return Array
*/
function getCustomExportColumns(&$event)
{
$columns = parent::getCustomExportColumns($event);
$new_columns = Array(
'__VIRTUAL__CustomerName' => 'CustomerName',
'__VIRTUAL__TotalAmount' => 'TotalAmount',
'__VIRTUAL__AmountWithoutVAT' => 'AmountWithoutVAT',
'__VIRTUAL__SubtotalWithDiscount' => 'SubtotalWithDiscount',
'__VIRTUAL__SubtotalWithoutDiscount' => 'SubtotalWithoutDiscount',
'__VIRTUAL__OrderNumber' => 'OrderNumber',
);
return array_merge($columns, $new_columns);
}
/**
* Saves content of temp table into live and
* redirects to event' default redirect (normally grid template)
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSave(&$event)
{
parent::OnSave($event);
if ( $event->status != kEvent::erSUCCESS ) {
return ;
}
$copied_ids = unserialize($this->Application->RecallVar($event->Prefix . '_copied_ids' . $this->Application->GetVar('wid'), serialize(Array ())));
foreach ($copied_ids as $id) {
$an_event = new kEvent($this->Prefix . ':Dummy');
$this->Application->SetVar($this->Prefix . '_id', $id);
$this->Application->SetVar($this->Prefix . '_mode', ''); // this is to fool ReserveItems to use live table
$this->ReserveItems($an_event);
}
}
/**
* Occures before an item is copied to live table (after all foreign keys have been updated)
* Id of item being copied is passed as event' 'id' param
*
* @param kEvent $event
*/
function OnBeforeCopyToLive(&$event)
{
$id = $event->getEventParam('id');
$copied_ids = unserialize($this->Application->RecallVar($event->Prefix.'_copied_ids'.$this->Application->GetVar('wid'), serialize(array())));
array_push($copied_ids, $id);
$this->Application->StoreVar($event->Prefix.'_copied_ids'.$this->Application->GetVar('wid'), serialize($copied_ids) );
}
/**
* Checks, that currently loaded item is allowed for viewing (non permission-based)
*
* @param kEvent $event
* @return bool
*/
function checkItemStatus(&$event)
{
if ($this->Application->isAdminUser) {
return true;
}
$object =& $event->getObject();
if (!$object->isLoaded()) {
return true;
}
return $object->GetDBField('PortalUserId') == $this->Application->RecallVar('user_id');
}
// ===== Gift Certificates Related =====
+ /**
+ * Enter description here...
+ *
+ * @param kEvent $event
+ */
+ function OnApplyGiftCertificate(&$event)
+ {
+ $code = $this->Application->GetVar('giftcert_code');
+
+ if ( $code == '' ) {
+ return;
+ }
+
+ $object =& $event->getObject();
+ /* @var $object OrdersItem */
+
+ $gift_certificate =& $this->Application->recallObject('gc', null, Array ('skip_autoload' => true));
+ /* @var $gift_certificate kDBItem */
+
+ $gift_certificate->Load($code, 'Code');
+
+ if ( !$gift_certificate->isLoaded() ) {
+ $event->status = kEvent::erFAIL;
+ $object->setCheckoutError(OrderCheckoutErrorType::GIFT_CERTIFICATE, OrderCheckoutError::GC_CODE_INVALID);
+ $event->redirect = false; // check!!!
+
+ return;
+ }
+
+ $debit = $gift_certificate->GetDBField('Debit');
+ $expire_date = $gift_certificate->GetDBField('Expiration');
+
+ if ( $gift_certificate->GetDBField('Status') != 1 || ($expire_date && $expire_date < adodb_mktime()) || ($debit <= 0) ) {
+ $event->status = kEvent::erFAIL;
+ $object->setCheckoutError(OrderCheckoutErrorType::GIFT_CERTIFICATE, OrderCheckoutError::GC_CODE_EXPIRED);
+ $event->redirect = false;
+
+ return;
+ }
+
+ $object->SetDBField('GiftCertificateId', $gift_certificate->GetDBField('GiftCertificateId'));
+ $object->Update();
+
+ $object->setCheckoutError(OrderCheckoutErrorType::GIFT_CERTIFICATE, OrderCheckoutError::GC_APPLIED);
+ }
+
+ /**
+ * Removes gift certificate from order
+ *
+ * @param kEvent $event
+ * @deprecated
+ */
function OnRemoveGiftCertificate(&$event)
{
$object =& $event->getObject();
+ /* @var $object OrdersItem */
+
$this->RemoveGiftCertificate($object);
+ $object->setCheckoutError(OrderCheckoutErrorType::GIFT_CERTIFICATE, OrderCheckoutError::GC_REMOVED);
+
$event->CallSubEvent('OnRecalculateItems');
- $event->SetRedirectParam('checkout_error', 107);
}
function RemoveGiftCertificate(&$object)
{
$object->RemoveGiftCertificate();
}
function RecalculateGift(&$event)
{
$object =& $event->getObject();
/* @var $object OrdersItem */
if ($object->GetDBField('Status') > ORDER_STATUS_PENDING) {
return ;
}
$object->RecalculateGift($event);
}
function GetWholeOrderGiftCertificateDiscount($gift_certificate_id)
{
if (!$gift_certificate_id) {
return 0;
}
$sql = 'SELECT Debit
FROM '.TABLE_PREFIX.'GiftCertificates
WHERE GiftCertificateId = '.$gift_certificate_id;
return $this->Conn->GetOne($sql);
}
/**
* Downloads shipping tracking bar code, that was already generated by USPS service
*
* @param kEvent $event
*/
function OnDownloadLabel(&$event)
{
$event->status = kEvent::erSTOP;
ini_set('memory_limit', '300M');
ini_set('max_execution_time', '0');
$object =& $event->getObject();
+ /* @var $object kDBItem */
+
$file = $object->GetDBField('ShippingTracking') . '.pdf';
$full_path = USPS_LABEL_FOLDER . $file;
if ( !file_exists($full_path) || !is_file($full_path) ) {
return;
}
header('Content-type: ' . kUtil::mimeContentType($full_path));
header('Content-Disposition: attachment; filename="' . $file . '"');
readfile($full_path);
}
+
+ /**
+ * Enter description here...
+ *
+ * @param kEvent $event
+ */
+ function OnCombinedPlaceOrder(&$event)
+ {
+
+ $event->CallSubEvent('OnUpdate');
+ }
}
\ No newline at end of file
Index: branches/5.2.x/units/orders/orders_item.php
===================================================================
--- branches/5.2.x/units/orders/orders_item.php (revision 14640)
+++ branches/5.2.x/units/orders/orders_item.php (revision 14641)
@@ -1,281 +1,317 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class OrdersItem extends kDBItem
{
function SetFieldsFromHash($hash, $set_fields=null)
{
parent::SetFieldsFromHash($hash, $set_fields);
$options = $this->GetFieldOptions('PaymentCCExpDate');
if( $this->GetDirtyField($options['month_field']) || $this->GetDirtyField($options['year_field']) )
{
$this->SetDirtyField('PaymentCCExpDate', 0);
$this->SetField('PaymentCCExpDate', 0);
}
}
/**
* Returns gateway data based on payment type used in order
*
* @return Array
*/
function getGatewayData($pt_id = null)
{
// get Gateway fields
if (!isset($pt_id) || !$pt_id) {
$pt_id = $this->GetDBField('PaymentType');
}
$pt_table = $this->Application->getUnitOption('pt','TableName');
$sql = 'SELECT GatewayId FROM %s WHERE PaymentTypeId = %s';
$gw_id = $this->Conn->GetOne( sprintf($sql, $pt_table, $pt_id) );
$sql = 'SELECT * FROM %s WHERE GatewayId = %s';
$ret = $this->Conn->GetRow( sprintf($sql, TABLE_PREFIX.'Gateways', $gw_id) );
// get Gateway parameters based on payment type
$gwf_table = $this->Application->getUnitOption('gwf','TableName');
$gwfv_table = $this->Application->getUnitOption('gwfv','TableName');
$sql = 'SELECT gwfv.Value, gwf.SystemFieldName
FROM %s gwf
LEFT JOIN %s gwfv ON gwf.GWConfigFieldId = gwfv.GWConfigFieldId
WHERE gwfv.PaymentTypeId = %s AND gwf.GatewayId = %s';
$ret['gw_params'] = $this->Conn->GetCol( sprintf($sql, $gwf_table, $gwfv_table, $pt_id, $gw_id), 'SystemFieldName' );
$ret['gw_params']['gateway_id'] = $gw_id;
if ($this->GetDBField('IsRecurringBilling') && $this->Application->ConfigValue('Comm_AutoProcessRecurringOrders')) {
if (isset($ret['gw_params']['shipping_control'])) {
$ret['gw_params']['shipping_control'] = SHIPPING_CONTROL_DIRECT;
}
}
return $ret;
}
/**
* Checks if tangible items are present in order
*
* @return bool
*/
function HasTangibleItems()
{
$sql = 'SELECT COUNT(*)
FROM '.TABLE_PREFIX.'OrderItems orditems
LEFT JOIN '.TABLE_PREFIX.'Products p ON p.ProductId = orditems.ProductId
WHERE (orditems.OrderId = '.$this->GetID().') AND (p.Type = '.PRODUCT_TYPE_TANGIBLE.')';
return $this->Conn->GetOne($sql) ? true : false;
}
/**
* Calculates tax value of order items based on billing & shipping country specified
*
* @return double
*/
function getTaxPercent()
{
$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$shipping_country_id = $cs_helper->getCountryStateId($this->GetDBField('ShippingCountry'), DESTINATION_TYPE_COUNTRY);
$shipping_state_id = $cs_helper->getCountryStateId($this->GetDBField('ShippingState'), DESTINATION_TYPE_STATE);
$shipping_zip = (string) $this->GetDBField('ShippingZip');
$billing_country_id = $cs_helper->getCountryStateId($this->GetDBField('BillingCountry'), DESTINATION_TYPE_COUNTRY);
$billing_state_id = $cs_helper->getCountryStateId($this->GetDBField('BillingState'), DESTINATION_TYPE_STATE);
$billing_zip = (string) $this->GetDBField('BillingZip');
/*
$dest_ids = array_diff( array_unique( Array( $shipping_country_id, $shipping_state_id, $billing_country_id, $billing_state_id ) ), Array(0) );
$dest_values = array_diff( array_unique( Array( $this->Conn->qstr($shipping_zip), $this->Conn->qstr($billing_zip) ) ), Array('\'\'') );
*/
$tax = false;
$sql = 'SELECT tx.*
FROM '.$this->Application->getUnitOption('tax', 'TableName').' tx
LEFT JOIN '.$this->Application->getUnitOption('taxdst', 'TableName').' txd ON tx.TaxZoneId = txd.TaxZoneId
WHERE
( txd.StdDestId IN ('.$shipping_country_id.','.$shipping_state_id.')
AND
( (txd.DestValue = "" OR txd.DestValue IS NULL)
OR
txd.DestValue = '.$this->Conn->qstr($shipping_zip).'
)
)
OR
( txd.StdDestId IN ('.$billing_country_id.','.$billing_state_id.')
AND
( (txd.DestValue = "" OR txd.DestValue IS NULL)
OR
txd.DestValue = '.$this->Conn->qstr($billing_zip).'
)
)
ORDER BY tx.TaxValue DESC';
$tax = $this->Conn->GetRow($sql);
if ($tax == false) {
$tax['TaxValue'] = 0;
$tax['ApplyToShipping'] = 0;
$tax['ApplyToProcessing'] = 0;
}
return $tax;
}
function RecalculateTax()
{
$tax = $this->getTaxPercent();
$this->SetDBField( 'VATPercent', $tax['TaxValue'] );
$this->SetDBField( 'ShippingTaxable', $tax['ApplyToShipping']);
$this->SetDBField( 'ProcessingTaxable', $tax['ApplyToProcessing']);
$this->UpdateTotals();
$subtotal = $this->GetDBField('AmountWithoutVAT');
$query = 'SELECT SUM(Quantity * Price) FROM '.TABLE_PREFIX.'OrderItems AS oi
LEFT JOIN '.TABLE_PREFIX.'Products AS p
ON p.ProductId = oi.ProductId
WHERE p.Type = 6 AND oi.OrderId = '.$this->GetDBField('OrderId');
$tax_exempt = $this->Conn->GetOne($query);
if ($tax_exempt) $subtotal -= $tax_exempt;
$this->SetDBField( 'VAT', round($subtotal * $tax['TaxValue'] / 100, 2) );
$this->UpdateTotals();
}
function UpdateTotals()
{
$total = 0;
$total += $this->GetDBField('SubTotal');
if ($this->GetDBField('ShippingTaxable')) $total += $this->GetDBField('ShippingCost');
if ($this->GetDBField('ProcessingTaxable')) $total += $this->GetDBField('ProcessingFee');
$this->SetDBField('AmountWithoutVAT', $total);
$total += $this->GetDBField('VAT');
if (!$this->GetDBField('ShippingTaxable')) $total += $this->GetDBField('ShippingCost');
if (!$this->GetDBField('ProcessingTaxable')) $total += $this->GetDBField('ProcessingFee');
$total += $this->GetDBField('InsuranceFee');
$this->SetDBField('TotalAmount', $total);
}
function getTotalAmount()
{
return $this->GetDBField('SubTotal') +
$this->GetDBField('ShippingCost') +
$this->GetDBField('VAT') +
$this->GetDBField('ProcessingFee') +
$this->GetDBField('InsuranceFee') -
$this->GetDBField('GiftCertificateDiscount');
}
function requireCreditCard()
{
$sql = 'SELECT RequireCCFields
FROM ' . $this->Application->getUnitOption('pt', 'TableName') . ' pt
LEFT JOIN '.TABLE_PREFIX.'Gateways gw ON gw.GatewayId = pt.GatewayId
WHERE pt.PaymentTypeId = ' . $this->GetDBField('PaymentType');
return $this->Conn->GetOne($sql);
}
function getNextSubNumber()
{
$table = $this->Application->GetLiveName($this->TableName);
$sql = 'SELECT MAX(SubNumber) FROM '.$table.' WHERE Number = '.$this->GetDBField('Number');
return $this->Conn->GetOne($sql) + 1;
}
function ResetAddress($prefix)
{
$fields = Array('To','Company','Phone','Fax','Email','Address1','Address2','City','State','Zip','Country');
foreach($fields as $field)
{
$this->SetDBField($prefix.$field, $this->Fields[$prefix.$field]['default']);
}
}
function IsProfileAddress($address_type)
{
return $this->Application->GetVar($this->Prefix.'_IsProfileAddress');
}
// ===== Gift Certificates Related =====
function RecalculateGift(&$event)
{
$gc_id = $this->GetDBField('GiftCertificateId');
if ($gc_id < 1) {
return;
}
$gc =& $this->Application->recallObject('gc', null, Array('skip_autoload' => true));
/* @var $gc kDBItem */
$gc->Load($gc_id);
if ($gc->GetDBField('Status') == gcDISABLED) {
// disabled GC
$this->SetDBField('GiftCertificateId', 0);
$this->SetDBField('GiftCertificateDiscount', 0);
// disabled
return;
}
$debit = $gc->GetDBField('Debit') + $this->GetDBField('GiftCertificateDiscount');
$this->UpdateTotals();
$total = $this->GetDBField('TotalAmount');
$gift_certificate_discount = $debit >= $total ? $total : $debit;
$this->SetDBField('TotalAmount', $total - $gift_certificate_discount);
$this->GetDBField('GiftCertificateDiscount', $gift_certificate_discount);
$debit -= $gift_certificate_discount;
$gc->SetDBField('Debit', $debit);
$gc->SetDBField('Status', $debit > 0 ? gcENABLED : gcUSED);
$gc->Update();
if ($gift_certificate_discount == 0) {
$this->RemoveGiftCertificate($object);
- $event->SetRedirectParam('checkout_error', 108);
+ $this->setCheckoutError(OrderCheckoutErrorType::GIFT_CERTIFICATE, OrderCheckoutError::GC_REMOVED_AUTOMATICALLY);
}
+
$this->SetDBField('GiftCertificateDiscount', $gift_certificate_discount);
}
function RemoveGiftCertificate()
{
$gc_id = $this->GetDBField('GiftCertificateId');
$gc =& $this->Application->recallObject('gc', null, Array('skip_autoload' => true));
/* @var $gc kDBItem */
$gc->Load($gc_id);
$debit = $gc->GetDBField('Debit') + $this->GetDBField('GiftCertificateDiscount');
if ($gc->isLoaded() && ($debit > 0)) {
$gc->SetDBField('Debit', $debit);
$gc->SetDBField('Status', gcENABLED);
$gc->Update();
}
$this->SetDBField('GiftCertificateId', 0);
$this->SetDBField('GiftCertificateDiscount', 0);
}
+
+ /**
+ * Sets checkout error
+ *
+ * @param int $error_type = {product,coupon,gc}
+ * @param int $error_code
+ * @param int $product_id - {ProductId}:{OptionsSalt}:{BackOrderFlag}:{FieldName}
+ */
+ function setCheckoutError($error_type, $error_code, $product_id = null)
+ {
+ $errors = $this->Application->RecallVar('checkout_errors');
+ $errors = $errors ? unserialize($errors) : Array ();
+
+ if ( isset($product_id) ) {
+ $error_type .= ':' . $product_id;
+
+ // any error takes priority over FIELD_UPDATE_SUCCESS error
+ if ( isset($errors[$error_type]) && $error_code == OrderCheckoutError::FIELD_UPDATE_SUCCESS ) {
+ return ;
+ }
+ }
+
+ if ( is_numeric($error_code) ) {
+ $errors[$error_type] = $error_code;
+ }
+ else {
+ unset($errors[$error_type]);
+ }
+
+ if ( $this->Application->isDebugMode() ) {
+ $this->Application->Debugger->appendHTML('CO_ERROR: ' . $error_type . ' - ' . $error_code);
+ }
+
+ $this->Application->StoreVar('checkout_errors', serialize($errors));
+ }
}
\ No newline at end of file
Index: branches/5.2.x/units/orders/order_validator.php
===================================================================
--- branches/5.2.x/units/orders/order_validator.php (revision 14640)
+++ branches/5.2.x/units/orders/order_validator.php (revision 14641)
@@ -1,245 +1,245 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class OrderValidator extends kValidator {
public function __construct($application = null)
{
parent::__construct($application);
$this->ErrorMsgs['credit_card_validation_error'] = '!lu_cc_validation_error!';
$this->ErrorMsgs['credit_card_expired'] = '!lu_cc_expired!';
}
/**
* Return error message for field
*
* @param string $field
* @return string
* @access public
*/
public function GetErrorMsg($field, $force_escape = null)
{
if ( $field != 'OrderNumber' ) {
return parent::GetErrorMsg($field, $force_escape);
}
$number['error'] = parent::GetErrorMsg('Number', $force_escape);
$number['pseudo'] = $this->GetErrorPseudo('Number');
$subnumber['error'] = parent::GetErrorMsg('SubNumber', $force_escape);
$subnumber['pseudo'] = $this->GetErrorPseudo('SubNumber');
// if pseudo match & not empty -> return 1st
// if one of pseudos not empty -> return it
// if we got one pseudo "bad_type" and other pseudo "required", then return "bad_type" error message
if ( $number['pseudo'] && ($number['pseudo'] == $subnumber['pseudo']) ) {
return $number['error'];
}
if ( $number['pseudo'] && !$subnumber['pseudo'] ) {
return $number['error'];
}
if ( !$number['pseudo'] && $subnumber['pseudo'] ) {
return $subnumber['error'];
}
if ( $number['pseudo'] == 'bad_type' ) {
return $number['error'];
}
if ( $subnumber['pseudo'] == 'bad_type' ) {
return $subnumber['error'];
}
/*$msg = '[' . $number_error . '(' . $number_pseudo . ')] [' . $subnumber_error . '] (' . $subnumber_pseudo . ')';
return $msg;*/
}
/**
* Check field value by user-defined alghoritm
*
* @param string $field field name
* @param Array $params field options from config
* @return bool
*/
function CustomValidation($field, $params)
{
// TODO: move to OrdersEventHandler OR extend kValidator class
$res = true;
$res = $res && $this->ValidateCCNumber($field, $params);
$res = $res && $this->ValidateCCExpiration($field, $params);
return $res;
}
/**
* Check if field value is valid credit card number against credit card type specified
*
* @param string $field field name
* @param Array $params field options from config
* @return bool
* @access private
*/
function ValidateCCNumber($field, $params)
{
$value = $this->dataSource->GetDBField($field);
$cardtype_field = getArrayValue($params, 'cardtype_field');
if ( !$cardtype_field || !$value || !$this->dataSource->requireCreditCard() ) {
return true;
}
if ( $this->Application->ConfigValue('Comm_MaskProcessedCreditCards') ) {
$mask_found = strpos($value, str_repeat('X', 4)) !== false;
if ( $this->Application->isAdminUser && $mask_found ) {
// masked card numbers always appear valid in admin
return true;
}
}
if (defined('DEBUG_MODE') && kUtil::constOn('DBG_PAYMENT_GW')) {
$gw_data = $this->dataSource->getGatewayData();
$this->Application->registerClass( $gw_data['ClassName'], GW_CLASS_PATH.'/'.$gw_data['ClassFile'] );
$gateway_object =& $this->Application->recallObject( $gw_data['ClassName'] );
$test_numbers = $gateway_object->GetTestCCNumbers();
if ( in_array($value, $test_numbers) ) {
return true;
}
}
$error_field = isset($params['error_field']) ? $params['error_field'] : $field;
// '1' => 'Visa','2' => 'Mastercard', '3' => 'Amex', '4' => 'Discover', 5 => 'Diners Club', 6 => 'JBC'
// Innocent until proven guilty
$cc_valid = true;
- // Get rid of any non-digits
+ // Get rid of any non-digits
$value = preg_replace('/[^\d]/', '', $value);
- // Perform card-specific checks, if applicable
+ // Perform card-specific checks, if applicable
switch( $this->dataSource->GetDBField($cardtype_field) )
{
case 2: // MasterCard
$cc_valid = preg_match('/^5[1-5].{14}$/', $value);
break;
case 1: // Visa
$cc_valid = preg_match('/^4.{15}$|^4.{12}$/', $value);
break;
case 3: // American Express
$cc_valid = preg_match('/^3[47].{13}$/', $value);
break;
case 4: // Discover
$cc_valid = preg_match('/^6011.{12}$/', $value);
break;
case 5: // Diners Club
$cc_valid = preg_match('/^30[0-5].{11}$|^3[68].{12}$/', $value);
break;
case 6: // JBC
$cc_valid = preg_match('/^3.{15}$|^2131|1800.{11}$/', $value);
break;
default:
$this->SetError($error_field, 'credit_card_validation_error');
return false;
break;
}
// The Luhn formula works right to left, so reverse the number.
$value = strrev($value);
$total = 0;
for($x = 0; $x < strlen($value); $x++)
{
$digit = substr($value, $x, 1);
// If it's an odd digit, double it
if( $x / 2 != floor($x/2) )
{
$digit *= 2;
// If the result is two digits, add them
if( strlen($digit) == 2 )
{
$digit = substr($digit, 0, 1) + substr($digit, 1, 1);
}
}
// Add the current digit, doubled and added if applicable, to the Total
$total += $digit;
}
// If it passed (or bypassed) the card-specific check and the Total is
// evenly divisible by 10, it's cool!
if ($cc_valid && $total % 10 == 0) {
return true;
}
$this->SetError($error_field, 'credit_card_validation_error');
return false;
}
/**
* Check if field value is non-expired credit card expiration date
*
* @param string $field field name
* @param Array $params field options from config
* @return bool
* @access private
*/
function ValidateCCExpiration($field, $params)
{
$formatter = getArrayValue($params, 'formatter');
if ( ($formatter != 'kCCDateFormatter') || !$this->dataSource->requireCreditCard() ) {
return true;
}
if ( !$this->Application->isAdminUser ) {
// validate expiration date only for front
if ( preg_match('/([\d]{2})\/([\d]{2})/', $this->dataSource->GetDBField($field), $rets) ) {
$month = $rets[1];
$year = $rets[2];
$now_date = adodb_mktime(0, 0, 0, adodb_date('m'), adodb_date('d'), adodb_date('Y') );
$day_count = adodb_date('t', adodb_mktime(0, 0, 0, $month, 1, $year) );
$cc_date = adodb_mktime(23, 59, 59, $month, $day_count, $year);
if ($cc_date < $now_date) {
$error_field = isset($params['error_field']) ? $params['error_field'] : $field;
$this->SetError($error_field, 'credit_card_expired');
return false;
}
}
}
return true;
}
}
\ No newline at end of file
Index: branches/5.2.x/units/orders/orders_config.php
===================================================================
--- branches/5.2.x/units/orders/orders_config.php (revision 14640)
+++ branches/5.2.x/units/orders/orders_config.php (revision 14641)
@@ -1,554 +1,564 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
$config = Array (
'Prefix' => 'ord',
'ItemClass' => Array ('class' => 'OrdersItem', 'file' => 'orders_item.php', 'build_event' => 'OnItemBuild'),
'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'),
'EventHandlerClass' => Array ('class' => 'OrdersEventHandler', 'file' => 'orders_event_handler.php', 'build_event' => 'OnBuild'),
'TagProcessorClass' => Array ('class' => 'OrdersTagProcessor', 'file' => 'orders_tag_processor.php', 'build_event' => 'OnBuild'),
'ValidatorClass' => 'OrderValidator',
'AutoLoad' => true,
'RegisterClasses' => Array (
Array ('pseudo' => 'OrderCalculator', 'class' => 'OrderCalculator', 'file' => 'order_calculator.php', 'build_event' => ''),
Array ('pseudo' => 'OrderManager', 'class' => 'OrderManager', 'file' => 'order_manager.php', 'build_event' => ''),
Array ('pseudo' => 'OrderValidator', 'class' => 'OrderValidator', 'file' => 'order_validator.php', 'build_event' => '', 'require_classes' => 'kValidator'),
),
'Hooks' => Array (
Array (
'Mode' => hAFTER,
'Conditional' => false,
'HookToPrefix' => 'ord',
'HookToSpecial' => '',
'HookToEvent' => Array ( 'OnPreSave' ),
'DoPrefix' => '',
'DoSpecial' => '',
'DoEvent' => 'OnRecalculateItems',
),
- /* OnApplyCoupon is called as hook for OnUpdateCart/OnCheckout, which calls OnRecalcualate themself
- Array (
- 'Mode' => hAFTER,
+ Array(
+ 'Mode' => hBEFORE,
'Conditional' => false,
- 'HookToPrefix' => 'coup',
+ 'HookToPrefix' => '',
'HookToSpecial' => '',
- 'HookToEvent' => Array ( 'OnApplyCoupon' ),
+ 'HookToEvent' => Array( 'OnUpdateCart', 'OnUpdateCartJSON', 'OnCheckout' ),
'DoPrefix' => '',
'DoSpecial' => '',
- 'DoEvent' => 'OnRecalculateItems',
- ),*/
+ 'DoEvent' => 'OnApplyCoupon',
+ ),
+
+ Array (
+ 'Mode' => hBEFORE,
+ 'Conditional' => false,
+ 'HookToPrefix' => '',
+ 'HookToSpecial' => '',
+ 'HookToEvent' => Array( 'OnUpdateCart', 'OnUpdateCartJSON', 'OnCheckout' ),
+ 'DoPrefix' => '',
+ 'DoSpecial' => '',
+ 'DoEvent' => 'OnApplyGiftCertificate',
+ ),
Array (
'Mode' => hAFTER,
'Conditional' => false,
'HookToPrefix' => 'u',
'HookToSpecial' => '',
'HookToEvent' => Array ( 'OnCreate' ),
'DoPrefix' => '',
'DoSpecial' => '',
'DoEvent' => 'OnUserCreate',
),
Array (
'Mode' => hBEFORE,
'Conditional' => false,
'HookToPrefix' => 'u',
'HookToSpecial' => '',
'HookToEvent' => Array ('OnCheckExpiredMembership'),
'DoPrefix' => '',
'DoSpecial' => '',
'DoEvent' => 'OnCheckRecurringOrders',
),
Array (
'Mode' => hAFTER,
'Conditional' => false,
'HookToPrefix' => 'u',
'HookToSpecial' => '',
'HookToEvent' => Array ( 'OnAfterLogin' ),
'DoPrefix' => '',
'DoSpecial' => '',
'DoEvent' => 'OnUserLogin',
),
Array (
'Mode' => hBEFORE, // before because OnInpLogin is called after real in-portal login and uses data from hooks
'Conditional' => false,
'HookToPrefix' => 'u',
'HookToSpecial' => '',
'HookToEvent' => Array ( 'OnInpLogin' ),
'DoPrefix' => '',
'DoSpecial' => '',
'DoEvent' => 'OnUserLogin',
),
),
'AggregateTags' => Array (
Array (
'AggregateTo' => 'orditems',
'AggregatedTagName' => 'LinkRemoveFromCart',
'LocalTagName' => 'Orditems_LinkRemoveFromCart',
),
Array (
'AggregateTo' => 'orditems',
'AggregatedTagName' => 'ProductLink',
'LocalTagName' => 'Orderitems_ProductLink',
),
Array (
'AggregateTo' => 'orditems',
'AggregatedTagName' => 'ProductExists',
'LocalTagName' => 'Orderitems_ProductExists',
),
),
'QueryString' => Array (
1 => 'id',
2 => 'Page',
3 => 'PerPage',
4 => 'event',
5 => 'mode',
),
'IDField' => 'OrderId',
'StatusField' => Array ('Status'), // field, that is affected by Approve/Decline events
'ViewMenuPhrase' => 'la_title_Orders',
'CatalogTabIcon' => 'icon16_item.png',
'TitleField' => 'OrderNumber',
'TitlePresets' => Array (
'default' => Array (
'new_status_labels' => Array ('ord' => '!la_title_Adding_Order!'),
'edit_status_labels' => Array ('ord' => '!la_title_Editing_Order!'),
'new_titlefield' => Array ('ord' => '!la_title_New_Order!'),
),
'orders_incomplete' => Array ( 'prefixes' => Array ('ord.incomplete_List'),
'format' => "!la_title_IncompleteOrders!",
),
'orders_pending' => Array ( 'prefixes' => Array ('ord.pending_List'),
'format' => "!la_title_PendingOrders!",
),
'orders_backorders' => Array ( 'prefixes' => Array ('ord.backorders_List'),
'format' => "!la_title_BackOrders!",
),
'orders_toship' => Array ( 'prefixes' => Array ('ord.toship_List'),
'format' => "!la_title_OrdersToShip!",
),
'orders_processed' => Array ( 'prefixes' => Array ('ord.processed_List'),
'format' => "!la_title_OrdersProcessed!",
),
'orders_returns' => Array ( 'prefixes' => Array ('ord.returns_List'),
'format' => "!la_title_OrdersReturns!",
),
'orders_denied' => Array ( 'prefixes' => Array ('ord.denied_List'),
'format' => "!la_title_OrdersDenied!",
),
'orders_archived' => Array ( 'prefixes' => Array ('ord.archived_List'),
'format' => "!la_title_OrdersArchived!",
),
'orders_search' => Array ( 'prefixes' => Array ('ord.search_List'),
'format' => "!la_title_OrdersSearch!",
),
'orders_edit_general' => Array ('prefixes' => Array ('ord'), 'format' => "#ord_status# '#ord_titlefield#' - !la_title_General!"),
'orders_edit_billing' => Array ('prefixes' => Array ('ord'), 'format' => "#ord_status# '#ord_titlefield#' - !la_title_OrderBilling!"),
'orders_edit_shipping' => Array ('prefixes' => Array ('ord'), 'format' => "#ord_status# '#ord_titlefield#' - !la_title_OrderShipping!"),
'orders_edit_items' => Array ('prefixes' => Array ('ord', 'orditems_List'), 'format' => "#ord_status# '#ord_titlefield#' - !la_title_OrderItems!"),
'orders_edit_preview' => Array ('prefixes' => Array ('ord'), 'format' => "#ord_status# '#ord_titlefield#' - !la_title_OrderPreview!"),
'orders_gw_result' => Array ('prefixes' => Array ('ord'), 'format' => "!la_title_OrderGWResult!"),
'orders_export' => Array ('format' => '!la_title_OrdersExport!'),
'orders_product_edit' => Array ('format' => '!la_title_Editing_Order_Item!'),
),
'EditTabPresets' => Array (
'Default' => Array (
'general' => Array ('title' => 'la_tab_General', 't' => 'in-commerce/orders/orders_edit', 'priority' => 1),
'items' => Array ('title' => 'la_tab_Items', 't' => 'in-commerce/orders/orders_edit_items', 'priority' => 2),
'shipping' => Array ('title' => 'la_tab_Shipping', 't' => 'in-commerce/orders/orders_edit_shipping', 'priority' => 3),
'billing' => Array ('title' => 'la_tab_Billing', 't' => 'in-commerce/orders/orders_edit_billing', 'priority' => 4),
'preview' => Array ('title' => 'la_tab_Preview', 't' => 'in-commerce/orders/orders_edit_preview', 'priority' => 5),
),
),
'PermSection' => Array ('main' => 'in-commerce:orders'),
'Sections' => Array (
'in-commerce:orders' => Array (
'parent' => 'in-commerce',
'icon' => 'in-commerce:orders',
'label' => 'la_tab_Orders',
'url' => Array ('t' => 'in-commerce/orders/orders_pending_list', 'pass' => 'm'),
'permissions' => Array ('view', 'add', 'edit', 'delete', 'advanced:approve', 'advanced:deny', 'advanced:archive', 'advanced:place', 'advanced:process', 'advanced:ship', 'advanced:reset_to_pending'),
'priority' => 1,
'type' => stTREE,
),
),
'SectionAdjustments' => Array (
'in-portal:visits' => Array (
'url' => Array ('t' => 'in-commerce/visits/visits_list_incommerce', 'pass' => 'm'),
),
),
'StatisticsInfo' => Array (
'pending' => Array (
'icon' => 'core:icon16_item.png',
'label' => 'la_title_Orders',
'js_url' => "#url#",
'url' => Array ('t' => 'in-commerce/orders/orders_pending_list', 'pass' => 'm'),
'status' => ORDER_STATUS_PENDING,
),
),
'TableName' => TABLE_PREFIX . 'Orders',
'CalculatedFields' => Array (
'' => Array (
'CustomerName' => 'IF( ISNULL(u.Login), IF (%1$s.PortalUserId = ' . USER_ROOT . ', \'root\', IF (%1$s.PortalUserId = ' . USER_GUEST . ', \'Guest\', \'n/a\')), CONCAT(u.FirstName,\' \',u.LastName) )',
'Username' => 'IF( ISNULL(u.Login),\'root\',u.Login)',
'OrderNumber' => 'CONCAT(LPAD(Number,6,"0"),\'-\',LPAD(SubNumber,3,"0") )',
'SubtotalWithoutDiscount' => '(SubTotal + DiscountTotal)',
'SubtotalWithDiscount' => '(SubTotal)',
'AmountWithoutVAT' => '(SubTotal+IF(ShippingTaxable=1, ShippingCost, 0)+IF(ProcessingTaxable=1, ProcessingFee, 0))',
'TotalAmount' => 'SubTotal+ShippingCost+VAT+ProcessingFee+InsuranceFee-GiftCertificateDiscount',
'CouponCode' => 'pc.Code',
'CouponName' => 'pc.Name',
'AffiliateUser' => 'IF( LENGTH(au.Login),au.Login,\'!la_None!\')',
'AffiliatePortalUserId' => 'af.PortalUserId',
'GiftCertificateCode' => 'gc.Code',
'GiftCertificateRecipient' => 'gc.Recipient',
),
'myorders' => Array (
'OrderNumber' => 'CONCAT(LPAD(Number,6,"0"),\'-\',LPAD(SubNumber,3,"0") )',
'SubtotalWithoutDiscount' => '(SubTotal + DiscountTotal)',
'SubtotalWithDiscount' => '(SubTotal)',
'AmountWithoutVAT' => '(SubTotal+IF(ShippingTaxable=1, ShippingCost, 0)+IF(ProcessingTaxable=1, ProcessingFee, 0))',
'TotalAmount' => 'SubTotal+ShippingCost+VAT+ProcessingFee+InsuranceFee-GiftCertificateDiscount',
/*'ItemsCount' => 'COUNT(%1$s.OrderId)',*/
),
),
// %1$s - table name of object
// %2$s - calculated fields
'ListSQLs' => Array (
'' => ' SELECT %1$s.* %2$s
FROM %1$s
LEFT JOIN '.TABLE_PREFIX.'PortalUser u ON %1$s.PortalUserId = u.PortalUserId
LEFT JOIN '.TABLE_PREFIX.'ProductsCoupons pc ON %1$s.CouponId = pc.CouponId
LEFT JOIN '.TABLE_PREFIX.'GiftCertificates gc ON %1$s.GiftCertificateId = gc.GiftCertificateId
LEFT JOIN '.TABLE_PREFIX.'Affiliates af ON %1$s.AffiliateId = af.AffiliateId
LEFT JOIN '.TABLE_PREFIX.'PortalUser au ON af.PortalUserId = au.PortalUserId',
'myorders' => ' SELECT %1$s.* %2$s
FROM %1$s
LEFT JOIN '.TABLE_PREFIX.'PortalUser u ON %1$s.PortalUserId = u.PortalUserId',
// LEFT JOIN '.TABLE_PREFIX.'OrderItems ON %1$s.OrderId = '.TABLE_PREFIX.'OrderItems.OrderId',
),
'ItemSQLs' => Array (
'' => ' SELECT %1$s.* %2$s FROM %1$s
LEFT JOIN '.TABLE_PREFIX.'PortalUser u ON %1$s.PortalUserId = u.PortalUserId
LEFT JOIN '.TABLE_PREFIX.'ProductsCoupons pc ON %1$s.CouponId = pc.CouponId
LEFT JOIN '.TABLE_PREFIX.'GiftCertificates gc ON %1$s.GiftCertificateId = gc.GiftCertificateId
LEFT JOIN '.TABLE_PREFIX.'Affiliates af ON %1$s.AffiliateId = af.AffiliateId
LEFT JOIN '.TABLE_PREFIX.'PortalUser au ON af.PortalUserId = au.PortalUserId',
),
'SubItems' => Array ('orditems'),
'ListSortings' => Array (
'' => Array (
'Sorting' => Array ('OrderDate' => 'desc'),
)
),
'Fields' => Array (
'OrderId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0, 'filter_type' => 'equals'),
'Number' => Array ('type' => 'int', 'required' =>1, 'formatter' => 'kFormatter', 'unique' =>Array ('SubNumber'), 'format' => '%06d', 'max_value_inc'>999999, 'not_null' => 1, 'default' => 0),
'SubNumber' => Array ('type' => 'int', 'required' =>1, 'formatter' => 'kFormatter', 'unique' =>Array ('Number'), 'format' => '%03d', 'max_value_inc'>999, 'not_null' => 1, 'default' => 0),
'Status' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' =>Array (0=> 'la_Incomplete',1=> 'la_Pending',2=> 'la_BackOrders',3=> 'la_ToShip',4=> 'la_Processed',5=> 'la_Denied',6=> 'la_Archived'), 'use_phrases' =>1, 'not_null' => 1, 'default' => 0, 'filter_type' => 'equals'),
'OnHold' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter',
'options' => Array (0 => 'la_No', 1 => 'la_Yes'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 0,
),
'OrderDate' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'required' => 1, 'default' => '#NOW#'),
'PortalUserId' =>Array ('type' => 'int', 'formatter' => 'kLEFTFormatter', 'error_msgs' => Array ('invalid_option' => '!la_error_UserNotFound!'), 'options' =>Array (USER_ROOT => 'root', USER_GUEST => 'Guest'), 'left_sql' => 'SELECT %s FROM '.TABLE_PREFIX.'PortalUser WHERE `%s` = \'%s\'', 'left_key_field' => 'PortalUserId', 'left_title_field' => 'Login', 'required' =>1, 'not_null' =>1, 'default' =>-1),
'OrderIP' => Array ('type' => 'string', 'not_null' => 1, 'default' => '', 'filter_type' => 'like'),
'UserComment' => Array ('type' => 'string', 'formatter' => 'kFormatter', 'using_fck' => 1, 'default' => NULL),
'AdminComment' => Array ('type' => 'string', 'formatter' => 'kFormatter', 'using_fck' => 1, 'default' => NULL),
'BillingTo' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'BillingCompany' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'BillingPhone' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'BillingFax' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'BillingEmail' => Array (
'type' => 'string',
'formatter' => 'kFormatter',
'regexp' => '/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i',
'not_null' => 1, 'default' => '',
),
'BillingAddress1' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'BillingAddress2' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'BillingCity' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'BillingState' => Array (
'type' => 'string',
'formatter' => 'kOptionsFormatter',
'options' => Array (),
'option_key_field' => 'DestAbbr',
'option_title_field' => 'Translation',
'not_null' => 1, 'default' => '',
),
'BillingZip' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'BillingCountry' => Array(
'type' => 'string',
'formatter' => 'kOptionsFormatter',
'options_sql' => ' SELECT IF(l%2$s_Name = "", l%3$s_Name, l%2$s_Name) AS Name, IsoCode
FROM '.TABLE_PREFIX.'CountryStates
WHERE Type = ' . DESTINATION_TYPE_COUNTRY . '
ORDER BY Name',
'option_key_field' => 'IsoCode', 'option_title_field' => 'Name',
'not_null' => 1, 'default' => 'USA'
),
'VAT' => Array ('type' => 'float', 'formatter' => 'kFormatter', 'not_null' =>1, 'default' => '0', 'format' => '%01.2f'),
'VATPercent' => Array ('type' => 'float', 'formatter' => 'kFormatter', 'not_null' =>1, 'default' => '0', 'format' => '%01.3f'),
'PaymentType' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter',
'options_sql' => 'SELECT %s
FROM ' . TABLE_PREFIX . 'PaymentTypes
WHERE Status = 1
ORDER BY Priority DESC, Name ASC',
'option_key_field' => 'PaymentTypeId', 'option_title_field' => 'Description',
'not_null' => 1, 'default' => 0
),
'PaymentAccount' => Array ('type' => 'string', 'not_null' => 1, 'cardtype_field' => 'PaymentCardType', 'default' => '', 'filter_type' => 'like'),
'PaymentNameOnCard' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'PaymentCCExpDate' => Array ('type' => 'string', 'formatter' => 'kCCDateFormatter', 'month_field' => 'PaymentCCExpMonth', 'year_field' => 'PaymentCCExpYear', 'not_null' => 1, 'default' => ''),
'PaymentCardType' => Array ('type' => 'string', 'not_null' => 1, 'formatter' => 'kOptionsFormatter', 'options' => Array ('' => '', '1' => 'Visa', '2' => 'Mastercard', '3' => 'Amex', '4' => 'Discover', '5' => 'Diners Club', '6' => 'JBC'), 'default' => ''),
'PaymentExpires' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => '#NOW#'),
'ShippingTo' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'ShippingCompany' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'ShippingPhone' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'ShippingFax' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'ShippingEmail' => Array (
'type' => 'string', 'not_null' => 1, 'default' => '',
'formatter' => 'kOptionsFormatter',
'options' => Array (),
'option_key_field' => 'DestAbbr',
'option_title_field' => 'Translation',
),
'ShippingAddress1' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'ShippingAddress2' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'ShippingCity' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'ShippingState' => Array (
'type' => 'string', 'formatter' => 'kOptionsFormatter',
'options' => Array (),
'option_key_field' => 'DestAbbr', 'option_title_field' => 'Translation',
'not_null' => 1, 'default' => ''),
'ShippingZip' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'ShippingCountry' => Array(
'type' => 'string', 'formatter' => 'kOptionsFormatter',
'options_sql' => ' SELECT IF(l%2$s_Name = "", l%3$s_Name, l%2$s_Name) AS Name, IsoCode
FROM '.TABLE_PREFIX.'CountryStates
WHERE Type = ' . DESTINATION_TYPE_COUNTRY . '
ORDER BY Name',
'option_key_field' => 'IsoCode', 'option_title_field' => 'Name',
'not_null' => 1, 'default' => 'USA'
),
'ShippingType' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter',
'options_sql' => 'SELECT %s
FROM ' . TABLE_PREFIX . 'ShippingType
WHERE Status = 1',
'option_key_field' => 'ShippingID',
'option_title_field' => 'Name',
'not_null' => 1, 'default' => 0,
),
'ShippingCost' => Array ('type' => 'double', 'formatter' => 'kFormatter', 'format' => '%01.2f', 'not_null' => 1, 'default' => '0.00'),
'ShippingCustomerAccount' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'ShippingTracking' => Array ('type' => 'string', 'not_null' => 1, 'default' => ''),
'ShippingDate' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => null),
'SubTotal' => Array ('type' => 'float', 'formatter' => 'kFormatter', 'format' => '%01.2f', 'not_null' => 1, 'default' => '0.00'),
'ReturnTotal' => Array ('type' => 'float', 'formatter' => 'kFormatter', 'format' => '%01.2f', 'not_null' => 1, 'default' => '0.00'),
'CostTotal' => Array ('type' => 'float', 'formatter' => 'kFormatter', 'format' => '%01.2f', 'not_null' => 1, 'default' => '0.00'),
'OriginalAmount' => Array ('type' => 'float', 'formatter' => 'kFormatter', 'format' => '%01.2f', 'not_null' => 1, 'default' => '0.00'),
'ShippingOption' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter',
'options' => Array (
0 => 'la_ship_all_together', 1 => 'la_ship_backorder_separately', 2 => 'la_ship_backorders_upon_avail',
),
'use_phrases' => 1, 'not_null' => 1, 'default' => 0,
),
'ShippingGroupOption' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'use_phrases' => 1,
'options' => Array (0 => 'la_opt_AutoGroupShipments', 1 => 'la_opt_ManualGroupShipments'),
'not_null' => 1, 'default' => 0,
),
'GiftCertificateId' => Array ('type' => 'int', 'default' => null),
'GiftCertificateDiscount' => Array ('type' => 'float', 'formatter' => 'kFormatter', 'format' => '%01.2f', 'not_null' => 1, 'default' => '0.00',),
'ShippingInfo' => Array ('type' => 'string', 'default' => NULL),
'CouponId' => Array ('type' => 'int', 'default' => null),
'CouponDiscount' => Array ('type' => 'float', 'not_null' => 1, 'default' => '0.00', 'formatter' => 'kFormatter', 'format' => '%01.2f'),
'DiscountTotal' => Array ('type' => 'float', 'not_null' => 1, 'default' => '0.00', 'formatter' => 'kFormatter', 'format' => '%01.2f'),
'TransactionStatus' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter',
'options' => Array (0 => 'la_opt_Invalid', 1 => 'la_opt_Verified', 2 => 'la_opt_Penging'),
'use_phrases' =>1, 'not_null' => 1, 'default' => 2,
),
'GWResult1' => Array ('type' => 'string', 'formatter' => 'kSerializedFormatter', 'default' => NULL),
'GWResult2' => Array ('type' => 'string', 'formatter' => 'kSerializedFormatter', 'default' => NULL),
'AffiliateId' => Array ('type' => 'int', 'formatter' => 'kLEFTFormatter', 'error_msgs' => Array ('invalid_option' => '!la_error_UserNotFound!'), 'options' => Array (0 => 'lu_None'), 'left_sql' => 'SELECT %s FROM '.TABLE_PREFIX.'Affiliates af LEFT JOIN '.TABLE_PREFIX.'PortalUser pu ON pu.PortalUserId = af.PortalUserId WHERE `%s` = \'%s\'', 'left_key_field' => 'AffiliateId', 'left_title_field' => 'Login', 'not_null' =>1, 'default' =>0),
'VisitId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'AffiliateCommission' => Array ('type' => 'double', 'formatter' => 'kFormatter', 'format' => '%.02f', 'not_null' => 1, 'default' => '0.0000'),
'ProcessingFee' => Array ('type' => 'double', 'formatter' => 'kFormatter', 'format' => '%.02f', 'not_null' => '0', 'default' => '0.0000'),
'InsuranceFee' => Array ('type' => 'float', 'formatter' => 'kFormatter', 'format' => '%01.2f', 'not_null' => 1, 'default' => '0.00'),
'ShippingTaxable' => Array ('type' => 'int', 'not_null' => 0, 'default' => 0),
'ProcessingTaxable' => Array ('type' => 'int', 'not_null' => 0, 'default' => 0),
'IsRecurringBilling' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter',
'options' => Array (0 => 'la_No', 1 => 'la_Yes',), 'use_phrases' => 1,
'default' => 0, 'not_null' => 1,
),
'ChargeOnNextApprove' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter',
'options' => Array (0 => 'la_No', 1 => 'la_Yes',), 'use_phrases' => 1,
'default' => 0, 'not_null' => 1,
),
'NextCharge' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => null),
'GroupId' => Array ('type' => 'int', 'not_null' => 1, 'default' => 0),
'GoogleOrderNumber' => Array ('type' => 'string', 'default' => NULL), // MySQL BIGINT UNSIGNED = 8 Bytes, PHP int = 4 Bytes -> threat as string
),
'VirtualFields' => Array (
'CustomerName' => Array ('type' => 'string', 'default' => '', 'filter_type' => 'like'),
'TotalAmount' => Array ('type' => 'float', 'formatter' => 'kFormatter', 'format' => '%01.2f', 'default' => '0.00'),
'AmountWithoutVAT' => Array ('type' => 'float', 'formatter' => 'kFormatter', 'format' => '%01.2f', 'default' => '0.00'),
'SubtotalWithDiscount' => Array ('type' => 'float', 'formatter' => 'kFormatter', 'format' => '%01.2f', 'default' => '0.00'),
'SubtotalWithoutDiscount' => Array ('type' => 'float', 'formatter' => 'kFormatter', 'format' => '%01.2f', 'default' => '0.00'),
'OrderNumber' => Array ('type' => 'string', 'default' => '', 'filter_type' => 'like'),
'CouponCode' => Array ('type' => 'string', 'default' => ''),
'CouponName' => Array ('type' => 'string', 'default' => ''),
'GiftCertificateCode' => Array ('type' => 'string', 'default' => ''),
'GiftCertificateRecipient' => Array ('type' => 'string', 'default' => ''),
// for ResetToUser
'UserTo' => Array ('type' => 'string', 'default' => ''),
'UserCompany' => Array ('type' => 'string', 'default' => ''),
'UserPhone' => Array ('type' => 'string', 'default' => ''),
'UserFax' => Array ('type' => 'string', 'default' => ''),
'UserEmail' => Array ('type' => 'string', 'default' => ''),
'UserAddress1' => Array ('type' => 'string', 'default' => ''),
'UserAddress2' => Array ('type' => 'string', 'default' => ''),
'UserCity' => Array ('type' => 'string', 'default' => ''),
'UserState' => Array ('type' => 'string', 'default' => ''),
'UserZip' => Array ('type' => 'string', 'default' => ''),
'UserCountry' => Array ('type' => 'string', 'default' => ''),
// for Search
'Username' => Array ('type' => 'string', 'filter_type' => 'like', 'default' => ''),
'HasBackOrders' => Array ('type' => 'int', 'default' => 0),
'PaymentCVV2' => Array ('type' => 'string', 'default' => ''),
'AffiliateUser' => Array ('type' => 'string', 'filter_type' => 'like', 'default' => ''),
'AffiliatePortalUserId' => Array ('type' => 'int', 'default' => 0),
// export related fields: begin
'ExportFormat' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'CSV', /*2 => 'XML'*/), 'default' => 1),
'ExportFilename' => Array ('type' => 'string', 'default' => ''),
'FieldsSeparatedBy' => Array ('type' => 'string', 'default' => ', '),
'FieldsEnclosedBy' => Array ('type' => 'string', 'default' => '"'),
'LineEndings' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'Windows', 2 => 'UNIX'), 'default' => 1),
'LineEndingsInside' => Array ('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'CRLF', 2 => 'LF'), 'default' => 2),
'IncludeFieldTitles' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter',
'options' => Array (0 => 'la_No', 1 => 'la_Yes'),
'use_phrases' => 1, 'default' => 1,
),
'ExportColumns' => Array ('type' => 'string', 'formatter' => 'kOptionsFormatter', 'options' => Array (), 'default' => ''),
'AvailableColumns' => Array ('type' => 'string', 'formatter' => 'kOptionsFormatter', 'options' => Array (), 'default' => ''),
'ExportPresets' => Array ('type' => 'string', 'formatter' => 'kOptionsFormatter', 'options' => Array (), 'default' => ''),
'ExportSavePreset' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter',
'options' => Array (0 => 'la_No', 1 => 'la_Yes'),
'use_phrases' => 1, 'default' => 0,
),
'ExportPresetName' => Array ('type' => 'string', 'default' => ''),
// export related fields: end
),
'Grids' => Array (
'Default' => Array (
'Icons' => Array (
'default' => 'icon16_item.png',
1 => 'icon16_pending.png',
5 => 'icon16_disabled.png',
'module' => 'core',
),
'Fields' => Array (
'OrderId' => Array ('title' => 'column:la_fld_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 70, ),
'OrderNumber' => Array ( 'data_block' => 'grid_ordernumber_td', 'filter_block' => 'grid_like_filter', 'width' => 100, ),
'OrderDate' => Array ( 'title' => 'la_col_OrderDate', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_date_range_filter', 'width' => 140, ),
'CustomerName' => Array ( 'title' => 'la_col_CustomerName', 'data_block' => 'grid_userlink_td', 'user_field' => 'PortalUserId', 'filter_block' => 'grid_like_filter', 'width' => 140, ),
'PaymentType' => Array ( 'data_block' => 'grid_billinglink_td', 'filter_block' => 'grid_options_filter', 'width' => 140, ),
'TotalAmount' => Array ( 'data_block' => 'grid_previewlink_td', 'filter_block' => 'grid_range_filter', 'width' => 140, ),
'AffiliateUser' => Array ( 'data_block' => 'grid_userlink_td', 'user_field' => 'AffiliatePortalUserId', 'filter_block' => 'grid_like_filter', 'width' => 140, ),
'OnHold' => Array ('filter_block' => 'grid_options_filter', 'width' => 100, ),
),
),
'Search' => Array (
'Icons' => Array (
'default' => 'icon16_item.png',
1 => 'icon16_pending.png',
5 => 'icon16_disabled.png',
'module' => 'core',
),
'Fields' => Array (
'OrderId' => Array ('title' => 'column:la_fld_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 70, ),
'OrderNumber' => Array ('data_block' => 'grid_ordernumber_td', 'filter_block' => 'grid_like_filter', 'width' => 100, ),
'Status' => Array ('filter_block' => 'grid_options_filter', 'filter_block' => 'grid_options_filter', 'width' => 100, ),
'OrderDate' => Array ('title' => 'la_col_OrderDate', 'filter_block' => 'grid_date_range_filter', 'width' => 140, ),
'CustomerName' => Array ('title' => 'la_col_CustomerName', 'data_block' => 'grid_userlink_td', 'user_field' => 'PortalUserId', 'filter_block' => 'grid_like_filter'),
'PaymentType' => Array ('data_block' => 'grid_billinglink_td', 'filter_block' => 'grid_options_filter'),
'TotalAmount' => Array ('data_block' => 'grid_previewlink_td', 'filter_block' => 'grid_float_range_filter'),
'AffiliateUser' => Array ('data_block' => 'grid_userlink_td', 'user_field' => 'AffiliatePortalUserId', 'filter_block' => 'grid_user_like_filter'),
'OrderIP' => Array ('filter_block' => 'grid_like_filter'),
'Username' => Array ('filter_block' => 'grid_user_like_filter'),
'PaymentAccount' => Array ('title' => 'column:la_fld_CreditCardNumber', 'filter_block' => 'grid_like_filter'),
),
),
),
);
\ No newline at end of file
Index: branches/5.2.x/units/orders/order_calculator.php
===================================================================
--- branches/5.2.x/units/orders/order_calculator.php (revision 14640)
+++ branches/5.2.x/units/orders/order_calculator.php (revision 14641)
@@ -1,850 +1,860 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
/**
* Performs order price calculations
*
*/
class OrderCalculator extends kBase {
/**
* Order manager instance
*
* @var OrderManager
*/
protected $manager = null;
/**
* Items, associated with current order
*
* @var Array
*/
protected $items = Array ();
/**
* Creates new clean instance of calculator
*
*/
public function __construct()
{
parent::__construct();
$this->reset();
}
/**
* Sets order manager instance to calculator
*
* @param OrderManager $manager
*/
public function setManager(&$manager)
{
$this->manager =& $manager;
}
public function reset()
{
$this->items = Array ();
}
/**
* Returns order object used in order manager
*
* @return OrdersItem
*/
protected function &getOrder()
{
$order =& $this->manager->getOrder();
return $order;
}
/**
- * Sets error from last operation
+ * Sets checkout error
*
+ * @param int $error_type = {product,coupon,gc}
* @param int $error_code
+ * @param int $product_id - {ProductId}:{OptionsSalt}:{BackOrderFlag}:{FieldName}
+ * @return void
+ * @access protected
*/
- protected function setError($error_code)
+ protected function setError($error_type, $error_code, $product_id = null)
{
- $this->manager->setError($error_code);
+ $this->manager->setError($error_type, $error_code, $product_id);
}
/**
* Perform order calculations and prepares operations for order manager
*
*/
public function calculate()
{
$this->queryItems();
$this->groupItems();
$this->generateOperations();
$this->applyWholeOrderFlatDiscount();
}
/**
* Groups order items, when requested
*
* @return Array
*/
protected function groupItems()
{
$skipped_items = Array ();
foreach ($this->items as $item_id => $item_data) {
if ( in_array($item_id, $skipped_items) ) {
continue;
}
$group_items = $this->getItemsToGroupWith($item_id);
if (!$group_items) {
continue;
}
foreach ($group_items as $group_item_id) {
$this->items[$item_id]['Quantity'] += $this->items[$group_item_id]['Quantity'];
$this->items[$group_item_id]['Quantity'] = 0;
}
$items_skipped = array_merge($items_skipped, $group_items);
}
}
/**
* Returns order item ids, that can be grouped with given order item id
*
* @param int $target_item_id
* @return Array
* @see OrderCalculator::canBeGrouped
*/
protected function getItemsToGroupWith($target_item_id)
{
$ret = Array ();
foreach ($this->items as $item_id => $item_data) {
if ( $this->canBeGrouped($this->items[$item_id], $this->items[$target_item_id]) ) {
$ret[] = $item_id;
}
}
return array_diff($ret, Array ($target_item_id));
}
/**
* Checks if 2 given order items can be grouped together
*
* @param Array $src_item
* @param Array $dst_item
* @return bool
*/
public function canBeGrouped($src_item, $dst_item)
{
if ($dst_item['Type'] != PRODUCT_TYPE_TANGIBLE) {
return false;
}
return ($src_item['ProductId'] == $dst_item['ProductId']) && ($src_item['OptionsSalt'] == $dst_item['OptionsSalt']);
}
/**
* Retrieves order contents from database
*
*/
protected function queryItems()
{
$poc_table = $this->Application->getUnitOption('poc', 'TableName');
$query = ' SELECT oi.ProductId, oi.OptionsSalt, oi.ItemData, oi.Quantity,
IF(p.InventoryStatus = ' . ProductInventory::BY_OPTIONS . ', poc.QtyInStock, p.QtyInStock) AS QtyInStock,
p.QtyInStockMin, p.BackOrder, p.InventoryStatus,
p.Type, oi.OrderItemId
FROM ' . $this->getTable('orditems') . ' AS oi
LEFT JOIN ' . TABLE_PREFIX . 'Products AS p ON oi.ProductId = p.ProductId
LEFT JOIN ' . $poc_table . ' poc ON (poc.CombinationCRC = oi.OptionsSalt) AND (oi.ProductId = poc.ProductId)
WHERE oi.OrderId = ' . $this->getOrder()->GetID();
$this->items = $this->Conn->Query($query, 'OrderItemId');
}
/**
* Generates operations and returns true, when something was changed
*
* @return bool
*/
protected function generateOperations()
{
$this->manager->resetOperationTotals();
foreach ($this->items as $item) {
$this->ensureMinQty($item);
$to_order = $back_order = 0;
$available = $this->getAvailableQty($item);
if ( $this->allowBackordering($item) ) {
// split order into order & backorder
if ($item['BackOrder'] == ProductBackorder::ALWAYS) {
$to_order = $available = 0;
$back_order = $item['Quantity'];
}
elseif ($item['BackOrder'] == ProductBackorder::AUTO) {
$to_order = $available;
$back_order = $item['Quantity'] - $available;
}
$qty = $to_order + $back_order;
$price = $this->getPlainProductPrice($item, $qty);
$cost = $this->getProductCost($item, $qty);
$discount_info = $this->getDiscountInfo( $item['ProductId'], $price, $qty );
$this->manager->addOperation($item, 0, $to_order, $price, $cost, $discount_info);
$this->manager->addOperation($item, 1, $back_order, $price, $cost, $discount_info);
}
else {
// store as normal order (and remove backorder)
// we could get here with backorder=never then we should order only what's available
$to_order = min($item['Quantity'], $available);
$price = $this->getPlainProductPrice($item, $to_order);
$cost = $this->getProductCost($item, $to_order);
$discount_info = $this->getDiscountInfo( $item['ProductId'], $price, $to_order );
$this->manager->addOperation($item, 0, $to_order, $price, $cost, $discount_info, $item['OrderItemId']);
$this->manager->addOperation($item, 1, 0, $price, $cost, $discount_info); // remove backorder record
if ($to_order < $item['Quantity']) {
// ordered less, then requested -> inform user
- $this->setError($to_order > 0 ? 2 : 3);
+ if ( $to_order > 0 ) {
+ $this->setError(OrderCheckoutErrorType::PRODUCT, OrderCheckoutError::QTY_UNAVAILABLE, $item['ProductId'] . ':' . $item['OptionsSalt'] . ':0:Quantity');
+ }
+ else {
+ $this->setError(OrderCheckoutErrorType::PRODUCT, OrderCheckoutError::QTY_OUT_OF_STOCK, $item['ProductId'] . ':' . $item['OptionsSalt'] . ':0:Quantity');
+ }
}
}
}
}
/**
* Adds product to order (not to db)
*
* @param Array $item
* @param kCatDBItem $product
* @param int $qty
*/
public function addProduct($item, &$product, $qty)
{
$this->updateItemDataFromProduct($item, $product);
$price = $this->getPlainProductPrice($item, $qty);
$cost = $this->getProductCost($item, $qty);
$discount_info = $this->getDiscountInfo( $item['ProductId'], $price, $qty );
$this->manager->addOperation( $item, 0, $qty, $price, $cost, $discount_info, $item['OrderItemId'] );
}
/**
* Apply whole order flat discount after sub-total been calculated
*
*/
protected function applyWholeOrderFlatDiscount()
{
$sub_total_flat = $this->manager->getOperationTotal('SubTotalFlat');
$flat_discount = min( $sub_total_flat, $this->getWholeOrderPlainDiscount($global_discount_id) );
$coupon_flat_discount = min( $sub_total_flat, $this->getWholeOrderCouponDiscount() );
if ($coupon_flat_discount && $coupon_flat_discount > $flat_discount) {
$global_discount_type = 'coupon';
$flat_discount = $coupon_flat_discount;
$global_discount_id = $coupon_id;
}
else {
$global_discount_type = 'discount';
}
$sub_total = $this->manager->getOperationTotal('SubTotal');
if ($sub_total_flat - $sub_total < $flat_discount) {
// individual item discounts together are smaller when order flat discount
$this->manager->setOperationTotal('CouponDiscount', $flat_discount == $coupon_flat_discount ? $flat_discount : 0);
$this->manager->setOperationTotal('SubTotal', $sub_total_flat - $flat_discount);
// replace discount for each operation
foreach ($this->operations as $index => $operation) {
$discounted_price = ($operation['Price'] / $sub_total_flat) * $sub_total;
$this->operations[$index]['DiscountInfo'] = Array ($global_discount_id, $global_discount_type, $discounted_price, 0);
}
}
}
/**
* Returns discount information for given product price and qty
*
* @param int $product_id
* @param float $price
* @param int $qty
* @return Array
*/
protected function getDiscountInfo($product_id, $price, $qty)
{
$discounted_price = $this->getDiscountedProductPrice($product_id, $price, $discount_id);
$couponed_price = $this->getCouponDiscountedPrice($product_id, $price);
if ($couponed_price < $discounted_price) {
$discount_type = 'coupon';
$discount_id = $coupon_id;
$discounted_price = $couponed_price;
$coupon_discount = ($price - $couponed_price) * $qty;
}
else {
$coupon_discount = 0;
$discount_type = 'discount';
}
return Array ($discount_id, $discount_type, $discounted_price, $coupon_discount);
}
/**
* Returns product qty, available for ordering
*
* @param Array $item
* @return int
*/
protected function getAvailableQty($item)
{
if ( $item['InventoryStatus'] == ProductInventory::DISABLED ) {
// always available
return $item['Quantity'] * 2;
}
return max(0, $item['QtyInStock'] - $item['QtyInStockMin']);
}
/**
* Checks, that product in given order item can be backordered
*
* @param Array $item
* @return bool
*/
protected function allowBackordering($item)
{
if ($item['BackOrder'] == ProductBackorder::ALWAYS) {
return true;
}
$available = $this->getAvailableQty($item);
$backordering = $this->Application->ConfigValue('Comm_Enable_Backordering');
return $backordering && ($item['Quantity'] > $available) && ($item['BackOrder'] == ProductBackorder::AUTO);
}
/**
* Make sure, that user can't order less, then minimal required qty of product
*
* @param Array $item
*/
protected function ensureMinQty(&$item)
{
$sql = 'SELECT MIN(MinQty)
FROM ' . TABLE_PREFIX . 'ProductsPricing
WHERE ProductId = ' . $item['ProductId'];
$min_qty = max(1, $this->Conn->GetOne($sql));
$qty = $item['Quantity'];
if ($qty > 0 && $qty < $min_qty) {
// qty in cart increased to meat minimal qry requirements of given product
- $this->setError(6);
+ $this->setError(OrderCheckoutErrorType::PRODUCT, OrderCheckoutError::QTY_CHANGED_TO_MINIMAL, $item['ProductId'] . ':' . $item['OptionsSalt'] . ':0:Quantity');
+
$item['Quantity'] = $min_qty;
}
}
/**
* Return product price for given qty, taking no discounts into account
*
* @param Array $item
* @param int $qty
* @return float
*/
public function getPlainProductPrice($item, $qty)
{
$item_data = $this->getItemData($item);
if ( isset($item_data['ForcePrice']) ) {
return $item_data['ForcePrice'];
}
$pricing_id = $this->getPriceBracketByQty($item, $qty);
$sql = 'SELECT Price
FROM ' . TABLE_PREFIX . 'ProductsPricing
WHERE PriceId = ' . $pricing_id;
$price = (float)$this->Conn->GetOne($sql);
if ( isset($item_data['Options']) ) {
$price += $this->getOptionPriceAddition($price, $item_data);
$price = $this->getCombinationPriceOverride($price, $item_data);
}
return max($price, 0);
}
/**
* Return product cost for given qty, taking no discounts into account
*
* @param Array $item
* @param int $qty
* @return float
*/
public function getProductCost($item, $qty)
{
$pricing_id = $this->getPriceBracketByQty($item, $qty);
$sql = 'SELECT Cost
FROM ' . TABLE_PREFIX . 'ProductsPricing
WHERE PriceId = ' . $pricing_id;
return (float)$this->Conn->GetOne($sql);
}
/**
* Return product price for given qty, taking no discounts into account
*
* @param Array $item
* @param int $qty
* @return float
*/
protected function getPriceBracketByQty($item, $qty)
{
$orderby_clause = '';
$where_clause = Array ();
$product_id = $item['ProductId'];
if ( $this->usePriceBrackets($item) ) {
$user_id = $this->getOrder()->GetDBField('PortalUserId');
$where_clause = Array (
'GroupId IN (' . $this->Application->getUserGroups($user_id) . ')',
'pp.ProductId = ' . $product_id,
'pp.MinQty <= ' . $qty,
$qty . ' < pp.MaxQty OR pp.MaxQty = -1',
);
$orderby_clause = $this->getPriceBracketOrderClause($user_id);
}
else {
$item_data = $this->getItemData($item);
$where_clause = Array(
'pp.ProductId = ' . $product_id,
'pp.PriceId = ' . $this->getPriceBracketFromRequest($product_id, $item_data),
);
}
$sql = 'SELECT pp.PriceId
FROM ' . TABLE_PREFIX . 'ProductsPricing AS pp
LEFT JOIN ' . TABLE_PREFIX . 'Products AS p ON p.ProductId = pp.ProductId
WHERE (' . implode(') AND (', $where_clause) . ')';
if ($orderby_clause) {
$sql .= ' ORDER BY ' . $orderby_clause;
}
return (float)$this->Conn->GetOne($sql);
}
/**
* Checks if price brackets should be used in price calculations
*
* @param Array $item
* @return bool
*/
protected function usePriceBrackets($item)
{
return $item['Type'] == PRODUCT_TYPE_TANGIBLE;
}
/**
* Return product pricing id for given product.
* If not passed - return primary pricing ID
*
* @param int $product_id
* @return int
*/
public function getPriceBracketFromRequest($product_id, $item_data)
{
if ( !is_array($item_data) ) {
$item_data = unserialize($item_data);
}
// remembered pricing during checkout
if ( isset($item_data['PricingId']) && $item_data['PricingId'] ) {
return $item_data['PricingId'];
}
// selected pricing from product detail page
$price_id = $this->Application->GetVar('pr_id');
if ($price_id) {
return $price_id;
}
$sql = 'SELECT PriceId
FROM ' . TABLE_PREFIX . 'ProductsPricing
WHERE ProductId = ' . $product_id . ' AND IsPrimary = 1';
return $this->Conn->GetOne($sql);
}
/**
* Returns order clause for price bracket selection based on configration
*
* @param int $user_id
* @return string
*/
protected function getPriceBracketOrderClause($user_id)
{
if ($this->Application->ConfigValue('Comm_PriceBracketCalculation') == 1) {
// if we have to stick to primary group, then its pricing will go first,
// but if there is no pricing for primary group, then next optimal will be taken
$primary_group = $this->getUserPrimaryGroup($user_id);
return '( IF(GroupId = ' . $primary_group . ', 1, 2) ) ASC, pp.Price ASC';
}
return 'pp.Price ASC';
}
/**
* Returns addition to product price based on used product option
*
* @param float $price
* @param Array $item_data
* @return float
*/
protected function getOptionPriceAddition($price, $item_data)
{
$addtion = 0;
$opt_helper =& $this->Application->recallObject('kProductOptionsHelper');
/* @var $opt_helper kProductOptionsHelper */
foreach ($item_data['Options'] as $opt => $val) {
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'ProductOptions
WHERE ProductOptionId = ' . $opt;
$data = $this->Conn->GetRow($sql);
$parsed = $opt_helper->ExplodeOptionValues($data);
if (!$parsed) {
continue;
}
$conv_prices = $parsed['Prices'];
$conv_price_types = $parsed['PriceTypes'];
if ( is_array($val) ) {
foreach ($val as $a_val) {
$a_val = kUtil::unhtmlentities($a_val);
if ( isset($conv_prices[$a_val]) && $conv_prices[$a_val] ) {
if ( $conv_price_types[$a_val] == '$' ) {
$addtion += $conv_prices[$a_val];
}
elseif ($conv_price_types[$a_val] == '%') {
$addtion += $price * $conv_prices[$a_val] / 100;
}
}
}
}
else {
$a_val = kUtil::unhtmlentities($val);
if ( isset($conv_prices[$a_val]) && $conv_prices[$a_val] ) {
if ($conv_price_types[$a_val] == '$') {
$addtion += $conv_prices[$a_val];
}
elseif ($conv_price_types[$a_val] == '%') {
$addtion += $price * $conv_prices[$a_val] / 100;
}
}
}
}
return $addtion;
}
/**
* Returns product price after applying combination price override
*
* @param float $price
* @param Array $item_data
* @return float
*/
protected function getCombinationPriceOverride($price, $item_data)
{
$combination_salt = $this->generateOptionsSalt( $item_data['Options'] );
if (!$combination_salt) {
return $price;
}
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'ProductOptionCombinations
WHERE CombinationCRC = ' . $combination_salt;
$combination = $this->Conn->GetRow($sql);
if (!$combination) {
return $price;
}
switch ( $combination['PriceType'] ) {
case OptionCombinationPriceType::EQUALS:
return $combination['Price'];
break;
case OptionCombinationPriceType::FLAT:
return $price + $combination['Price'];
break;
case OptionCombinationPriceType::PECENT:
return $price * (1 + $combination['Price'] / 100);
break;
}
return $price;
}
/**
* Generates salt for given option set
*
* @param Array $options
* @return int
*/
public function generateOptionsSalt($options)
{
$opt_helper =& $this->Application->recallObject('kProductOptionsHelper');
/* @var $opt_helper kProductOptionsHelper */
return $opt_helper->OptionsSalt($options, true);
}
/**
* Return product price for given qty, taking possible discounts into account
*
* @param int $product_id
* @param int $price
* @param int $discount_id
* @return float
*/
public function getDiscountedProductPrice($product_id, $price, &$discount_id)
{
$discount_id = 0;
$user_id = $this->getOrder()->GetDBField('PortalUserId');
$join_clause = Array (
'd.DiscountId = di.DiscountId',
'di.ItemType = ' . DiscountItemType::PRODUCT . ' OR (di.ItemType = ' . DiscountItemType::WHOLE_ORDER . ' AND d.Type = ' . DiscountType::PERCENT . ')',
'd.Status = ' . STATUS_ACTIVE,
'd.GroupId IN (' . $this->Application->getUserGroups($user_id) . ')',
'd.Start IS NULL OR d.Start < ' . $this->getOrder()->GetDBField('OrderDate'),
'd.End IS NULL OR d.End > ' . $this->getOrder()->GetDBField('OrderDate'),
);
$sql = 'SELECT
CASE d.Type
WHEN ' . DiscountType::FLAT . ' THEN ' . $price . ' - d.Amount
WHEN ' . DiscountType::PERCENT . ' THEN ' . $price . ' * (1 - d.Amount / 100)
ELSE ' . $price . '
END, d.DiscountId
FROM ' . TABLE_PREFIX . 'Products AS p
LEFT JOIN ' . TABLE_PREFIX . 'ProductsDiscountItems AS di ON (di.ItemResourceId = p.ResourceId) OR (di.ItemType = ' . DiscountItemType::WHOLE_ORDER . ')
LEFT JOIN ' . TABLE_PREFIX . 'ProductsDiscounts AS d ON (' . implode(') AND (', $join_clause) . ')
WHERE (p.ProductId = ' . $product_id . ') AND (d.DiscountId IS NOT NULL)';
$pricing = $this->Conn->GetCol($sql, 'DiscountId');
if (!$pricing) {
return $price;
}
// get minimal price + discount
$discounted_price = min($pricing);
$pricing = array_flip($pricing);
$discount_id = $pricing[$discounted_price];
// optimal discount, but prevent negative price
return max( min($discounted_price, $price), 0 );
}
public function getWholeOrderPlainDiscount(&$discount_id)
{
$discount_id = 0;
$user_id = $this->getOrder()->GetDBField('PortalUserId');
$join_clause = Array (
'd.DiscountId = di.DiscountId',
'di.ItemType = ' . DiscountItemType::WHOLE_ORDER . ' AND d.Type = ' . DiscountType::FLAT,
'd.Status = ' . STATUS_ACTIVE,
'd.GroupId IN (' . $this->Application->getUserGroups($user_id) . ')',
'd.Start IS NULL OR d.Start < ' . $this->getOrder()->GetDBField('OrderDate'),
'd.End IS NULL OR d.End > ' . $this->getOrder()->GetDBField('OrderDate'),
);
$sql = 'SELECT d.Amount AS Discount, d.DiscountId
FROM ' . TABLE_PREFIX . 'ProductsDiscountItems AS di
LEFT JOIN ' . TABLE_PREFIX . 'ProductsDiscounts AS d ON (' . implode(') AND (', $join_clause) . ')
WHERE d.DiscountId IS NOT NULL';
$pricing = $this->Conn->GetCol($sql, 'DiscountId');
if (!$pricing) {
return 0;
}
$discounted_price = max($pricing);
$pricing = array_flip($pricing);
$discount_id = $pricing[$discounted_price];
return max($discounted_price, 0);
}
public function getCouponDiscountedPrice($product_id, $price)
{
if ( !$this->getCoupon() ) {
return $price;
}
$join_clause = Array (
'c.CouponId = ci.CouponId',
'ci.ItemType = ' . CouponItemType::PRODUCT . ' OR (ci.ItemType = ' . CouponItemType::WHOLE_ORDER . ' AND c.Type = ' . CouponType::PERCENT . ')',
);
$sql = 'SELECT
MIN(
CASE c.Type
WHEN ' . CouponType::FLAT . ' THEN ' . $price . ' - c.Amount
WHEN ' . CouponType::PERCENT . ' THEN ' . $price . ' * (1 - c.Amount / 100)
ELSE ' . $price . '
END
)
FROM ' . TABLE_PREFIX . 'Products AS p
LEFT JOIN ' . TABLE_PREFIX . 'ProductsCouponItems AS ci ON (ci.ItemResourceId = p.ResourceId) OR (ci.ItemType = ' . CouponItemType::WHOLE_ORDER . ')
LEFT JOIN ' . TABLE_PREFIX . 'ProductsCoupons AS c ON (' . implode(') AND (', $join_clause) . ')
WHERE p.ProductId = ' . $product_id . ' AND ci.CouponId = ' . $this->getCoupon() . '
GROUP BY p.ProductId';
$coupon_price = $this->Conn->GetOne($sql);
if ($coupon_price === false) {
return $price;
}
return max( min($price, $coupon_price), 0 );
}
public function getWholeOrderCouponDiscount()
{
if ( !$this->getCoupon() ) {
return 0;
}
$where_clause = Array (
'ci.CouponId = ' . $this->getCoupon(),
'ci.ItemType = ' . CouponItemType::WHOLE_ORDER,
'c.Type = ' . CouponType::FLAT,
);
$sql = 'SELECT Amount
FROM ' . TABLE_PREFIX . 'ProductsCouponItems AS ci
LEFT JOIN ' . TABLE_PREFIX . 'ProductsCoupons AS c ON c.CouponId = ci.CouponId
WHERE (' . implode(') AND (', $where_clause) . ')';
return $this->Conn->GetOne($sql);
}
protected function getCoupon()
{
return $this->getOrder()->GetDBField('CouponId');
}
/**
* Returns primary group of given user
*
* @param int $user_id
* @return int
*/
protected function getUserPrimaryGroup($user_id)
{
if ($user_id > 0) {
$sql = 'SELECT PrimaryGroupId
FROM ' . TABLE_PREFIX . 'PortalUser
WHERE PortalUserId = ' . $user_id;
return $this->Conn->GetOne($sql);
}
return $this->Application->ConfigValue('User_LoggedInGroup');
}
/**
* Returns ItemData associated with given order item
*
* @param Array $item
* @return Array
*/
protected function getItemData($item)
{
$item_data = $item['ItemData'];
if ( is_array($item_data) ) {
return $item_data;
}
return $item_data ? unserialize($item_data) : Array ();
}
/**
* Sets ItemData according to product
*
* @param Array $item
* @param kCatDBItem $product
*/
protected function updateItemDataFromProduct(&$item, &$product)
{
$item_data = $this->getItemData($item);
$item_data['IsRecurringBilling'] = $product->GetDBField('IsRecurringBilling');
// it item is processed in order using new style, then put such mark in orderitem record
$processing_data = $product->GetDBField('ProcessingData');
if ($processing_data) {
$processing_data = unserialize($processing_data);
if ( isset($processing_data['HasNewProcessing']) ) {
$item_data['HasNewProcessing'] = 1;
}
}
$item['ItemData'] = serialize($item_data);
}
/**
* Returns table name according to order temp mode
*
* @param string $prefix
* @return string
*/
protected function getTable($prefix)
{
return $this->manager->getTable($prefix);
}
}
\ No newline at end of file
Index: branches/5.2.x/units/orders/order_manager.php
===================================================================
--- branches/5.2.x/units/orders/order_manager.php (revision 14640)
+++ branches/5.2.x/units/orders/order_manager.php (revision 14641)
@@ -1,482 +1,485 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
/**
* Manages order contents
*
*/
class OrderManager extends kBase {
- /**
- * Error code from last operation
- *
- * @var int
- */
- protected $errorCode = 0;
-
protected $errorMessages = Array (
1 => 'state_changed',
2 => 'qty_unavailable',
3 => 'outofstock',
4 => 'invalid_code',
5 => 'code_expired',
6 => 'min_qty',
7 => 'code_removed',
8 => 'code_removed_automatically',
9 => 'changed_after_login',
10 => 'coupon_applied',
104 => 'invalid_gc_code',
105 => 'gc_code_expired',
107 => 'gc_code_removed',
108 => 'gc_code_removed_automatically',
110 => 'gift_certificate_applied',
);
/**
* Order, used in calculator
*
* @var OrdersItem
*/
protected $order = null;
/**
* Order calculator instance
*
* @var OrderCalculator
*/
protected $calculator = null;
/**
* Operations to be performed on order items later
*
* @var Array
*/
protected $operations = Array ();
/**
* Totals override
*
* @var Array
*/
protected $totalsOverride = Array ();
public function __construct()
{
parent::__construct();
$this->calculator =& $this->Application->makeClass('OrderCalculator');
$this->calculator->setManager($this);
$this->reset();
}
/**
* Sets order to be used in calculator
*
* @param OrdersItem $order
*/
public function setOrder(&$order)
{
$this->order =& $order;
$this->reset();
}
function reset()
{
- $this->errorCode = 0;
$this->operations = Array ();
$this->totalsOverride = Array ();
$this->calculator->reset();
}
public function resetOperationTotals()
{
$this->totalsOverride = Array ();
}
/**
- * Sets error from last operation
+ * Sets checkout error
*
+ * @param int $error_type = {product,coupon,gc}
* @param int $error_code
+ * @param int $product_id - {ProductId}:{OptionsSalt}:{BackOrderFlag}:{FieldName}
+ * @return void
+ * @access public
*/
- public function setError($error_code)
+ public function setError($error_type, $error_code, $product_id = null)
{
- $this->errorCode = $error_code;
+ $this->order->setCheckoutError($error_type, $error_code, $product_id);
}
/**
- * Sets error from last operation
+ * Gets error count
*
* @return int
+ * @access public
*/
- public function getError()
+ public function getErrorCount()
{
- return $this->errorCode;
+ $errors = $this->Application->RecallVar('checkout_errors');
+
+ if ( !$errors ) {
+ return 0;
+ }
+
+ return count( unserialize($errors) );
}
/**
* Returns order object reference
*
* @return OrdersItem
*/
public function &getOrder()
{
return $this->order;
}
/**
* Calculates given order
*
*/
public function calculate()
{
$this->calculator->calculate();
- $changed = $this->applyOperations() || ($this->getError() > 0);
+ $changed = $this->applyOperations() || ($this->getErrorCount() > 0);
$this->setOrderTotals();
return $changed;
}
public function addOperation($item, $backorder_flag, $qty, $price, $cost, $discount_info, $order_item_id = 0)
{
$operation = Array (
'ProductId' => $item['ProductId'],
'BackOrderFlag' => $backorder_flag,
'Quantity' => $qty,
'Price' => $price,
'Cost' => $cost,
'DiscountInfo' => $discount_info,
'OrderItemId' => $order_item_id,
'OptionsSalt' => $item['OptionsSalt'],
'ItemData' => $item['ItemData'],
'PackageNum' => array_key_exists('PackageNum', $item) ? $item['PackageNum'] : 1,
);
$this->operations[] = $operation;
}
/**
* Returns total based on added operations
*
* @param string $type
* @return float
*/
public function getOperationTotal($type)
{
if ( isset($this->totalsOverride[$type]) ) {
return $this->totalsOverride[$type];
}
$ret = 0;
foreach ($this->operations as $operation) {
if ($type == 'SubTotalFlat') {
$ret += $operation['Quantity'] * $operation['Price'];
}
elseif ($type == 'CostTotal') {
$ret += $operation['Quantity'] * $operation['Cost'];
}
elseif ($type == 'SubTotal') {
$ret += $operation['Quantity'] * $operation['DiscountInfo'][2]; // discounted price
}
elseif ($type == 'CouponDiscount') {
$ret += $operation['DiscountInfo'][3];
}
}
return $ret;
}
public function setOperationTotal($type, $value)
{
$this->totalsOverride[$type] = $value;
}
/**
* Apply scheduled operations
*
*/
public function applyOperations()
{
$ret = false;
$order_item =& $this->Application->recallObject('orditems.-item', null, Array('skip_autoload' => true));
/* @var $order_item kDBItem */
foreach ($this->operations as $operation) {
$item = $this->getOrderItemByOperation($operation);
$item_id = $item['OrderItemId'];
if ($item_id) { // if Product already exists in the order
if ( $this->noChangeRequired($item, $operation) ) {
continue;
}
$order_item->Load($item_id);
if ($operation['Quantity'] > 0) { // Update Price by _TOTAL_ qty
$item_data = $order_item->GetDBField('ItemData');
$item_data = $item_data ? unserialize($item_data) : Array ();
$item_data['DiscountId'] = $operation['DiscountInfo'][0];
$item_data['DiscountType'] = $operation['DiscountInfo'][1];
$fields_hash = Array (
'Quantity' => $operation['Quantity'],
'FlatPrice' => $operation['Price'],
'Price' => $operation['DiscountInfo'][2],
'Cost' => $operation['Cost'],
'ItemData' => serialize($item_data),
);
$order_item->SetDBFieldsFromHash($fields_hash);
$order_item->Update();
}
else { // delete products with 0 qty
$order_item->Delete();
}
}
elseif ($operation['Quantity'] > 0) {
// if we are adding product
// discounts are saved from OrdersEvetnHandler::AddItemToOrder method
$item_data = $operation['ItemData'];
$item_data = $item_data ? unserialize($item_data) : Array ();
$item_data['DiscountId'] = $operation['DiscountInfo'][0];
$item_data['DiscountType'] = $operation['DiscountInfo'][1];
$fields_hash = Array (
'ProductId' => $operation['ProductId'],
'ProductName' => $this->getProductField( $operation['ProductId'], 'Name' ),
'Quantity' => $operation['Quantity'],
'FlatPrice' => $operation['Price'],
'Price' => $operation['DiscountInfo'][2],
'Cost' => $operation['Cost'],
'Weight' => $this->getProductField( $operation['ProductId'], 'Weight' ),
'OrderId' => $this->order->GetID(),
'BackOrderFlag' => $operation['BackOrderFlag'],
'ItemData' => serialize($item_data),
'PackageNum' => $operation['PackageNum'],
);
$order_item->SetDBFieldsFromHash($fields_hash);
$order_item->Create();
}
else {
// item requiring to set qty to 0, meaning already does not exist
continue;
}
$ret = true;
}
return $ret;
}
/**
* Sets order fields, containing total values
*
*/
public function setOrderTotals()
{
$sub_total = $this->getOperationTotal('SubTotal');
$this->order->SetDBField('SubTotal', $sub_total);
$cost_total = $this->getOperationTotal('CostTotal');
$this->order->SetDBField('CostTotal', $cost_total);
$sub_total_flat = $this->getOperationTotal('SubTotalFlat');
$this->order->SetDBField('DiscountTotal', $sub_total_flat - $sub_total);
$coupon_discount = $this->getOperationTotal('CouponDiscount');
$this->order->SetDBField('CouponDiscount', $coupon_discount);
}
/**
* Returns exising order item data, based on operation details
*
* @param Array $operation
* @return Array
*/
protected function getOrderItemByOperation($operation)
{
if ( $operation['OrderItemId'] ) {
$where_clause = Array (
'OrderItemId = ' . $operation['OrderItemId'],
);
}
else {
$where_clause = Array (
'OrderId = ' . $this->order->GetID(),
'ProductId = ' . $operation['ProductId'],
'BackOrderFlag ' . ($operation['BackOrderFlag'] ? ' >= 1' : ' = 0'),
'OptionsSalt = ' . $operation['OptionsSalt'],
);
}
$sql = 'SELECT OrderItemId, Quantity, FlatPrice, Price, BackOrderFlag, ItemData
FROM ' . $this->getTable('orditems') . '
WHERE (' . implode(') AND (', $where_clause) . ')';
return $this->Conn->GetRow($sql);
}
/**
* Checks, that there are no database changes required to order item from operation
*
* @param Array $item
* @param Array $operation
* @return bool
*/
protected function noChangeRequired($item, $operation)
{
$item_data = $item['ItemData'] ? unserialize( $item['ItemData'] ) : Array ();
$conditions = Array (
$operation['Quantity'] > 0,
$item['Quantity'] == $operation['Quantity'],
round($item['FlatPrice'], 3) == round($operation['Price'], 3),
round($item['Price'], 3) == round($operation['DiscountInfo'][2], 3),
(string)getArrayValue($item_data, 'DiscountType') == $operation['DiscountInfo'][1],
(int)getArrayValue($item_data, 'DiscountId') == $operation['DiscountInfo'][0],
);
foreach ($conditions as $condition) {
if (!$condition) {
return false;
}
}
return true;
}
/**
* Returns product name by id
*
* @param int $product_id
* @param string $field
* @return string
*/
protected function getProductField($product_id, $field)
{
$product =& $this->Application->recallObject('p', null, Array ('skip_autoload' => true));
/* @var $product kCatDBItem */
if ( !$product->isLoaded() || ($product->GetID() != $product_id) ) {
$product->Load($product_id);
}
return $field == 'Name' ? $product->GetField($field) : $product->GetDBField($field);
}
/**
* Returns table name according to order temp mode
*
* @param string $prefix
* @return string
*/
public function getTable($prefix)
{
$table_name = $this->Application->getUnitOption($prefix, 'TableName');
if ( $this->order->IsTempTable() ) {
return $this->Application->GetTempName($table_name, 'prefix:' . $this->order->Prefix);
}
return $table_name;
}
/**
* Adds product to order
*
* @param kCatDBItem $product
* @param string $item_data
* @param int $qty
* @param int $package_num
*/
public function addProduct(&$product, $item_data, $qty = null, $package_num = null)
{
if ( !isset($qty) ) {
$qty = 1;
}
$item = $this->getItemFromProduct($product, $item_data);
$order_item = $this->getOrderItem($item);
if ( $this->calculator->canBeGrouped($item, $item) && $order_item ) {
$qty += $order_item['Quantity'];
}
$item['OrderItemId'] = $order_item ? $order_item['OrderItemId'] : 0;
if ( isset($package_num) ) {
$item['PackageNum'] = $package_num;
}
$this->calculator->addProduct($item, $product, $qty);
$this->applyOperations();
}
/**
* Returns virtual $item based on given product
*
* @param kCatDBItem $product
* @param string $item_data
* @return Array
*/
protected function getItemFromProduct(&$product, $item_data)
{
$item_data_array = unserialize($item_data);
$options = isset($item_data_array['Options']) ? $item_data_array['Options'] : false;
$options_salt = $options ? $this->calculator->generateOptionsSalt($options) : 0;
$item = Array (
'ProductId' => $product->GetID(),
'OptionsSalt' => $options_salt,
'ItemData' => $item_data,
'Type' => $product->GetDBField('Type'),
'OrderItemId' => 0,
);
return $item;
}
/**
* Returns OrderItem formed from $item
*
* @param Array $item
* @return Array
*/
protected function getOrderItem($item)
{
$where_clause = Array (
'OrderId = ' . $this->order->GetID(),
'ProductId = ' . $item['ProductId'],
);
if ( $item['OptionsSalt'] ) {
$where_clause[] = 'OptionsSalt = ' . $item['OptionsSalt'];
}
$sql = 'SELECT Quantity, OrderItemId
FROM ' . $this->getTable('orditems') . '
WHERE (' . implode(') AND (', $where_clause) . ')';
return $this->Conn->GetRow($sql);
}
}
\ No newline at end of file
Index: branches/5.2.x/units/orders/orders_tag_processor.php
===================================================================
--- branches/5.2.x/units/orders/orders_tag_processor.php (revision 14640)
+++ branches/5.2.x/units/orders/orders_tag_processor.php (revision 14641)
@@ -1,1498 +1,1612 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class OrdersTagProcessor extends kDBTagProcessor
{
/**
* Print location using only filled in fields
*
* @param Array $params
* @access public
*/
function PrintLocation($params)
{
$object =& $this->getObject($params);
$type = getArrayValue($params,'type');
if($type == 'Company')
{
return $this->PrintCompanyLocation($params);
}
$fields = Array('City','State','Zip','Country');
$ret = '';
foreach($fields as $field)
{
$value = $object->GetField($type.$field);
if ($field == 'Country' && $value) $ret .= '<br/>';
if($value) $ret .= $value.', ';
}
return rtrim($ret,', ');
}
function PrintCompanyLocation($params)
{
$ret = '';
$fields = Array ('City','State','ZIP','Country');
foreach ($fields as $field) {
$value = $this->Application->ConfigValue('Comm_'.$field);
if ($field == 'Country') {
$current_language = $this->Application->GetVar('m_lang');
$primary_language = $this->Application->GetDefaultLanguageId();
$sql = 'SELECT IF(l' . $current_language . '_Name = "", l' . $primary_language . '_Name, l' . $current_language . '_Name)
FROM ' . TABLE_PREFIX . 'CountryStates
WHERE IsoCode = ' . $this->Conn->qstr($value);
$value = $this->Conn->GetOne($sql);
}
if ($field == 'Country' && $value) {
$ret .= '<br/>';
}
if ($value) {
$ret .= $value.', ';
}
}
return rtrim($ret,', ');
}
function Orditems_LinkRemoveFromCart($params)
{
return $this->Application->HREF($this->Application->GetVar('t'), '', Array('pass' => 'm,orditems,ord', 'ord_event' => 'OnRemoveFromCart', 'm_cat_id'=>0));
}
function Orderitems_ProductLink($params)
{
$object =& $this->Application->recallObject('orditems');
$url_params = Array (
'p_id' => $object->GetDBField('ProductId'),
'pass' => 'm,p',
);
return $this->Application->HREF($params['template'], '', $url_params);
}
function Orderitems_ProductExists($params)
{
$object =& $this->Application->recallObject('orditems');
return $object->GetDBField('ProductId') > 0;
}
function PrintCart($params)
{
$o = '';
$params['render_as'] = $params['item_render_as'];
$tag_params = array_merge($params, Array ('per_page' => -1));
- $o_items = $this->Application->ProcessParsedTag(rtrim('orditems.'.$this->Special, '.'), 'PrintList', $tag_params);
+ $o_items = $this->Application->ProcessParsedTag(rtrim('orditems.' . $this->Special, '.'), 'PrintList', $tag_params);
+
+ if ( $o_items ) {
+ if ( isset($params['header_render_as']) ) {
+ $cart_params = array ('name' => $params['header_render_as']);
+ $o .= $this->Application->ParseBlock($cart_params);
+ }
- if ($o_items) {
- $cart_params = array('name' => $params['header_render_as']);
- $o = $this->Application->ParseBlock($cart_params);
$o .= $o_items;
- $cart_params = array('name' => $params['footer_render_as']);
- $o .= $this->Application->ParseBlock($cart_params);
- } else {
- $cart_params = array('name' => $params['empty_cart_render_as']);
+
+ if ( isset($params['footer_render_as']) ) {
+ $cart_params = array ('name' => $params['footer_render_as']);
+ $o .= $this->Application->ParseBlock($cart_params);
+ }
+ }
+ elseif ( isset($params['empty_cart_render_as']) ) {
+ $cart_params = array ('name' => $params['empty_cart_render_as']);
$o = $this->Application->ParseBlock($cart_params);
}
return $o;
}
function ShopCartForm($params)
{
return $this->Application->ProcessParsedTag('m', 'ParseBlock', array_merge($params, Array(
'name' => 'kernel_form', 'PrefixSpecial'=>'ord'
)) );
}
function BackOrderFlag($params)
{
$object =& $this->Application->recallObject('orditems');
return $object->GetDBField('BackOrderFlag');
}
function OrderIcon($params)
{
$object =& $this->Application->recallObject('orditems');
if ($object->GetDBField('BackOrderFlag') == 0) {
return $params['ordericon'];
} else {
return $params['backordericon'];
}
}
function Status($params)
{
$status_map = Array(
'incomplete' => ORDER_STATUS_INCOMPLETE,
'pending' => ORDER_STATUS_PENDING,
'backorder' => ORDER_STATUS_BACKORDERS,
'toship' => ORDER_STATUS_TOSHIP,
'processed' => ORDER_STATUS_PROCESSED,
'denied' => ORDER_STATUS_DENIED,
'archived' => ORDER_STATUS_ARCHIVED,
);
$object =& $this->getObject($params);
$status = $object->GetDBField('Status');
$result = true;
if (isset($params['is'])) {
$result = $result && ($status == $status_map[$params['is']]);
}
if (isset($params['is_not'])) {
$result = $result && ($status != $status_map[$params['is_not']]);
}
return $result;
}
function ItemsInCart($params)
{
$object =& $this->getObject($params);
if ($object->GetDBField('Status') != ORDER_STATUS_INCOMPLETE || $object->GetID() == FAKE_ORDER_ID) {
return 0;
}
$object =& $this->Application->recallObject('orditems', 'orditems_List');
/* @var $object kDBList */
return $object->GetRecordsCount();
}
function CartNotEmpty($params)
{
$object =& $this->getObject($params);
if ($object->GetDBField('Status') != ORDER_STATUS_INCOMPLETE || $object->GetID() == FAKE_ORDER_ID) {
return 0;
}
$order_id = $this->Application->RecallVar('ord_id');
if ($order_id) {
$sql = 'SELECT COUNT(*)
FROM ' . TABLE_PREFIX . 'OrderItems
WHERE OrderId = ' . $order_id;
return $this->Conn->GetOne($sql);
}
return 0;
}
function CartIsEmpty($params)
{
return $this->CartNotEmpty($params) ? false : true;
}
- function CartHasBackorders($params)
+ function CartHasBackorders($params = Array ())
{
$object =& $this->getObject($params);
$sql = 'SELECT COUNT(*)
FROM ' . TABLE_PREFIX . 'OrderItems
WHERE OrderId = ' . $object->GetID() . '
GROUP BY BackOrderFlag';
$different_types = $this->Conn->GetCol($sql);
return count($different_types) > 1;
}
function PrintShippings($params)
{
$o = '';
+ $limitations_cache = Array ();
+
$object =& $this->getObject($params);
- $ord_id = $object->GetId();
+ /* @var $object kDBItem */
- $shipping_option = $object->GetDBField('ShippingOption');
- $backorder_select = $shipping_option == 0 ? '0 As BackOrderFlag' : 'BackOrderFlag';
+ $ord_id = $object->GetID();
+ $oi_table = $this->Application->getUnitOption('orditems', 'TableName');
+
+ if ( $object->IsTempTable() ) {
+ $oi_table = $this->Application->GetTempName($oi_table, 'prefix' . $object->Prefix);
+ }
- $order_items =& $this->Application->recallObject('orditems', 'orditems_List', Array('skip_autoload' => true) );
- $oi_table = $order_items->TableName;
+ list ($split_shipments, $limit_types) = $this->GetShippingLimitations($ord_id);
+
+ foreach ($split_shipments as $group => $data) {
+ $sql = 'UPDATE ' . $oi_table . '
+ SET SplitShippingGroup = ' . $group . '
+ WHERE ProductId IN (' . implode(',', $data['Products']) . ')';
+ $this->Conn->Query($sql);
- list($split_shipments, $limit_types) = $this->GetShippingLimitations($ord_id);
- foreach ($split_shipments as $group => $data)
- {
- $query = 'UPDATE '.$oi_table.' SET SplitShippingGroup = '.$group.'
- WHERE ProductId IN ('.implode(',', $data['Products']).')';
- $this->Conn->Query($query);
$limitations_cache[$group] = $data['Types'];
}
+
$shipping_group_option = $object->GetDBField('ShippingGroupOption');
- $shipping_group_select = $shipping_group_option == 0 ? '0 AS SplitShippingGroup' : 'SplitShippingGroup';
- if (count($split_shipments) > 1) {
- $this->Application->SetVar('shipping_limitations_apply', 1);
+ $shipping_group_select = $shipping_group_option == ORDER_GROUP_SHIPPMENTS_AUTO ? '0' : 'oi.SplitShippingGroup';
+
+ if ( count($split_shipments) > 1 ) {
// different shipping limitations apply
- if ($limit_types == 'NONE') {
+ $this->Application->SetVar('shipping_limitations_apply', 1);
+
+ if ( $limit_types == 'NONE' ) {
// order can't be shipped with single shipping type
+ $shipping_group_option = ORDER_GROUP_SHIPPMENTS_MANUAL;
+ $shipping_group_select = 'oi.SplitShippingGroup';
$this->Application->SetVar('shipping_limitations_apply', 2);
- $shipping_group_select = 'SplitShippingGroup';
- $shipping_group_option = 1;
}
}
else {
$this->Application->SetVar('shipping_limitations_apply', 0);
}
+
+
+
+ $shipping_option = $object->GetDBField('ShippingOption');
+
$weight_sql = 'IF(oi.Weight IS NULL, 0, oi.Weight * oi.Quantity)';
- $query = 'SELECT
- '.$backorder_select.',
- oi.ProductName,
- oi.ShippingTypeId,
- SUM(oi.Quantity) AS TotalItems,
- SUM('.$weight_sql.') AS TotalWeight,
- SUM(oi.Price * oi.Quantity) AS TotalAmount,'.
- // calculate free Totals => SUM(ALL) - SUM(PROMO) '
- 'SUM(oi.Quantity) - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, oi.Quantity, 0)) AS TotalItemsPromo,
- SUM('.$weight_sql.') - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, '.$weight_sql.', 0)) AS TotalWeightPromo,
- SUM(oi.Price * oi.Quantity) - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, oi.Price * oi.Quantity, 0)) AS TotalAmountPromo,
- '.$shipping_group_select.'
- FROM '.$oi_table.' oi
- LEFT JOIN '.$this->Application->getUnitOption('p', 'TableName').' p
- ON oi.ProductId = p.ProductId
- WHERE oi.OrderId = '.$ord_id.' AND p.Type = 1
- GROUP BY BackOrderFlag, SplitShippingGroup
- ORDER BY BackOrderFlag ASC, SplitShippingGroup ASC';
- $shipments = $this->Conn->Query($query);
+ $sql = 'SELECT
+ ' . ($shipping_option == ORDER_SHIP_ALL_TOGETHER ? '0' : 'oi.BackOrderFlag') . ' AS BackOrderFlagCalc,
+ oi.ProductName,
+ oi.ShippingTypeId,
+
+ SUM(oi.Quantity) AS TotalItems,
+ SUM(' . $weight_sql . ') AS TotalWeight,
+ SUM(oi.Price * oi.Quantity) AS TotalAmount,
+
+ SUM(oi.Quantity) - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, oi.Quantity, 0)) AS TotalItemsPromo,
+ SUM(' . $weight_sql . ') - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, ' . $weight_sql . ', 0)) AS TotalWeightPromo,
+ SUM(oi.Price * oi.Quantity) - SUM(IF(p.MinQtyFreePromoShipping > 0 AND p.MinQtyFreePromoShipping <= oi.Quantity, oi.Price * oi.Quantity, 0)) AS TotalAmountPromo,
+
+ ' . $shipping_group_select . ' AS SplitShippingGroupCalc
+ FROM ' . $oi_table . ' oi
+ LEFT JOIN ' . TABLE_PREFIX . 'Products p ON oi.ProductId = p.ProductId
+ WHERE oi.OrderId = ' . $ord_id . ' AND p.Type = ' . PRODUCT_TYPE_TANGIBLE . '
+ GROUP BY BackOrderFlagCalc, SplitShippingGroupCalc
+ ORDER BY BackOrderFlagCalc ASC, SplitShippingGroupCalc ASC';
+ $shipments = $this->Conn->Query($sql);
+
+
+
+
- $block_params = Array();
+
+ $block_params = Array ();
$block_params['name'] = $this->SelectParam($params, 'render_as,block');
$block_params['user_country_id'] = $object->GetDBField('ShippingCountry');
$block_params['user_state_id'] = $object->GetDBField('ShippingState');
$block_params['user_zip'] = $object->GetDBField('ShippingZip');
$block_params['user_city'] = $object->GetDBField('ShippingCity');
$block_params['user_addr1'] = $object->GetDBField('ShippingAddress1');
$block_params['user_addr2'] = $object->GetDBField('ShippingAddress2');
$block_params['user_name'] = $object->GetDBField('ShippingTo');
- if( ($block_params['user_addr1'] == '' || $block_params['user_city'] == '' ||
- $block_params['user_zip'] == '' || $block_params['user_country_id'] == '') &&
- getArrayValue($params, 'invalid_address_render_as'))
- {
- $block_params['name'] = $params['invalid_address_render_as'];
- return $this->Application->ParseBlock($block_params);
- }
-
$group = 1;
foreach ($shipments as $shipment) {
- $where = array('OrderId = '.$ord_id);
- if ($shipping_group_option != 0) {
- $where[] = 'SplitShippingGroup = '.$shipment['SplitShippingGroup'];
+ $where = Array ('OrderId = ' . $ord_id);
+
+ if ( $shipping_group_option == ORDER_GROUP_SHIPPMENTS_MANUAL ) {
+ $where[] = 'SplitShippingGroup = ' . $shipment['SplitShippingGroupCalc'];
}
- if ($shipping_option > 0) { // not all together
- $where[] = 'BackOrderFlag = '.$shipment['BackOrderFlag'];
+
+ if ( $shipping_option != ORDER_SHIP_ALL_TOGETHER ) {
+ $where[] = 'BackOrderFlag = ' . $shipment['BackOrderFlagCalc'];
}
- $query = 'UPDATE '.$oi_table.' SET PackageNum = '.$group.'
- '.($where ? 'WHERE '.implode(' AND ', $where) : '');
- $this->Conn->Query($query);
+ $sql = 'UPDATE ' . $oi_table . '
+ SET PackageNum = ' . $group . '
+ WHERE ' . implode(' AND ', $where);
+ $this->Conn->Query($sql);
+
$group++;
}
- $this->Application->RemoveVar('LastShippings');
+
$group = 1;
+ $this->Application->RemoveVar('LastShippings');
$this->Application->SetVar('ShipmentsExists', 1);
- foreach ($shipments as $shipment) {
-
-
- $block_params['package_num'] = $group;
- $block_params['limit_types'] = strpos($shipping_group_select, '0 AS') !== false ? $limit_types : $limitations_cache[$shipment['SplitShippingGroup']];
+ foreach ($shipments as $shipment) {
+ $block_params['package_num'] = $group;
+ $block_params['limit_types'] = $shipping_group_option == ORDER_GROUP_SHIPPMENTS_AUTO ? $limit_types : $limitations_cache[ $shipment['SplitShippingGroupCalc'] ];
- $this->Application->SetVar('ItemShipmentsExists', 1);
+ $this->Application->SetVar('ItemShipmentsExists', 1); // also set from Order_PrintShippingTypes tag
- switch ($shipment['BackOrderFlag']) {
+ switch ( $shipment['BackOrderFlagCalc'] ) {
case 0:
- if ( $this->CartHasBackOrders(Array()) && $shipping_option == 0 ) {
+ if ( $this->CartHasBackOrders() && $shipping_option == ORDER_SHIP_ALL_TOGETHER ) {
$block_params['shipment'] = $this->Application->Phrase('lu_all_available_backordered');
}
else {
$block_params['shipment'] = $this->Application->Phrase('lu_ship_all_available');;
}
break;
+
case 1:
$block_params['shipment'] = $this->Application->Phrase('lu_ship_all_backordered');;
break;
+
default:
$block_params['shipment'] = $this->Application->Phrase('lu_ship_backordered');
break;
}
$block_params['promo_weight_metric'] = $shipment['TotalWeightPromo'];
$block_params['promo_amount'] = $shipment['TotalAmountPromo'];
$block_params['promo_items'] = $shipment['TotalItemsPromo'];
$block_params['weight_metric'] = $shipment['TotalWeight'];
$block_params['weight'] = $shipment['TotalWeight'];
- $regional =& $this->Application->recallObject('lang.current');
- if ($block_params['weight_metric'] == '')
- {
+ if ( $block_params['weight_metric'] == '' ) {
$block_params['weight'] = $this->Application->Phrase('lu_NotAvailable');
}
- elseif ($regional->GetDBField('UnitSystem') == 1)
- {
- $block_params['weight'] .= ' '.$this->Application->Phrase('lu_kg');
- }
- elseif ($regional->GetDBField('UnitSystem') == 2)
- {
- list($pounds, $ounces) = kUtil::Kg2Pounds($block_params['weight']);
- $block_params['weight'] = $pounds.' '.$this->Application->Phrase('lu_pounds').' '.
- $ounces.' '.$this->Application->Phrase('lu_ounces');
+ else {
+ $block_params['weight'] = $this->_formatWeight( $block_params['weight'] );
}
+
$block_params['items'] = $shipment['TotalItems'];
- $iso = $this->GetISO($params['currency']);
- $amount = $this->ConvertCurrency($shipment['TotalAmount'], $iso);
+ $amount = $this->ConvertCurrency($shipment['TotalAmount'], $this->GetISO( $params['currency'] ));
$amount = sprintf("%.2f", $amount);
-// $block_params['amount'] = $this->AddCurrencySymbol($amount, $iso);
$block_params['amount'] = $shipment['TotalAmount'];
- $block_params['field_name'] = $this->InputName(Array('field' => 'ShippingTypeId')).'['.($group).']';
+ $block_params['field_name'] = $this->InputName( Array('field' => 'ShippingTypeId') ) . '[' . $group . ']';
$parsed_block = $this->Application->ParseBlock($block_params);
- if($this->Application->GetVar('ItemShipmentsExists'))
- {
+ if ( $this->Application->GetVar('ItemShipmentsExists') ) {
$o .= $parsed_block;
}
- else
- {
+ else {
$this->Application->SetVar('ShipmentsExists', 0);
- if(getArrayValue($params, 'no_shipments_render_as'))
- {
+
+ if ( getArrayValue($params, 'no_shipments_render_as') ) {
$block_params['name'] = $params['no_shipments_render_as'];
+
return $this->Application->ParseBlock($block_params);
}
}
- $group++;
- }
- if(getArrayValue($params, 'table_header_render_as'))
- {
- $o = $this->Application->ParseBlock( Array('name' => $params['table_header_render_as']) ).$o;
- }
- if(getArrayValue($params, 'table_footer_render_as'))
- {
- $o .= $this->Application->ParseBlock( Array('name' => $params['table_footer_render_as']) );
+ $group++;
}
return $o;
}
- function GetShippingLimitations($ord_id)
+ /**
+ * Checks, that all given address fields are valid
+ *
+ * @param Array $params
+ * @return bool
+ */
+ function AddressValid($params)
{
- /*$query = 'SELECT
- c.CachedShippingLimitation
- FROM '.TABLE_PREFIX.'OrderItems AS oi
- LEFT JOIN '.TABLE_PREFIX.'Products AS p
- ON p.ProductId = oi.ProductId
- LEFT JOIN '.TABLE_PREFIX.'CategoryItems AS ci
- ON ci.ItemResourceId = p.ResourceId
- LEFT JOIN '.TABLE_PREFIX.'Category AS c
- ON c.CategoryId = ci.CategoryId
- WHERE
- oi.OrderId = '.$ord_id.'
- AND
- ci.PrimaryCat = 1
- AND
- c.CachedShippingMode = 1;';
- $cat_limitations = $this->Conn->GetCol($query);*/
- $cat_limitations = array();
-
- $query = 'SELECT ShippingLimitation, ShippingMode, oi.ProductId as ProductId
- FROM '.TABLE_PREFIX.'Products AS p
- LEFT JOIN '.TABLE_PREFIX.'OrderItems AS oi ON
- oi.ProductId = p.ProductId
- WHERE oi.OrderId = '.$ord_id.' AND p.Type = 1'; // .' AND p.ShippingMode = 1';
- $limitations = $this->Conn->Query($query, 'ProductId');
+ $object =& $this->getObject($params);
+ /* @var $object kDBItem */
- $split_shipments = array();
+ $address_type = isset($params['type']) ? strtolower($params['type']) : 'shipping';
+ $address_type = ucfirst($address_type);
+ $check_fields = Array ('Address1', 'City', 'Zip', 'Country');
+
+ foreach ($check_fields as $check_field) {
+ if ( $object->GetDBField($address_type . $check_field) == '' ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ function GetShippingLimitations($ord_id)
+ {
$limit = false;
+ $types_index = $split_shipments = $cat_limitations = Array ();
+
+ $sql = 'SELECT p.ShippingLimitation, p.ShippingMode, oi.ProductId AS ProductId
+ FROM ' . TABLE_PREFIX . 'Products p
+ LEFT JOIN ' . TABLE_PREFIX . 'OrderItems AS oi ON oi.ProductId = p.ProductId
+ WHERE oi.OrderId = ' . $ord_id . ' AND p.Type = ' . PRODUCT_TYPE_TANGIBLE;
+ $limitations = $this->Conn->Query($sql, 'ProductId');
- $types_index = array();
+ // group products by shipping type range and calculate intersection of all types available for ALL products
+ // the intersection calculation is needed to determine if the order can be shipped with single type or not
- // group products by shipping type range and caculate intersection of all types available for ALL products
- // the intersaction caclulation is needed to determine if the order can be shipped with single type or not
if ($limitations) {
- $limit_types = null;
- foreach ($limitations as $product_id => $row)
- {
+ $limit_types = false;
+
+ foreach ($limitations as $product_id => $row) {
// if shipping types are limited - get the types
- $types = $row['ShippingLimitation'] != '' ? explode('|', substr($row['ShippingLimitation'], 1, -1)) : array('ANY');
+ $types = $row['ShippingLimitation'] != '' ? explode('|', substr($row['ShippingLimitation'], 1, -1)) : Array ('ANY');
+
// if shipping is NOT limited to selected types (default - so products with no limitations at all also counts)
- if ($row['ShippingMode'] == 0) {
+ if ($row['ShippingMode'] == PRODUCT_SHIPPING_MODE_ANY_AND_SELECTED) {
array_push($types, 'ANY'); // can be shipped with ANY (literally) type
$types = array_unique($types);
}
+
//adding product id to split_shipments group by types range
$i = array_search(serialize($types), $types_index);
if ($i === false) {
$types_index[] = serialize($types);
- $i = count($types_index)-1;
+ $i = count($types_index) - 1;
}
+
$split_shipments[$i]['Products'][] = $product_id;
$split_shipments[$i]['Types'] = serialize($types);
- if ($limit_types == null) { //it is null only when we process first item with limitations
- $limit_types = $types; //initial scope
+
+ if ($limit_types === false) {
+ // it is false only when we process first item with limitations
+ $limit_types = $types; // initial scope
}
// this is to avoid ANY intersect CUST_1 = (), but allows ANY intersect CUST_1,ANY = (ANY)
- if (in_array('ANY', $limit_types) && !in_array('ANY', $types)) {
+ if ( in_array('ANY', $limit_types) && !in_array('ANY', $types) ) {
array_splice($limit_types, array_search('ANY', $limit_types), 1, $types);
}
+
// this is to avoid CUST_1 intersect ANY = (), but allows CUST_1 intersect CUST_1,ANY = (ANY)
- if (!in_array('ANY', $limit_types) && in_array('ANY', $types)) {
+ if ( !in_array('ANY', $limit_types) && in_array('ANY', $types) ) {
array_splice($types, array_search('ANY', $types), 1, $limit_types);
}
+
$limit_types = array_intersect($limit_types, $types);
}
+
$limit_types = count($limit_types) > 0 ? serialize(array_unique($limit_types)) : 'NONE';
}
- return array($split_shipments, $limit_types);
+
+ return Array ($split_shipments, $limit_types);
}
function PaymentTypeForm($params)
{
$object =& $this->getObject($params);
$payment_type_id = $object->GetDBField('PaymentType');
if($payment_type_id)
{
$this->Application->SetVar('pt_id', $payment_type_id);
$block_params['name'] = $this->SelectParam($params, $this->UsingCreditCard($params) ? 'cc_render_as,block_cc' : 'default_render_as,block_default' );
return $this->Application->ParseBlock($block_params);
}
return '';
}
/**
* Returns true in case if credit card was used as payment type for order
*
* @param Array $params
* @return bool
*/
function UsingCreditCard($params)
{
$object =& $this->getObject($params);
$pt = $object->GetDBField('PaymentType');
if (!$pt) {
$pt = $this->Conn->GetOne('SELECT PaymentTypeId FROM '.TABLE_PREFIX.'PaymentTypes WHERE IsPrimary = 1');
$object->SetDBField('PaymentType', $pt);
}
$pt_table = $this->Application->getUnitOption('pt','TableName');
$sql = 'SELECT GatewayId FROM %s WHERE PaymentTypeId = %s';
$gw_id = $this->Conn->GetOne( sprintf( $sql, $pt_table, $pt ) );
$sql = 'SELECT RequireCCFields FROM %s WHERE GatewayId = %s';
return $this->Conn->GetOne( sprintf($sql, TABLE_PREFIX.'Gateways', $gw_id) );
}
function PaymentTypeDescription($params)
{
return $this->Application->ProcessParsedTag('pt', 'Field', array_merge($params, Array(
'field' => 'Description'
)) );
}
function PaymentTypeInstructions($params)
{
return $this->Application->ProcessParsedTag('pt', 'Field', array_merge($params, Array(
'field' => 'Instructions'
)) );
}
function PrintMonthOptions($params)
{
$object =& $this->getObject($params);
$date = explode('/', $object->GetDBField($params['date_field_name']));
if (!$date || sizeof($date) != 2) {
$date=array("", "");
}
$o = '';
$params['name'] = $params['block'];
for ($i = 1; $i <= 12; $i++) {
$month_str = str_pad($i, 2, "0", STR_PAD_LEFT);
if ($date[0] == $month_str) {
$params['selected'] = ' selected';
}else {
$params['selected'] = '';
}
$params['mm'] = $month_str;
$o .= $this->Application->ParseBlock($params);
}
return $o;
}
function PrintYearOptions($params)
{
$object =& $this->getObject($params);
$value = $object->GetDBField( $params['field'] );
$block_params = $this->prepareTagParams($params);
$block_params['name'] = $this->SelectParam($params, 'render_as,block');
$o = '';
$this_year = adodb_date('y');
for($i = $this_year; $i <= $this_year + 10; $i++)
{
$year_str = str_pad($i, 2, '0', STR_PAD_LEFT);
$block_params['selected'] = ($value == $year_str) ? $params['selected'] : '';
$block_params['key'] = $year_str;
$block_params['option'] = $year_str;
$o .= $this->Application->ParseBlock($block_params);
}
return $o;
}
function PrintMyOrders($params)
{
}
/**
* Checks, that order data can be editied based on it's status
*
* @param Array $params
* @return bool
*/
function OrderEditable($params)
{
$id_field = $this->Application->getUnitOption($this->Prefix, 'IDField');
$table_name = $this->Application->getUnitOption($this->Prefix, 'TableName');
if ($this->Application->IsTempMode($this->Prefix, $this->Special)) {
$table_name = $this->Application->GetTempName($table_name, 'prefix:' . $this->Prefix);
}
// use direct select here (not $this->getObject) because this tag is
// used even before "combined_header" block is used (on "orders_edit_items" template)
$sql = 'SELECT Status, PaymentType
FROM ' . $table_name . '
WHERE ' . $id_field . ' = ' . $this->Application->GetVar( $this->getPrefixSpecial() . '_id' );
$order_data = $this->Conn->GetRow($sql);
if (!$order_data) {
// new order adding, when even not in database
return true;
}
switch ($order_data['Status']) {
case ORDER_STATUS_INCOMPLETE:
$ret = true;
break;
case ORDER_STATUS_PENDING:
case ORDER_STATUS_BACKORDERS:
$sql = 'SELECT PlacedOrdersEdit
FROM ' . $this->Application->getUnitOption('pt', 'TableName') . '
WHERE ' . $this->Application->getUnitOption('pt', 'IDField') . ' = ' . $order_data['PaymentType'];
$ret = $this->Conn->GetOne($sql);
break;
default:
$ret = false;
break;
}
return $ret;
}
function CheckoutSteps($params)
{
$steps = explode(',', $params['steps']);
foreach ($steps as $key => $item)
{
$templates[$key] = trim($item);
}
$templates = explode(',', $params['templates']);
foreach ($templates as $key => $item)
{
$templates[$key] = trim($item);
}
$total_steps = count($templates);
$t = $this->Application->GetVar('t');
$o = '';
$block_params = array();
$i = 0;
$passed_current = preg_match("/".preg_quote($templates[count($templates)-1], '/')."/", $t);
foreach ($steps as $step => $name)
{
if (preg_match("/".preg_quote($templates[$step], '/')."/", $t)) {
$block_params['name'] = $this->SelectParam($params, 'current_step_render_as,block_current_step');
$passed_current = true;
}
else {
$block_params['name'] = $passed_current ? $this->SelectParam($params, 'render_as,block') : $this->SelectParam($params, 'passed_step_render_as,block_passed_step');
}
$block_params['title'] = $this->Application->Phrase($name);
$block_params['template'] = $templates[$i];
$block_params['template_link'] = $this->Application->HREF($templates[$step], '', Array('pass'=>'m'));
$block_params['next_step_template'] = isset($templates[$i + 1]) ? $templates[$i + 1] : '';
$block_params['number'] = $i + 1;
$i++;
$o.= $this->Application->ParseBlock($block_params, 1);
}
return $o;
}
function ShowOrder($params)
{
$order_params = $this->prepareTagParams($params);
// $order_params['Special'] = 'myorders';
// $order_params['PrefixSpecial'] = 'ord.myorders';
$order_params['name'] = $this->SelectParam($order_params, 'render_as,block');
// $this->Application->SetVar('ord.myorders_id', $this->Application->GetVar('ord_id'));
$object =& $this->getObject($params);
if (!$object->GetDBField('OrderId')) {
return;
}
return $this->Application->ParseBlock($order_params);
}
function BuildListSpecial($params)
{
if ($this->Special != '') {
return $this->Special;
}
$list_unique_key = $this->getUniqueListKey($params);
if ($list_unique_key == '') {
return parent::BuildListSpecial($params);
}
return crc32($list_unique_key);
}
function ListOrders($params)
{
$o = '';
$params['render_as'] = $params['item_render_as'];
$o_orders = $this->PrintList2($params);
if ($o_orders) {
$orders_params = array('name' => $params['header_render_as']);
$o = $this->Application->ParseBlock($orders_params);
$o .= $o_orders;
} else {
$orders_params = array('name' => $params['empty_myorders_render_as']);
$o = $this->Application->ParseBlock($orders_params);
}
return $o;
}
function HasRecentOrders($params)
{
$per_page = $this->SelectParam($params, 'per_page,max_items');
if ($per_page !== false) {
$params['per_page'] = $per_page;
}
return (int)$this->TotalRecords($params) > 0 ? 1 : 0;
}
function ListOrderItems($params)
{
$prefix_special = rtrim('orditems.'.$this->Special, '.');
return $this->Application->ProcessParsedTag($prefix_special, 'PrintList', array_merge($params, Array(
'per_page' => -1
)) );
}
function OrdersLink(){
$params['pass']='m,ord';
$main_processor =& $this->Application->RecallObject('m_TagProcessor');
return $main_processor->Link($params);
}
function PrintAddresses($params)
{
$object =& $this->getObject($params);
$address_list =& $this->Application->recallObject('addr','addr_List', Array('per_page'=>-1, 'skip_counting'=>true) );
$address_list->Query();
$address_id = $this->Application->GetVar($params['type'].'_address_id');
if (!$address_id) {
$sql = 'SELECT '.$address_list->IDField.'
FROM '.$address_list->TableName.'
WHERE PortalUserId = '.$object->GetDBField('PortalUserId').' AND LastUsedAs'.ucfirst($params['type']).' = 1';
$address_id = (int)$this->Conn->GetOne($sql);
}
$ret = '';
$block_params = $this->prepareTagParams($params);
$block_params['name'] = $this->SelectParam($params, 'render_as,block');
$address_list->GoFirst();
while (!$address_list->EOL()) {
$selected = ($address_list->GetID() == $address_id);
if ($selected && $address_list->GetDBField('IsProfileAddress')) {
$this->Application->SetVar($this->Prefix.'_IsProfileAddress', true);
}
$block_params['key'] = $address_list->GetID();
$block_params['value'] = $address_list->GetDBField('ShortAddress');
$block_params['selected'] = $selected ? ' selected="selected"' : '';
$ret .= $this->Application->ParseBlock($block_params, 1);
$address_list->GoNext();
}
return $ret;
}
function PrefillRegistrationFields($params)
{
if ( $this->Application->GetVar('fields_prefilled') ) {
return false;
}
$user =& $this->Application->recallObject('u', null, Array ('skip_autoload' => true));
/* @var $user kDBItem */
$order =& $this->Application->recallObject($this->Prefix . '.last');
$order_prefix = $params['type'] == 'billing' ? 'Billing' : 'Shipping';
$order_fields = Array (
'To', 'Company', 'Phone', 'Fax', 'Email', 'Address1',
'Address2', 'City', 'State', 'Zip', 'Country'
);
$names = explode(' ', $order->GetDBField($order_prefix.'To'), 2);
if (!$user->GetDBField('FirstName')) $user->SetDBField('FirstName', getArrayValue($names, 0) );
if (!$user->GetDBField('LastName')) $user->SetDBField('LastName', getArrayValue($names, 1) );
if (!$user->GetDBField('Company')) $user->SetDBField('Company', $order->GetDBField($order_prefix.'Company') );
if (!$user->GetDBField('Phone')) $user->SetDBField('Phone', $order->GetDBField($order_prefix.'Phone') );
if (!$user->GetDBField('Fax')) $user->SetDBField('Fax', $order->GetDBField($order_prefix.'Fax') );
if (!$user->GetDBField('Email')) $user->SetDBField('Email', $order->GetDBField($order_prefix.'Email') );
if (!$user->GetDBField('Street')) $user->SetDBField('Street', $order->GetDBField($order_prefix.'Address1') );
if (!$user->GetDBField('Street2')) $user->SetDBField('Street2', $order->GetDBField($order_prefix.'Address2') );
if (!$user->GetDBField('City')) $user->SetDBField('City', $order->GetDBField($order_prefix.'City') );
if (!$user->GetDBField('State')) $user->SetDBField('State', $order->GetDBField($order_prefix.'State') );
if (!$user->GetDBField('Zip')) $user->SetDBField('Zip', $order->GetDBField($order_prefix.'Zip') );
if (!$user->GetDBField('Country')) $user->SetDBField('Country', $order->GetDBField($order_prefix.'Country') );
$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$cs_helper->PopulateStates(new kEvent('u:OnBuild'), 'State', 'Country');
}
function UserLink($params)
{
$object =& $this->getObject($params);
$user_id = $object->GetDBField( $params['user_field'] );
if ($user_id) {
$url_params = Array (
'm_opener' => 'd',
'u_mode' => 't',
'u_event' => 'OnEdit',
'u_id' => $user_id,
'pass' => 'all,u',
'no_pass_through' => 1,
);
return $this->Application->HREF($params['edit_template'], '', $url_params);
}
}
function UserFound($params)
{
$virtual_users = Array(USER_ROOT, USER_GUEST, 0);
$object =& $this->getObject($params);
return !in_array( $object->GetDBField( $params['user_field'] ) , $virtual_users );
}
/**
* Returns a link for editing order
*
* @param Array $params
* @return string
*/
function OrderLink($params)
{
$object =& $this->getObject($params);
$url_params = Array (
'm_opener' => 'd',
$this->Prefix.'_mode' => 't',
$this->Prefix.'_event' => 'OnEdit',
$this->Prefix.'_id' => $object->GetID(),
'pass' => 'all,'.$this->Prefix,
'no_pass_through' => 1,
);
return $this->Application->HREF($params['edit_template'], '', $url_params);
}
function HasOriginalAmount($params)
{
$object =& $this->getObject($params);
$original_amount = $object->GetDBField('OriginalAmount');
return $original_amount && ($original_amount != $object->GetDBField('TotalAmount') );
}
/**
* Returns true, when order has tangible items
*
* @param Array $params
* @return bool
*
* @todo This is copy from OrdersItem::HasTangibleItems. Copy to helper (and create it) and use here.
*/
function OrderHasTangibleItems($params)
{
$object =& $this->getObject($params);
if ($object->GetID() == FAKE_ORDER_ID) {
return false;
}
$sql = 'SELECT COUNT(*)
FROM '.TABLE_PREFIX.'OrderItems orditems
LEFT JOIN '.TABLE_PREFIX.'Products p ON p.ProductId = orditems.ProductId
WHERE (orditems.OrderId = '.$object->GetID().') AND (p.Type = '.PRODUCT_TYPE_TANGIBLE.')';
return $this->Conn->GetOne($sql) ? true : false;
}
function ShipmentsExists($params)
{
return $this->Application->GetVar('ShipmentsExists') ? 1 : 0;
}
function Field($params)
{
$value = parent::Field($params);
$field = $this->SelectParam($params,'name,field');
if( ($field == 'PaymentAccount') && getArrayValue($params,'masked') )
{
$value = str_repeat('X',12).substr($value,-4);
}
return $value;
}
function CartHasError($params)
{
- return $this->Application->GetVar('checkout_error') > 0;
+ return $this->Application->RecallVar('checkout_errors');
}
function CheckoutError($params)
{
- $error_codes = Array (
- 1 => 'state_changed',
- 2 => 'qty_unavailable',
- 3 => 'outofstock',
- 4 => 'invalid_code',
- 5 => 'code_expired',
- 6 => 'min_qty',
- 7 => 'code_removed',
- 8 => 'code_removed_automatically',
- 9 => 'changed_after_login',
- 10 => 'coupon_applied',
- 104 => 'invalid_gc_code',
- 105 => 'gc_code_expired',
- 107 => 'gc_code_removed',
- 108 => 'gc_code_removed_automatically',
- 110 => 'gift_certificate_applied',
- );
+ $errors = $this->Application->RecallVar('checkout_errors');
+
+ if ( !$errors ) {
+ return '';
+ }
+
+// $this->Application->RemoveVar('checkout_errors');
+ $errors = unserialize($errors);
+
+ if ( isset($errors[OrderCheckoutErrorType::COUPON]) ) {
+ $mapping = Array (
+ OrderCheckoutError::COUPON_APPLIED => 'coupon_applied',
+ OrderCheckoutError::COUPON_REMOVED => 'code_removed',
+ OrderCheckoutError::COUPON_REMOVED_AUTOMATICALLY => 'code_removed_automatically',
+ OrderCheckoutError::COUPON_CODE_INVALID => 'invalid_code',
+ OrderCheckoutError::COUPON_CODE_EXPIRED => 'code_expired',
+ );
+
+ $error_phrase = $mapping[ $errors[OrderCheckoutErrorType::COUPON] ];
+ }
+ elseif ( isset($errors[OrderCheckoutErrorType::GIFT_CERTIFICATE]) ) {
+ $mapping = Array (
+ OrderCheckoutError::GC_APPLIED => 'gift_certificate_applied',
+ OrderCheckoutError::GC_REMOVED => 'gc_code_removed',
+ OrderCheckoutError::GC_REMOVED_AUTOMATICALLY => 'gc_code_removed_automatically',
+ OrderCheckoutError::GC_CODE_INVALID => 'invalid_gc_code',
+ OrderCheckoutError::GC_CODE_EXPIRED => 'gc_code_expired',
+
+ );
- $error_param = $error_codes[ $this->Application->GetVar('checkout_error') ];
- return $this->Application->Phrase($params[$error_param]);
+ $error_phrase = $mapping[ $errors[OrderCheckoutErrorType::GIFT_CERTIFICATE] ];
+ }
+ else {
+ $mapping = Array (
+ OrderCheckoutError::QTY_UNAVAILABLE => 'qty_unavailable',
+ OrderCheckoutError::QTY_OUT_OF_STOCK => 'outofstock',
+ OrderCheckoutError::QTY_CHANGED_TO_MINIMAL => 'min_qty',
+ );
+
+ foreach ($errors as $error_type => $error_code) {
+ if ( isset($mapping[$error_code]) ) {
+ $error_phrase = $mapping[$error_code];
+ break;
+ }
+ }
+
+ if ( !isset($error_phrase) ) {
+ $error_phrase = 'state_changed'; // 'changed_after_login'
+ }
+ }
+
+ return $this->Application->Phrase( $params[$error_phrase] );
+ }
+
+ /**
+ * Returns checkout errors in JSON format
+ *
+ * @param Array $params
+ * @return string
+ */
+ function PrintOrderInfo($params)
+ {
+ $order_helper =& $this->Application->recallObject('OrderHelper');
+ /* @var $order_helper OrderHelper */
+
+ $object =& $this->getObject($params);
+ /* @var $object kDBItem */
+
+ $currency = isset($params['currency']) ? $params['currency'] : 'selected';
+
+ return json_encode( $order_helper->getOrderInfo($object, $currency) );
+ }
+
+ /**
+ * Returns currency mark (%s is $amount placemark)
+ *
+ * @param Array $params
+ * @return string
+ */
+ function CurrencyMask($params)
+ {
+ $iso = $this->GetISO( $params['currency'] );
+
+ return $this->AddCurrencySymbol('%s', $iso);
+ }
+
+ function CheckoutErrorNew($params)
+ {
+ $errors = $this->Application->RecallVar('checkout_errors');
+
+ if ( !$errors ) {
+ return '';
+ }
+
+// $this->Application->RemoveVar('checkout_errors');
+ $errors = unserialize($errors);
+
+ $reflection = new ReflectionClass('OrderCheckoutErrorType');
+ $error_types = $reflection->getConstants();
+
+ $reflection = new ReflectionClass('OrderCheckoutError');
+ $error_codes = $reflection->getConstants();
+
+ $ret = Array ();
+
+ foreach ($errors as $error_type => $error_code) {
+ $error_type = explode(':', $error_type);
+
+ $error_explained = '<strong>' . array_search($error_type[0], $error_types) . '</strong>';
+
+ if ( $error_type[0] == OrderCheckoutErrorType::PRODUCT ) {
+ $error_explained .= ' (ProductId = <strong>' . $error_type[1] . '</strong>; OptionsSalt: <strong>' . $error_type[2] . '</strong>; BackOrderFlag: ' . $error_type[3] . '; Field: <strong>' . $error_type[4] . '</strong>)';
+ }
+
+ $error_explained .= ' - <strong>' . array_search($error_code, $error_codes) . '</strong>';
+ $ret[] = $error_explained;
+ }
+
+ return implode('<br/>', $ret);
}
function GetFormAction($params)
{
$object =& $this->getObject($params);
/* @var $object OrdersItem */
$gw_data = $object->getGatewayData($params['payment_type_id']);
$this->Application->registerClass( $gw_data['ClassName'], GW_CLASS_PATH.'/'.$gw_data['ClassFile'] );
$gateway_object =& $this->Application->recallObject( $gw_data['ClassName'] );
/* @var $gateway_object kGWBase */
return $gateway_object->getFormAction($gw_data['gw_params']);
}
function GetFormHiddenFields($params)
{
$object =& $this->getObject($params);
/* @var $object OrdersItem */
$gw_data = $object->getGatewayData(array_key_exists('payment_type_id', $params) ? $params['payment_type_id'] : null);
$this->Application->registerClass( $gw_data['ClassName'], GW_CLASS_PATH.'/'.$gw_data['ClassFile'] );
$gateway_object =& $this->Application->recallObject( $gw_data['ClassName'] );
$tpl = '<input type="hidden" name="%s" value="%s" />'."\n";
$hidden_fields = $gateway_object->getHiddenFields($object->GetFieldValues(), $params, $gw_data['gw_params']);
$ret = '';
if (!is_array($hidden_fields)) {
return $hidden_fields;
}
foreach($hidden_fields as $hidden_name => $hidden_value)
{
$ret .= sprintf($tpl, $hidden_name, $hidden_value);
}
return $ret;
}
function NeedsPlaceButton($params)
{
$object =& $this->getObject($params);
$gw_data = $object->getGatewayData();
$this->Application->registerClass( $gw_data['ClassName'], GW_CLASS_PATH.'/'.$gw_data['ClassFile'] );
$gateway_object =& $this->Application->recallObject( $gw_data['ClassName'] );
return $gateway_object->NeedPlaceButton($object->GetFieldValues(), $params, $gw_data['gw_params']);
}
function HasGatewayError($params)
{
return $this->Application->RecallVar('gw_error');
}
function ShowGatewayError($params)
{
$ret = $this->Application->RecallVar('gw_error');
$this->Application->RemoveVar('gw_error');
return $ret;
}
function ShippingType($params)
{
$object =& $this->getObject($params);
$shipping_info = unserialize( $object->GetDBField('ShippingInfo') );
if (count($shipping_info) > 1) {
return $this->Application->Phrase('lu_MultipleShippingTypes');
}
$shipping_info = array_shift($shipping_info);
return $shipping_info['ShippingName'];
}
function DiscountHelpLink($params)
{
$params['pass'] = 'all,orditems';
$params['m_cat_id'] = 0;
$m_tag_processor =& $this->Application->recallObject('m_TagProcessor');
return $m_tag_processor->Link($params);
}
function DiscountField($params)
{
$orditems =& $this->Application->recallObject( 'orditems' );
$item_data = $orditems->GetDBField('ItemData');
if(!$item_data) return '';
$item_data = unserialize($item_data);
$discount_prefix = ($item_data['DiscountType'] == 'coupon') ? 'coup' : 'd';
$discount =& $this->Application->recallObject($discount_prefix, null, Array('skip_autoload' => true));
if(!$discount->isLoaded())
{
$discount->Load($item_data['DiscountId']);
}
return $discount->GetField( $this->SelectParam($params, 'field,name') );
}
function HasDiscount($params)
{
$object =& $this->getObject($params);
return (float)$object->GetDBField('DiscountTotal') ? 1 : 0;
}
/**
* Allows to check if required product types are present in order
*
* @param Array $params
*/
function HasProductType($params)
{
$product_types = Array('tangible' => 1, 'subscription' => 2, 'service' => 3, 'downloadable' => 4, 'package' => 5, 'gift' => 6);
$object =& $this->getObject($params);
$sql = 'SELECT COUNT(*)
FROM '.TABLE_PREFIX.'OrderItems oi
LEFT JOIN '.TABLE_PREFIX.'Products p ON p.ProductId = oi.ProductId
WHERE (oi.OrderId = '.$object->GetID().') AND (p.Type = '.$product_types[ $params['type'] ].')';
return $this->Conn->GetOne($sql);
}
function PrintSerializedFields($params)
{
$object =& $this->getObject($params);
$field = $this->SelectParam($params, 'field');
if (!$field) $field = $this->Application->GetVar('field');
$data = unserialize($object->GetDBField($field));
$o = '';
$block_params['name'] = $params['render_as'];
foreach ($data as $field => $value) {
$block_params['field'] = $field;
$block_params['value'] = $value;
$o .= $this->Application->ParseBlock($block_params);
}
return $o;
}
function OrderProductEmail($params)
{
$order =& $this->Application->recallObject('ord');
$orditems =& $this->Application->recallObject('orditems');
$sql = 'SELECT ResourceId
FROM '.TABLE_PREFIX.'Products
WHERE ProductId = '.$orditems->GetDBField('ProductId');
$resource_id = $this->Conn->GetOne($sql);
$ml_formatter =& $this->Application->recallObject('kMultiLanguage');
$custom_fields = $this->Application->getUnitOption('p', 'CustomFields');
$custom_name = $ml_formatter->LangFieldName('cust_'.array_search($params['msg_custom_field'], $custom_fields));
$sql = 'SELECT '.$custom_name.'
FROM '.$this->Application->getUnitOption('p-cdata', 'TableName').'
WHERE ResourceId = '.$resource_id;
$message_template = $this->Conn->GetOne($sql);
if (!$message_template || trim($message_template) == '') {
// message template missing
return ;
}
$from_name = strip_tags($this->Application->ConfigValue('Site_Name'));
$from_email = $this->Application->ConfigValue('Smtp_AdminMailFrom');
$to_name = $order->GetDBField('BillingTo');
$to_email = $order->GetDBField('BillingEmail');
if (!$to_email) {
// billing email is empty, then use user's email
$sql = 'SELECT Email
FROM '.$this->Application->getUnitOption('u', 'TableName').'
WHERE PortalUserId = '.$order->GetDBField('PortalUserId');
$to_email = $this->Conn->GetOne($sql);
}
$esender =& $application->recallObject('EmailSender.-product');
/* @var $esender kEmailSendingHelper */
$esender->SetFrom($from_email, $from_name);
$esender->AddTo($to_email, $to_name);
$email_events_eh =& $this->Application->recallObject('emailevents_EventHandler');
/* @var $email_events_eh EmailEventsEventsHandler */
list ($message_headers, $message_body) = $email_events_eh->ParseMessageBody($message_template, Array());
if (!trim($message_body)) {
// message body missing
return false;
}
foreach ($message_headers as $header_name => $header_value) {
$esender->SetEncodedHeader($header_name, $header_value);
}
$esender->SetHTML($message_body);
$esender->Deliver();
}
function PrintTotals($params)
{
$order =& $this->getObject($params);
$totals = array();
if (ABS($order->GetDBField('SubTotal') - $order->GetDBField('AmountWithoutVAT')) > 0.01) {
$totals[] = 'products';
}
$has_tangible = $this->OrderHasTangibleItems($params);
if ($has_tangible && $order->GetDBField('ShippingTaxable')) {
$totals[] = 'shipping';
}
if ($order->GetDBField('ProcessingFee') > 0 && $order->GetDBField('ProcessingTaxable')) {
$totals[] = 'processing';
}
if ($order->GetDBField('ReturnTotal') > 0 && $order->GetDBField('ReturnTotal')) {
$totals[] = 'return';
}
$totals[] = 'sub_total';
if ($order->GetDBField('VAT') > 0) {
$totals[] = 'vat';
}
if ($has_tangible && !$order->GetDBField('ShippingTaxable')) {
$totals[] = 'shipping';
}
if ($order->GetDBField('ProcessingFee') > 0 && !$order->GetDBField('ProcessingTaxable')) {
$totals[] = 'processing';
}
$o = '';
foreach ($totals as $type)
{
if ($element = getArrayValue($params, $type.'_render_as')) {
$o .= $this->Application->ParseBlock( array('name' => $element), 1 );
}
}
return $o;
}
function ShowDefaultAddress($params)
{
$address_type = ucfirst($params['type']);
if ($this->Application->GetVar('check_'.strtolower($address_type).'_address')) {
// form type doesn't match check type, e.g. shipping check on billing form
return '';
}
// for required field highlighting on form when no submit made
$this->Application->SetVar('check_'.strtolower($address_type).'_address', 'true');
/*if ((strtolower($address_type) == 'billing') && $this->UsingCreditCard($params)) {
$this->Application->SetVar('check_credit_card', 'true');
}*/
$this->Application->HandleEvent(new kEvent('ord:SetStepRequiredFields'));
$user_id = $this->Application->RecallVar('user_id');
$sql = 'SELECT AddressId
FROM '.TABLE_PREFIX.'Addresses
WHERE PortalUserId = '.$user_id.' AND LastUsedAs'.$address_type.' = 1';
$address_id = $this->Conn->GetOne($sql);
if (!$address_id) {
return '';
}
$addr_list =& $this->Application->recallObject('addr', 'addr_List', Array('per_page'=>-1, 'skip_counting'=>true) );
$addr_list->Query();
$object =& $this->getObject();
if (!$addr_list->CheckAddress($object->GetFieldValues(), $address_type)) {
$addr_list->CopyAddress($address_id, $address_type);
}
}
function IsProfileAddress($params)
{
$object =& $this->getObject($params);
$address_type = ucfirst($params['type']);
return $object->IsProfileAddress($address_type);
}
function HasPayPalSubscription($params)
{
$object =& $this->getObject($params);
$sql = 'SELECT COUNT(*)
FROM '.TABLE_PREFIX.'OrderItems oi
LEFT JOIN '.TABLE_PREFIX.'Products p ON p.ProductId = oi.ProductId
WHERE (oi.OrderId = '.$object->GetID().') AND (p.PayPalRecurring = 1)';
return $this->Conn->GetOne($sql);
}
function GetPayPalSubscriptionForm($params)
{
$object =& $this->getObject($params);
$gw_data = $object->getGatewayData($params['payment_type_id']);
$this->Application->registerClass( $gw_data['ClassName'], GW_CLASS_PATH.'/'.$gw_data['ClassFile'] );
$gateway_object =& $this->Application->recallObject( $gw_data['ClassName'] );
$sql = 'SELECT oi.*
FROM '.TABLE_PREFIX.'OrderItems oi
LEFT JOIN '.TABLE_PREFIX.'Products p ON p.ProductId = oi.ProductId
WHERE (oi.OrderId = '.$object->GetID().') AND (p.PayPalRecurring = 1)';
$order_item = $this->Conn->GetRow($sql);
$order_item_data = unserialize($order_item['ItemData']);
$cycle = ceil($order_item_data['Duration'] / 86400);
$cycle_units = 'D';
$item_data = $object->GetFieldValues();
$item_data['item_name'] = $order_item['ProductName'];
$item_data['item_number'] = $order_item['OrderItemId'];
$item_data['custom'] = $order_item['OrderId'];
$item_data['a1'] = '';
$item_data['p1'] = '';
$item_data['t1'] = '';
$item_data['a2'] = '';
$item_data['p2'] = '';
$item_data['t2'] = '';
$item_data['a3'] = $order_item['Price']; //rate
$item_data['p3'] = $cycle; //cycle
$item_data['t3'] = $cycle_units; //cycle units D (days), W (weeks), M (months), Y (years)
$item_data['src'] = '1'; // Recurring payments. If set to 1, the payment will recur unless your customer cancels the subscription before the end of the billing cycle.
$item_data['sra'] = '1'; // Reattempt on failure. If set to 1, and the payment fails, the payment will be reattempted two more times. After the third failure, the subscription will be cancelled.
$item_data['srt'] = ''; // Recurring Times. This is the number of payments which will occur at the regular rate.
$hidden_fields = $gateway_object->getSubscriptionFields($item_data, $params, $gw_data['gw_params']);
$ret = '';
if (!is_array($hidden_fields)) {
return $hidden_fields;
}
$tpl = '<input type="hidden" name="%s" value="%s" />'."\n";
foreach($hidden_fields as $hidden_name => $hidden_value)
{
$ret .= sprintf($tpl, $hidden_name, $hidden_value);
}
return $ret;
}
function UserHasPendingOrders($params)
{
$sql = 'SELECT OrderId FROM '.$this->Application->getUnitOption($this->Prefix, 'TableName').'
WHERE PortalUserId = '.$this->Application->RecallVar('user_id').'
AND Status = '.ORDER_STATUS_PENDING;
return $this->Conn->GetOne($sql) ? 1 : 0;
}
function AllowAddAddress($params)
{
$user =& $this->Application->recallObject('u.current');
if ($user->GetDBField('cust_shipping_addr_block')) return false;
$address_list =& $this->Application->recallObject('addr','addr_List', Array('per_page'=>-1, 'skip_counting'=>true) );
$address_list->Query();
$max = $this->Application->ConfigValue('MaxAddresses');
return $max <= 0 ? true : $address_list->GetRecordsCount() < $max;
}
- function FreePromoShippingAvailable($params)
- {
- $object =& $this->Application->recallObject('orditems');
- $free_ship = $object->GetDBField('MinQtyFreePromoShipping');
- $tangible = ($object->GetDBField('Type') == 1)? 1 : 0;
- return ($tangible && ($free_ship > 0 && $free_ship <= $object->GetDBField('Quantity')))? 1 : 0;
- }
-
/**
* Creates link for removing coupon or gift certificate
*
* @param Array $params
* @return string
*/
function RemoveCouponLink($params)
{
$type = strtolower($params['type']);
$url_params = Array (
'pass' => 'm,ord',
'ord_event' => ($type == 'coupon') ? 'OnRemoveCoupon' : 'OnRemoveGiftCertificate',
'm_cat_id' => 0,
);
return $this->Application->HREF('', '', $url_params);
}
/**
* Calculates total weight of items in shopping cart
*
* @param Array $params
* @return float
*/
function TotalOrderWeight($params)
{
$object =& $this->getObject();
/* @var $object kDBItem */
$sql = 'SELECT SUM( IF(oi.Weight IS NULL, 0, oi.Weight * oi.Quantity) )
FROM '.TABLE_PREFIX.'OrderItems oi
WHERE oi.OrderId = '.$object->GetID();
$total_weight = $this->Conn->GetOne($sql);
if ($total_weight == '') {
// zero weight -> return text about it
return $this->Application->Phrase('lu_NotAvailable');
}
+ return $this->_formatWeight($total_weight);
+ }
+
+ function _formatWeight($weight)
+ {
$regional =& $this->Application->recallObject('lang.current');
+ /* @var $regional kDBItem */
- switch ($regional->GetDBField('UnitSystem')) {
+ switch ( $regional->GetDBField('UnitSystem') ) {
case 1:
// metric system -> add kg sign
- $total_weight .= ' '.$this->Application->Phrase('lu_kg');
+ $weight .= ' ' . $this->Application->Phrase('lu_kg');
break;
case 2:
// uk system -> convert to pounds
- list($pounds, $ounces) = kUtil::Kg2Pounds($total_weight);
- $total_weight = $pounds.' '.$this->Application->Phrase('lu_pounds').' '.$ounces.' '.$this->Application->Phrase('lu_ounces');
+ list ($pounds, $ounces) = kUtil::Kg2Pounds($weight);
+
+ $weight = $pounds . ' ' . $this->Application->Phrase('lu_pounds') . ' ' . $ounces . ' ' . $this->Application->Phrase('lu_ounces');
break;
}
- return $total_weight;
+ return $weight;
}
function InitCatalogTab($params)
{
$tab_params['mode'] = $this->Application->GetVar('tm'); // single/multi selection possible
$tab_params['special'] = $this->Application->GetVar('ts'); // use special for this tab
$tab_params['dependant'] = $this->Application->GetVar('td'); // is grid dependant on categories grid
// set default params (same as in catalog)
if ($tab_params['mode'] === false) $tab_params['mode'] = 'multi';
if ($tab_params['special'] === false) $tab_params['special'] = '';
if ($tab_params['dependant'] === false) $tab_params['dependant'] = 'yes';
// pass params to block with tab content
$params['name'] = $params['render_as'];
$params['prefix'] = trim($this->Prefix.'.'.($tab_params['special'] ? $tab_params['special'] : $this->Special), '.');
$params['cat_prefix'] = trim('c.'.($tab_params['special'] ? $tab_params['special'] : $this->Special), '.');
$params['tab_mode'] = $tab_params['mode'];
$params['grid_name'] = ($tab_params['mode'] == 'multi') ? $params['default_grid'] : $params['radio_grid'];
$params['tab_dependant'] = $tab_params['dependant'];
$params['show_category'] = $tab_params['special'] == 'showall' ? 1 : 0; // this is advanced view -> show category name
// use $pass_params to be able to pass 'tab_init' parameter from m_ModuleInclude tag
return $this->Application->ParseBlock($params, 1);
}
/**
* Checks if required payment method is available
*
* @param Array $params
* @return bool
*/
function HasPaymentGateway($params)
{
static $payment_types = Array ();
$gw_name = $params['name'];
if (!array_key_exists($gw_name, $payment_types)) {
$sql = 'SELECT pt.PaymentTypeId, pt.PortalGroups
FROM '.TABLE_PREFIX.'PaymentTypes pt
LEFT JOIN '.TABLE_PREFIX.'Gateways g ON pt.GatewayId = g.GatewayId
WHERE (g.Name = '.$this->Conn->qstr($params['name']).') AND (pt.Status = '.STATUS_ACTIVE.')';
$payment_types[$gw_name] = $this->Conn->GetRow($sql);
}
if (!$payment_types[$gw_name]) {
return false;
}
$pt_groups = explode(',', substr($payment_types[$gw_name]['PortalGroups'], 1, -1));
$user_groups = explode(',', $this->Application->RecallVar('UserGroups'));
return array_intersect($user_groups, $pt_groups) ? $payment_types[$gw_name]['PaymentTypeId'] : false;
}
function DisplayPaymentGateway($params)
{
$payment_type_id = $this->HasPaymentGateway($params);
if (!$payment_type_id) {
return '';
}
$object =& $this->getObject($params);
/* @var $object OrdersItem */
$gw_data = $object->getGatewayData($payment_type_id);
$block_params = $gw_data['gw_params'];
$block_params['name'] = $params['render_as'];
$block_params['payment_type_id'] = $payment_type_id;
return $this->Application->ParseBlock($block_params);
}
/**
* Checks, that USPS returned valid label
*
* @param Array $params
* @return bool
*/
function USPSLabelFound($params)
{
$object =& $this->getObject($params);
/* @var $object kDBItem */
$full_path = USPS_LABEL_FOLDER . $object->GetDBField( $params['field'] ) . '.pdf';
return file_exists($full_path) && is_file($full_path);
}
/**
* Prints SQE errors from session
*
* @param Array $params
* @return string
*/
function PrintSQEErrors($params)
{
$sqe_errors = $this->Application->RecallVar('sqe_errors');
if (!$sqe_errors) {
return '';
}
$o = '';
$block_params = $this->prepareTagParams($params);
$block_params['name'] = $params['render_as'];
$sqe_errors = unserialize($sqe_errors);
foreach ($sqe_errors as $order_number => $error_description) {
$block_params['order_number'] = $order_number;
$block_params['error_description'] = $error_description;
$o .= $this->Application->ParseBlock($block_params);
}
$this->Application->RemoveVar('sqe_errors');
return $o;
}
-
}
\ No newline at end of file
Index: branches/5.2.x/units/order_items/order_items_event_handler.php
===================================================================
--- branches/5.2.x/units/order_items/order_items_event_handler.php (revision 14640)
+++ branches/5.2.x/units/order_items/order_items_event_handler.php (revision 14641)
@@ -1,252 +1,355 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class OrderItemsEventHandler extends kDBEventHandler
{
/**
* Allows to override standart permission mapping
*
*/
function mapPermissions()
{
parent::mapPermissions();
$permissions = Array (
'OnItemBuild' => Array ('subitem' => true),
'OnSaveItems' => Array ('subitem' => 'add|edit'),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
/**
* Processes item selection from popup item selector
*
* @param kEvent $event
*/
function OnProcessSelected(&$event)
{
$object =& $event->getObject( Array('skip_autoload' => true) );
$selected_ids = $this->Application->GetVar('selected_ids');
$product_ids = $selected_ids['p'];
if ($product_ids) {
//after adding Options Selection during adding products to order in admin, selector is in single mode
// = allows selecting one item at a time, but we leave this code just in case :)
$product_ids = explode(',', $product_ids);
$product_object =& $this->Application->recallObject('p.-item', null, array('skip_autoload' => true));
+ /* @var $product_object ProductsItem */
foreach ($product_ids as $product_id) {
$product_object->Load($product_id);
$sql = 'SELECT COUNT(*)
FROM ' . $this->Application->getUnitOption('po', 'TableName') . '
WHERE (Required = 1) AND (ProductId = ' . $product_id . ')';
if ( $this->Conn->GetOne($sql) ) {
$url_params = Array (
$event->Prefix . '_event' => 'OnNew',
'p_id' => $product_id,
'm_opener' => 's',
);
$this->Application->EventManager->openerStackPush('in-commerce/orders/order_product_edit', $url_params, 'm,ord,p');
}
else {
$orders_h =& $this->Application->recallObject('ord_EventHandler');
/* @var $orders_h OrdersEventHandler */
// 1 for PacakgeNum - temporary solution to overcome splitting into separate sub-orders
// of orders with items added through admin when approving them
$orders_h->AddItemToOrder($event, $product_id, null, 1);
}
}
}
- $this->finalizePopup($event);
+ $event->SetRedirectParam('opener', 'u');
}
/**
* Updates subtotal field in order record.
* Only for "Items" tab in "Orders -> Order Edit" in Admin
*
* @param kEvent $event
+ * @access protected
*/
- function OnUpdate(&$event)
+ protected function OnUpdate(&$event)
{
- parent::OnUpdate($event);
- if ( ($this->Application->GetVar('t') != 'in-commerce/orders/orders_edit_items') ) {
- return true;
- }
+ $items_info = $this->Application->GetVar($event->getPrefixSpecial(true));
- $object =& $event->getObject();
-
- $items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
- if (!$items_info) {
- return ;
+ if ( !$items_info ) {
+ return;
}
- $sub_total = $this->getSubTotal($items_info);
- $return_total = $this->getReturnTotal($items_info);
+ $object =& $event->getObject(Array ('skip_autoload' => true));
+ /* @var $object kDBItem */
$table_info = $object->getLinkedInfo();
+
$main_object =& $this->Application->recallObject($table_info['ParentPrefix']);
+ /* @var $main_object OrdersItem */
+
+ foreach ($items_info as $id => $field_values) {
+ $object->Clear(); // otherwise validation errors will be passed to next object
+
+ $object->Load($id);
+ $object->SetFieldsFromHash($field_values);
+ $this->customProcessing($event, 'before');
+
+ if ( $object->Update($id) ) {
+ $this->customProcessing($event, 'after');
+ $event->status = kEvent::erSUCCESS;
+ }
+ else {
+ $oi_string = $object->GetDBField('ProductId') . ':' . $object->GetDBField('OptionsSalt') . ':' . $object->GetDBField('BackOrderFlag');
- if ($sub_total !== false) {
+ $field_errors = $object->GetFieldErrors();
+
+ foreach ($field_errors as $field => $error_params) {
+ $error_msg = $object->GetErrorMsg($field);
+
+ if ( $error_msg ) {
+ $main_object->setCheckoutError(OrderCheckoutErrorType::PRODUCT, OrderCheckoutError::FIELD_UPDATE_ERROR, $oi_string . ':' . $field);
+ }
+ }
+
+ $event->status = kEvent::erFAIL;
+ $event->redirect = false;
+// break;
+ }
+ }
+
+ if ( $this->Application->GetVar('t') != 'in-commerce/orders/orders_edit_items' ) {
+ return;
+ }
+
+ $sub_total = $this->getSubTotal($items_info);
+
+ if ( $sub_total !== false ) {
$main_object->SetDBField('SubTotal', $sub_total);
}
- $main_object->SetDBField('ReturnTotal', $return_total);
+ $main_object->SetDBField('ReturnTotal', $this->getReturnTotal($items_info));
$main_object->Update();
}
/**
+ * Remembers what fields were changed
+ *
+ * @param kEvent $event
+ */
+ function OnAfterItemUpdate(&$event)
+ {
+ parent::OnAfterItemUpdate($event);
+
+ if ( $this->Application->isAdmin ) {
+ return ;
+ }
+
+ $object =& $event->getObject();
+ /* @var $object kDBItem */
+
+ $changed_fields = $object->GetChangedFields();
+
+ if ( $changed_fields ) {
+ $table_info = $object->getLinkedInfo();
+
+ $main_object =& $this->Application->recallObject( $table_info['ParentPrefix'] );
+ /* @var $main_object OrdersItem */
+
+ $oi_string = $object->GetDBField('ProductId') . ':' . $object->GetDBField('OptionsSalt') . ':' . $object->GetDBField('BackOrderFlag');
+
+ foreach ($changed_fields as $changed_field => $change_info) {
+ $error_code = OrderCheckoutError::FIELD_UPDATE_SUCCESS;
+
+ if ( $changed_field == 'ItemData' ) {
+ $item_data_old = unserialize( $change_info['old'] );
+ $item_data_new = unserialize( $change_info['new'] );
+
+ if ( $item_data_old['DiscountId'] != $item_data_new['DiscountId'] || $item_data_old['DiscountType'] != $item_data_new['DiscountType'] ) {
+ if ( $item_data_new['DiscountId'] > 0 ) {
+ $error_code = $item_data_new['DiscountType'] == 'discount' ? OrderCheckoutError::DISCOUNT_APPLIED : OrderCheckoutError::COUPON_APPLIED;
+ }
+ else {
+ $error_code = $item_data_old['DiscountType'] == 'discount' ? OrderCheckoutError::DISCOUNT_REMOVED : OrderCheckoutError::COUPON_REMOVED;
+ }
+ }
+
+ if ( $error_code == OrderCheckoutError::DISCOUNT_APPLIED || $error_code == OrderCheckoutError::DISCOUNT_REMOVED ) {
+ // set general error too
+ $main_object->setCheckoutError(OrderCheckoutErrorType::DISCOUNT, $error_code);
+ }
+ }
+ elseif ( $changed_field == 'Quantity' ) {
+ // here is how qty is changed:
+ // OLD QTY -> NEW QTY
+ // RECALCULATE
+ // NEW QTY = IN_STOCK_QTY
+ // NEW ORDER ITEM with LEFTOVER QTY
+ $this->Application->Debugger->appendTrace();
+ $this->Application->Debugger->appendHTML('QTY_CHANGE (' . $oi_string . '): ' . $change_info['old'] . ' => ' . $change_info['new']);
+ }
+
+ $main_object->setCheckoutError(OrderCheckoutErrorType::PRODUCT, $error_code, $oi_string . ':' . $changed_field);
+ }
+ }
+ }
+
+ /**
* Returns subtotal
*
* @param Array $items_info
* @return float
*/
function getSubTotal($items_info)
{
$sub_total = 0;
foreach ($items_info as $id => $field_values) {
if (!array_key_exists('Price', $field_values)) {
return false;
}
$sub_total += $field_values['Quantity'] * $field_values['Price'];
}
return $sub_total;
}
/**
* Returns total returned amount (refund)
*
* @param Array $items_info
* @return float
*/
function getReturnTotal($items_info)
{
$return_total = 0;
foreach ($items_info as $id => $field_values) {
$return_total += $field_values['ReturnAmount'];
}
return $return_total;
}
/**
* Saves selected items
*
* @param kEvent $event
*/
function OnSaveItems(&$event)
{
$event->CallSubEvent('OnUpdate');
$event->redirect = false;
$event->setRedirectParams(Array('opener'=>'s','pass'=>'all'), true);
}
/**
* Occures after an item has been cloned
* Id of newly created item is passed as event' 'id' param
*
* @param kEvent $event
*/
function OnAfterClone(&$event)
{
$id = $event->getEventParam('id');
$table = $this->Application->getUnitOption($event->Prefix,'TableName');
$id_field = $this->Application->getUnitOption($event->Prefix,'IDField');
$sql = 'UPDATE '.$table.' SET QuantityReserved = NULL WHERE '.$id_field.' = '.$id;
$this->Conn->Query($sql);
}
/**
* Occurs after loading item, 'id' parameter
* allows to get id of item that was loaded
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemLoad(&$event)
{
parent::OnAfterItemLoad($event);
$object =& $event->getObject();
/* @var $object kDBItem */
- if ( $item_info = $object->GetDBField('ItemData') ) {
+ $item_info = $object->GetDBField('ItemData');
+
+ if ( $item_info ) {
$item_info = unserialize($item_info);
$object->SetDBField('DiscountType', getArrayValue($item_info, 'DiscountType'));
$object->SetDBField('DiscountId', getArrayValue($item_info, 'DiscountId'));
}
}
/**
* Apply any custom changes to list's sql query
*
* @param kEvent $event
* @return void
* @access protected
* @see kDBEventHandler::OnListBuild()
*/
protected function SetCustomQuery(&$event)
{
parent::SetCustomQuery($event);
$object =& $event->getObject();
+ /* @var $object kDBList */
$package_num = $event->getEventParam('package_num');
- if ($package_num) $object->addFilter('package_num', 'PackageNum = '.$package_num);
+ if ( $package_num ) {
+ $object->addFilter('package_num', 'PackageNum = ' . $package_num);
+ }
$type = $event->getEventParam('product_type');
- if ($type) {
- $object->addFilter('product_type', 'p.Type ='.$type);
+ if ( $type ) {
+ $object->addFilter('product_type', 'p.Type =' . $type);
}
}
/**
* Checks, that currently loaded item is allowed for viewing (non permission-based)
*
* @param kEvent $event
* @return bool
*/
function checkItemStatus(&$event)
{
- if ($this->Application->IsAdmin()) {
+ if ( $this->Application->isAdmin ) {
return true;
}
$object =& $event->getObject();
- if (!$object->isLoaded()) {
+ /* @var $object kDBItem */
+
+ if ( !$object->isLoaded() ) {
return true;
}
$order =& $this->Application->recallObject('ord');
/* @var $order kDBItem */
- if ($order->isLoaded() && ($order->GetID() == $object->GetDBField('OrderId'))) {
+ if ( $order->isLoaded() && ($order->GetID() == $object->GetDBField('OrderId')) ) {
return $order->GetDBField('PortalUserId') == $this->Application->RecallVar('user_id');
}
return false;
}
}
\ No newline at end of file
Index: branches/5.2.x/units/order_items/order_items_config.php
===================================================================
--- branches/5.2.x/units/order_items/order_items_config.php (revision 14640)
+++ branches/5.2.x/units/order_items/order_items_config.php (revision 14641)
@@ -1,175 +1,176 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
$config = Array (
'Prefix' => 'orditems',
'ItemClass' => Array('class' => 'kDBItem', 'file' => '', 'build_event' => 'OnItemBuild'),
'ListClass' => Array('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'),
'EventHandlerClass' => Array('class' => 'OrderItemsEventHandler', 'file' => 'order_items_event_handler.php', 'build_event' => 'OnBuild'),
'TagProcessorClass' => Array('class' => 'OrderItemsTagProcessor', 'file' => 'order_items_tag_processor.php', 'build_event' => 'OnBuild'),
'AutoLoad' => true,
'AggregateTags' => Array (
Array (
'AggregateTo' => '#PARENT#',
'AggregatedTagName' => 'ItemFieldEquals',
'LocalTagName' => 'FieldEquals',
),
),
'Hooks' => Array (
Array (
'Mode' => hBEFORE,
'Conditional' => false,
'HookToPrefix' => '#PARENT#',
'HookToSpecial' => '',
'HookToEvent' => Array('OnPreSave', 'OnRecalculateItems'),
'DoPrefix' => '',
'DoSpecial' => '',
'DoEvent' => 'OnUpdate',
),
),
'QueryString' => Array (
1 => 'id',
2 => 'Page',
3 => 'PerPage',
4 => 'event',
),
'IDField' => 'OrderItemId',
'TitleField' => 'OrderItemId',
'StatusField' => Array ('Status'),
'TableName' => TABLE_PREFIX.'OrderItems',
'ParentTableKey' => 'OrderId',
'ForeignKey' => 'OrderId',
'ParentPrefix' => 'ord',
'AutoDelete' => true,
'AutoClone' => true,
'ItemType' => 11,
'CalculatedFields' => Array (
'' => Array (
'ExtendedPrice' => '%1$s.Price * %1$s.Quantity',
'ExtendedPriceFlat' => '%1$s.FlatPrice * %1$s.Quantity',
'QuantityAvailable' => 'IF( ISNULL(p.QtyInStock) AND ISNULL(p.ProductId),"!la_ProductDeleted!", IF(p.Type = 1, IF(p.InventoryStatus = 2, poc.QtyInStock, p.QtyInStock), "") )',
'ItemDiscount' => '(%1$s.FlatPrice - %1$s.Price)',
'SKU' => 'IF(p.InventoryStatus = 2 OR NOT ISNULL(poc.CombinationCRC), poc.SKU, p.SKU)', // inventory by options OR combination found
),
),
'ListSQLs' => Array (
'' => ' SELECT %1$s.*, p.* %2$s
FROM %1$s
LEFT JOIN '.TABLE_PREFIX.'Products p ON %1$s.ProductId = p.ProductId
LEFT JOIN '.TABLE_PREFIX.'ProductOptionCombinations poc ON (%1$s.ProductId = poc.ProductId) AND (%1$s.OptionsSalt = poc.CombinationCRC)',
),
'ItemSQLs' => Array (
'' => 'SELECT *, (Quantity*Price) AS ExtendedPrice, 0 AS QuantityAvailable FROM %s',
),
'ListSortings' => Array (
'' => Array (
'Sorting' => Array ('ProductName' => 'asc', 'BackOrderFlag' => 'asc'),
)
),
'Fields' => Array (
'OrderItemId' => Array('type' => 'int', 'not_null' => 1, 'default' => 0),
'OrderId' => Array('type' => 'int','not_null' => 1, 'default' => 0),
'ProductId' => Array('type' => 'int','not_null' => 1, 'default' => 0),
'ProductName' => Array('type' => 'string','not_null' => 1, 'default' => ''),
'Quantity' => Array('type' => 'int','not_null' => 1, 'default' => 0),
'QuantityReserved' => Array('type' => 'int', 'default' => null),
'FlatPrice' => Array('type' => 'double','formatter'=>'kFormatter','format'=>'%01.2f','not_null' => '1','default' => '0.0000'),
'Price' => Array('type' => 'double','formatter'=>'kFormatter','format'=>'%01.2f','not_null' => '1','default' => '0.0000'),
'Cost' => Array('type' => 'double','formatter'=>'kFormatter','format'=>'%01.2f','not_null' => '1','default' => '0.0000'),
'BackOrderFlag' => Array('type' => 'int', 'default' => 0),
'Weight' => Array ('type' => 'double', 'default' => NULL),
'ShippingTypeId' => Array ('type' => 'string', 'default' => NULL),
'ItemData' => Array('type' => 'string','default' => null),
'OptionsSalt' => Array('type' => 'int', 'default' => 0),
'SplitShippingGroup' => Array('type' => 'int', 'not_null' => 1, 'default' => 0,),
'PackageNum' => Array('type' => 'int', 'not_null' => 1, 'default' => 0,),
'ReturnType' => Array (
'type' => 'int',
'formatter' => 'kOptionsFormatter', 'options' => Array (1 => 'la_opt_Refund', 2 => 'la_opt_Exchange', 3 => 'la_opt_Warranty'), 'use_phrases' => 1,
'not_null' => 1, 'default' => 0
),
'ReturnAmount' => Array ('type' => 'float', 'formatter' => 'kFormatter', 'format' => '%01.2f', 'not_null' => 1, 'default' => 0),
'ReturnedOn' => Array ('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => NULL),
),
'VirtualFields' => Array (
'ExtendedPrice' => Array('type'=>'double','formatter'=>'kFormatter','format'=>'%01.2f','default'=>'0.00'),
'ExtendedPriceFlat' => Array('type'=>'double','formatter'=>'kFormatter','format'=>'%01.2f','default'=>'0.00'),
'QuantityAvailable' => Array('type'=>'int','default'=>0),
'DiscountType' => Array('type'=>'string','default'=>''),
'DiscountId' => Array('type'=>'int','default'=>0),
'Name' => Array('type' => 'string', 'formatter' => 'kMultiLanguage', 'default' => ''),
'ItemDiscount' => Array('type'=>'double','formatter'=>'kFormatter','format'=>'%01.2f','default'=>'0.00'),
'SKU' => Array('type' => 'string', 'default' => ''),
'MinQtyFreeShipping'=> Array('type' => 'int', 'default' => 0,),
+ 'Virtual' => Array ('type' => 'int', 'default' => 0),
),
'Grids' => Array (
'Default' => Array (
'Icons' => Array (
'default' => 'icon16_product.png',
0 => 'icon16_product_disabled.png',
1 => 'icon16_product.png',
2 => 'icon16_product_pending.png',
),
'Fields' => Array (
'ProductName' => Array ('title' => 'la_col_ProductNameId', 'data_block' => 'grid_productname_td', 'filter_block' => 'grid_like_filter'),
'Quantity' => Array ('title' => 'la_col_Quantity', 'data_block' => 'grid_quantity_td', 'filter_block' => 'grid_range_filter'),
'QuantityReserved' => Array ('title' => 'la_col_QuantityReserved', 'filter_block' => 'grid_range_filter'),
'QuantityAvailable' => Array ('title' => 'la_col_QuantityAvailable', 'filter_block' => 'grid_range_filter'),
'Price' => Array ('data_block' => 'grid_price_td', 'filter_block' => 'grid_float_range_filter'),
'ExtendedPrice' => Array ('title' => 'la_col_ExtendedPrice', 'data_block' => 'grid_extendedprice_td', 'filter_block' => 'grid_float_range_filter'),
'ReturnType' => Array ('title' => 'la_col_ReturnType', 'data_block' => 'grid_options_td', 'filter_block' => 'grid_options_filter'),
'ReturnAmount' => Array ('title' => 'la_col_ReturnAmount', 'data_block' => 'grid_edit_td', 'filter_block' => 'grid_float_range_filter'),
'ReturnedOn' => Array ('title' => 'la_col_ReturnedOn', 'data_block' => 'grid_date_td', 'filter_block' => 'grid_date_range_filter'),
),
),
'NotEditable' => Array (
'Icons' => Array (
'default' => 'icon16_product.png',
0 => 'icon16_product_disabled.png',
1 => 'icon16_product.png',
2 => 'icon16_product_pending.png',
),
'Fields' => Array (
'ProductName' => Array ('title' => 'la_col_ProductNameId', 'data_block' => 'grid_productname_td'),
'Quantity' => Array ('title' => 'la_col_Quantity', 'filter_block' => 'grid_range_filter'),
'QuantityReserved' => Array ('title' => 'la_col_QuantityReserved', 'filter_block' => 'grid_range_filter'),
'QuantityAvailable' => Array ('title' => 'la_col_QuantityAvailable', 'filter_block' => 'grid_range_filter'),
'Price' => Array ('filter_block' => 'grid_float_range_filter'),
'ExtendedPrice' => Array ('title' => 'la_col_ExtendedPrice', 'filter_block' => 'grid_float_range_filter'),
'ReturnType' => Array ('title' => 'la_col_ReturnType', 'data_block' => 'grid_options_td', 'filter_block' => 'grid_options_filter'),
'ReturnAmount' => Array ('title' => 'la_col_ReturnAmount', 'data_block' => 'grid_edit_td', 'filter_block' => 'grid_float_range_filter'),
'ReturnedOn' => Array ('title' => 'la_col_ReturnedOn', 'data_block' => 'grid_date_td', 'filter_block' => 'grid_date_range_filter'),
),
),
),
);
\ No newline at end of file
Index: branches/5.2.x/units/order_items/order_items_tag_processor.php
===================================================================
--- branches/5.2.x/units/order_items/order_items_tag_processor.php (revision 14640)
+++ branches/5.2.x/units/order_items/order_items_tag_processor.php (revision 14641)
@@ -1,258 +1,297 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class OrderItemsTagProcessor extends kDBTagProcessor
{
function PrintGrid($params)
{
-
$order =& $this->Application->recallObject('ord');
- if ($order->GetDBField('Status') != ORDER_STATUS_INCOMPLETE) {
+ /* @var $order kDBList */
+
+ if ( $order->GetDBField('Status') != ORDER_STATUS_INCOMPLETE ) {
$params['grid'] = $params['NotEditable'];
}
else {
$params['grid'] = $params['Editable'];
}
return $this->Application->ProcessParsedTag('m', 'ParseBlock', $params);
}
- function IsTangible($params){
- $object =& $this->Application->recallObject( $this->getPrefixSpecial() );
- if ($object->GetDBField('Type') == 1)
- return true;
- else
- return false;
+ function IsTangible($params)
+ {
+ $object =& $this->getObject($params);
+ /* @var $object kDBItem */
+
+ return $object->GetDBField('Type') == PRODUCT_TYPE_TANGIBLE;
}
function HasQty($params)
{
- $object =& $this->Application->recallObject( $this->getPrefixSpecial() );
- $type = $object->GetDBField('Type');
- return in_array($type, array(1,6));
+ $object =& $this->getObject($params);
+ /* @var $object kDBItem */
+
+ return in_array($object->GetDBField('Type'), Array (PRODUCT_TYPE_TANGIBLE, 6));
}
function HasDiscount($params)
{
- $object =& $this->Application->recallObject( $this->getPrefixSpecial() );
+ $object =& $this->getObject($params);
+ /* @var $object kDBItem */
+
return (float)$object->GetDBField('ItemDiscount') ? 1 : 0;
}
function HasOptions($params)
{
$object =& $this->getObject($params);
$item_data = @unserialize($object->GetDBField('ItemData'));
return isset($item_data['Options']);
}
function PrintOptions($params)
{
$object =& $this->getObject($params);
+ /* @var $object kDBItem */
+
$item_data = @unserialize($object->GetDBField('ItemData'));
$render_as = $this->SelectParam($params, 'render_as');
$block_params['name'] = $render_as;
$opt_helper =& $this->Application->recallObject('kProductOptionsHelper');
+ /* @var $opt_helper kProductOptionsHelper */
$o = '';
$options = $item_data['Options'];
foreach ($options as $opt => $val) {
- if (!is_array($val)) {
+ if ( !is_array($val) ) {
$val = kUtil::unhtmlentities($val);
}
$key_data = $opt_helper->ConvertKey($opt, $object->GetDBField('ProductId'));
$parsed = $opt_helper->ExplodeOptionValues($key_data);
- if ($parsed) {
+ if ( $parsed ) {
$values = $parsed['Values'];
$prices = $parsed['Prices'];
$price_types = $parsed['PriceTypes'];
}
else {
- $values = array();
- $prices = array();
- $price_types = array();
+ $values = array ();
+ $prices = array ();
+ $price_types = array ();
}
$key = $key_data['Name'];
/*if (is_array($val)) {
$val = join(',', $val);
}*/
$lang =& $this->Application->recallObject('lang.current');
+ /* @var $lang LanguagesItem */
- if ($render_as) {
+ if ( $render_as ) {
$block_params['option'] = $key;
- if (is_array($val)) {
+ if ( is_array($val) ) {
$block_params['value'] = $val;
$block_params['type'] = $key_data['OptionType'];
$block_params['price'] = $prices;
$block_params['price_type'] = $price_types;
}
else {
$price_type = array_key_exists($val, $price_types) ? $price_types[$val] : '';
$price = array_key_exists($val, $prices) ? $prices[$val] : '';
- if ($price_type == '$') {
+ if ( $price_type == '$' ) {
$iso = $this->GetISO($params['currency']);
- $value = $this->AddCurrencySymbol($lang->formatNumber($this->ConvertCurrency($price_type, $iso),2), $iso, true); // true to force sign
+ $value = $this->AddCurrencySymbol($lang->formatNumber($this->ConvertCurrency($price_type, $iso), 2), $iso, true); // true to force sign
$block_params['price'] = $value;
$block_params['price_type'] = '';
$block_params['sign'] = ''; // sign is included in the formatted value
}
else {
$block_params['price'] = $price;
$block_params['price_type'] = $price_type;
$block_params['sign'] = $price >= 0 ? '+' : '-';
}
$block_params['value'] = htmlspecialchars($val);
$block_params['type'] = $key_data['OptionType'];
}
- $o.= $this->Application->ParseBlock($block_params, 1);
+ $o .= $this->Application->ParseBlock($block_params, 1);
}
else {
- $o .= $key.': '.$val.'<br>';
+ $o .= $key . ': ' . $val . '<br>';
}
}
return $o;
}
function ProductsInStock($params)
{
$object =& $this->getObject($params);
if (!$object->GetDBField('InventoryStatus')) {
// unlimited count available
return false;
}
if ($object->GetDBField('InventoryStatus') == 2) {
$poc_table = $this->Application->getUnitOption('poc', 'TableName');
$sql = 'SELECT QtyInStock
FROM '.$poc_table.'
WHERE (ProductId = '.$object->GetDBField('ProductId').') AND (Availability = 1) AND (CombinationCRC = '.$object->GetDBField('OptionsSalt').')';
$ret = $this->Conn->GetOne($sql);
}
else {
$ret = $object->GetDBField('QtyInStock');
}
return $ret;
}
function PrintOptionValues($params)
{
$block_params['name'] = $params['render_as'];
$values = $this->Application->Parser->GetParam('value');
+ /* @var $values Array */
+
$prices = $this->Application->Parser->GetParam('price');
$price_types = $this->Application->Parser->GetParam('price_type');
$o = '';
$i = 0;
foreach ($values as $val) {
$i++;
$val = kUtil::unhtmlentities($val);
$block_params['value'] = htmlspecialchars($val);
if ($price_types[$val] == '$') {
$iso = $this->GetISO($params['currency']);
$value = $this->AddCurrencySymbol(sprintf("%.2f", $this->ConvertCurrency($prices[$val], $iso)), $iso, true); // true to force sign
$block_params['price'] = $value;
$block_params['price_type'] = '';
$block_params['sign'] = ''; // sign is included in the formatted value
}
else {
$block_params['price'] = $prices[$val];
$block_params['price_type'] = $price_types[$val];
$block_params['sign'] = $prices[$val] >= 0 ? '+' : '-';
}
$block_params['is_last'] = $i == count($values);
$o.= $this->Application->ParseBlock($block_params, 1);
}
return $o;
}
/*function ConvertKey($key, &$object)
{
static $mapping = null;
if (is_null($mapping) || !isset($mapping[$object->GetDBField('ProductId')])) {
$table = TABLE_PREFIX.'ProductOptions';
$sql = 'SELECT * FROM '.$table.' WHERE ProductId = '.$object->GetDBField('ProductId');
$mapping[$object->GetDBField('ProductId')] = $this->Conn->Query($sql, 'ProductOptionId');
}
return $mapping[$object->GetDBField('ProductId')][$key];
}*/
function PrintList($params)
{
$list =& $this->GetList($params);
$id_field = $this->Application->getUnitOption($this->Prefix, 'IDField');
$list->Query();
$o = '';
$list->GoFirst();
$block_params=$this->prepareTagParams($params);
$block_params['name'] = $this->SelectParam($params, 'render_as,block');
$block_params['pass_params'] = 'true';
$product_object =& $this->Application->recallObject('p', 'p', Array('skip_autoload' => true));
/* @var $product_object kCatDBItem */
$i = 0;
$product_id = $product_object->GetID();
$product_id_get = $this->Application->GetVar('p_id');
while (!$list->EOL()) {
// load product used in orderitem
$this->Application->SetVar( $this->getPrefixSpecial().'_id', $list->GetDBField($id_field) ); // for edit/delete links using GET
$this->Application->SetVar('p_id', $list->GetDBField('ProductId'));
$product_object->Load( $list->GetDBField('ProductId') ); // correct product load
$this->Application->SetVar('m_cat_id', $product_object->GetDBField('CategoryId'));
- $block_params['is_last'] = ($i == $list->SelectedCount - 1);
+ $block_params['is_last'] = ($i == $list->GetSelectedCount() - 1);
$o.= $this->Application->ParseBlock($block_params, 1);
$list->GoNext();
$i++;
}
// restore IDs used in cycle
$this->Application->SetVar('p_id', $product_id_get);
$this->Application->DeleteVar($this->getPrefixSpecial().'_id');
$product_object->Load($product_id);
return $o;
}
function DisplayOptionsPricing($params)
{
$object =& $this->getObject($params);
- if ($object->GetDBField('OptionsSelectionMode') == 1) {
+ /* @var $object kDBItem */
+
+ if ( $object->GetDBField('OptionsSelectionMode') == 1 ) {
return false;
}
$item_data = unserialize($object->GetDBField('ItemData'));
- if (!is_array($item_data)) return false;
+ if ( !is_array($item_data) ) {
+ return false;
+ }
+
$options = getArrayValue($item_data, 'Options');
$helper =& $this->Application->recallObject('kProductOptionsHelper');
+ /* @var $helper kProductOptionsHelper */
+
$crc = $helper->OptionsSalt($options, true);
- $combs = $this->Conn->GetOne('SELECT COUNT(*) FROM '.TABLE_PREFIX.'ProductOptionCombinations WHERE CombinationCRC = '.$crc.' AND ProductId = '.$object->GetDBField('ProductId').' AND (Price != 0 OR (PriceType = 1 AND Price = 0))');
- return $combs == 0; // no overriding combinations found
+ $sql = 'SELECT COUNT(*)
+ FROM ' . TABLE_PREFIX . 'ProductOptionCombinations
+ WHERE CombinationCRC = ' . $crc . ' AND ProductId = ' . $object->GetDBField('ProductId') . ' AND (Price != 0 OR (PriceType = 1 AND Price = 0))';
+
+ return $this->Conn->GetOne($sql) == 0; // no overriding combinations found
+ }
+
+ function RowIndex($params)
+ {
+ $object =& $this->getObject($params);
+ /* @var $object kDBItem */
+
+ return $object->GetDBField('ProductId') . ':' . $object->GetDBField('OptionsSalt') . ':' . $object->GetDBField('BackOrderFlag');
+ }
+
+ function FreePromoShippingAvailable($params)
+ {
+ $object =& $this->getObject($params);
+ /* @var $object kDBItem */
+
+ $order_helper =& $this->Application->recallObject('OrderHelper');
+ /* @var $order_helper OrderHelper */
+
+ return $order_helper->eligibleForFreePromoShipping($object);
}
}
\ No newline at end of file
Index: branches/5.2.x/units/gift_certificates/gift_certificates_eh.php
===================================================================
--- branches/5.2.x/units/gift_certificates/gift_certificates_eh.php (revision 14640)
+++ branches/5.2.x/units/gift_certificates/gift_certificates_eh.php (revision 14641)
@@ -1,238 +1,197 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class GiftCertificateEventHandler extends kDBEventHandler {
/**
* Allows to override standart permission mapping
*
*/
function mapPermissions()
{
parent::mapPermissions();
$permissions = Array(
'OnItemBuild' => Array('self' => true),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
/**
- * Enter description here...
- *
- * @param kEvent $event
- */
- function OnApplyGiftCertificate(&$event)
- {
- $code = $this->Application->GetVar('giftcert_code');
- if ($code == '') {
- return ;
- }
-
- $object =& $event->getObject(Array('skip_autoload' => true));
- $object->Load($code, 'Code');
-
- if (!$object->isLoaded()) {
- $event->status = kEvent::erFAIL;
- $this->Application->SetVar('set_checkout_error', 104);
- $event->redirect = false; // check!!!
- return ;
- }
-
- $expire_date = $object->GetDBField('Expiration');
- $debit = $object->GetDBField('Debit');
- if( $object->GetDBField('Status') != 1 || ($expire_date && $expire_date < adodb_mktime()) ||
- ($debit <= 0))
- {
- $event->status = kEvent::erFAIL;
- $this->Application->SetVar('set_checkout_error', 105);
- $event->redirect->false;
- return ;
- }
-
- $this->Application->setUnitOption('ord', 'AutoLoad', true);
- $order =& $this->Application->recallObject('ord');
- $order->SetDBField('GiftCertificateId', $object->GetDBField('GiftCertificateId'));
- $order->Update();
-
- $this->Application->SetVar('set_checkout_error', 110);
- }
-
- /**
* Prepare temp tables for creating new item
* but does not create it. Actual create is
* done in OnPreSaveCreated
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreCreate(&$event)
{
parent::OnPreCreate($event);
$object =& $event->getObject();
/* @var $object kDBItem */
$exp_date = adodb_mktime();
$default_duration = $this->Application->ConfigValue('Comm_DefaultCouponDuration');
if ( $default_duration ) {
$exp_date += (int)$default_duration * 86400;
}
$object->SetDBField('Expiration_date', $exp_date);
}
/**
* Occurs before updating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemUpdate(&$event)
{
parent::OnBeforeItemUpdate($event);
$this->itemChanged($event);
}
/**
* Occurs before creating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemCreate(&$event)
{
parent::OnBeforeItemCreate($event);
$this->itemChanged($event);
$object =& $event->getObject();
/* @var $object kDBItem */
$object->SetDBField('Debit', $object->GetDBField('Amount'));
}
/**
* Occurs before item is changed
*
* @param kEvent $event
*/
function itemChanged(&$event)
{
$object =& $event->getObject();
/* @var $object kDBItem */
$amount = abs($object->GetDBField('Amount'));
$debit = abs($object->GetDBField('Debit'));
if ( $debit > $amount ) {
$debit = $amount;
}
$object->SetDBField('Amount', $amount);
$object->SetDBField('Debit', $debit);
if ( $object->GetDBField('SendVia') == 1 ) {
// by postal mail
if ( $this->Application->GetVar('email_certificate') == 0 ) {
$object->setRequired('RecipientEmail', false);
}
$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
if ( !$cs_helper->CountryHasStates($object->GetDBField('RecipientCountry')) ) {
$object->setRequired('RecipientState', false);
}
$cs_helper->CheckStateField($event, 'RecipientState', 'RecipientCountry');
}
else {
$non_required_fields = Array (
'RecipientState', 'RecipientCity', 'RecipientCountry', 'RecipientZipcode',
'RecipientAddress1', 'RecipientFirstname', 'RecipientLastname'
);
$object->setRequired($non_required_fields, false);
}
}
/**
* Print current gift certificate
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnSave(&$event)
{
parent::OnSave($event);
if ( $event->status != kEvent::erSUCCESS || !$this->Application->GetVar('print_certificate') ) {
return ;
}
$object =& $event->getObject();
/* @var $object kDBItem */
// get object id by unique field Code
$sql = 'SELECT ' . $object->IDField . '
FROM ' . $this->Application->GetLiveName($object->TableName) . '
WHERE Code = ' . $this->Conn->qstr( $object->GetDBField('Code') );
$id = $this->Conn->GetOne($sql);
$this->Application->StoreVar('print_certificate_id', $id);
}
/**
* Email selected gift certificate
*
* @param kEvent $event
*/
function OnEmailGiftCertificate(&$event)
{
$ids = $this->StoreSelectedIDs($event);
if (!$ids) {
return ;
}
$object =& $event->getObject( Array ('skip_autoload' => true) );
/* @var $object kDBItem */
foreach ($ids as $id) {
$object->Load($id);
$send_params = Array (
'from_email' => $this->Application->ConfigValue('Smtp_AdminMailFrom'),
'from_name' => $object->GetDBField('Purchaser'),
'to_email' => $object->GetDBField('RecipientEmail'),
'to_name' => $object->GetDBField('Recipient'),
'message' => $object->GetDBField('Message'),
'amount' => $object->GetField('Amount'),
'gifcert_id' => $object->GetDBField('Code'),
);
$this->Application->EmailEventUser('USER.GIFTCERTIFICATE', null, $send_params);
$this->Application->EmailEventAdmin('USER.GIFTCERTIFICATE', null, $send_params);
}
$this->clearSelectedIDs($event);
}
}
\ No newline at end of file
Index: branches/5.2.x/units/gift_certificates/gift_certificates_config.php
===================================================================
--- branches/5.2.x/units/gift_certificates/gift_certificates_config.php (revision 14640)
+++ branches/5.2.x/units/gift_certificates/gift_certificates_config.php (revision 14641)
@@ -1,138 +1,125 @@
<?php
/**
* @version $Id$
* @package In-Commerce
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license Commercial License
* This software is protected by copyright law and international treaties.
* Unauthorized reproduction or unlicensed usage of the code of this program,
* or any portion of it may result in severe civil and criminal penalties,
* and will be prosecuted to the maximum extent possible under the law
* See http://www.in-portal.org/commercial-license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
$config = Array (
'Prefix' => 'gc',
'ItemClass' => Array ('class' => 'kDBItem', 'file' => '', 'build_event' => 'OnItemBuild'),
'ListClass' => Array ('class' => 'kDBList', 'file' => '', 'build_event' => 'OnListBuild'),
'EventHandlerClass' => Array ('class' => 'GiftCertificateEventHandler', 'file' => 'gift_certificates_eh.php', 'build_event' => 'OnBuild'),
'TagProcessorClass' => Array ('class' => 'GiftCertificateTagProcessor', 'file' => 'gift_certificates_tp.php', 'build_event' => 'OnBuild'),
'AutoLoad' => true,
- 'Hooks' => Array (
- Array (
- 'Mode' => hBEFORE,
- 'Conditional' => false,
- 'HookToPrefix' => 'ord',
- 'HookToSpecial' => '',
- 'HookToEvent' => Array( 'OnUpdateCart', 'OnCheckout' ),
- 'DoPrefix' => '',
- 'DoSpecial' => '',
- 'DoEvent' => 'OnApplyGiftCertificate',
- ),
- ),
-
'QueryString' => Array (
1 => 'id',
2 => 'Page',
3 => 'PerPage',
4 => 'event',
5 => 'mode',
),
'IDField' => 'GiftCertificateId',
'StatusField' => Array('Status'),
'TitleField' => 'Recipient',
'TableName' => TABLE_PREFIX.'GiftCertificates',
'TitlePresets' => Array (
'default' => Array (
'new_status_labels' => Array ('gc' => '!la_title_AddingGiftCertificate!'),
'edit_status_labels' => Array ('gc' => '!la_title_EditingGiftCertificate!'),
'new_titlefield' => Array ('gc' => '!la_title_NewGiftCertificate!'),
),
'gift_certificates_list' => Array ('prefixes' => Array ('gc_List'), 'format' => "!la_title_GiftCertificates!",),
'gift_certificates_edit' => Array ('prefixes' => Array ('gc'), 'format' => "#gc_status# '#gc_titlefield#' - !la_title_General!",),
),
'PermSection' => Array ('main' => 'in-commerce:gift-certificates'),
'Sections' => Array (
'in-commerce:gift-certificates' => Array (
'parent' => 'in-commerce:discounts_folder',
'icon' => 'discounts_coupons',
'label' => 'la_tab_GiftCertificates',
'url' => Array('t' => 'in-commerce/discounts/gift_certificate_list', 'pass' => 'm'),
'permissions' => Array('view', 'add', 'edit', 'delete', 'advanced:approve', 'advanced:decline'),
'priority' => 3.3, // <parent_priority>.<own_priority>, because this section replaces parent in tree
'type' => stTAB,
),
),
'ListSQLs' => Array ('' => 'SELECT %1$s.* %2$s FROM %1$s',),
'ItemSQLs' => Array ('' => 'SELECT * FROM %1$s',),
'ListSortings' => Array (
'' => Array(
'Sorting' => Array('Recipient' => 'asc'),
)
),
'Fields' => Array (
'GiftCertificateId' => Array('type' => 'int', 'not_null' => 1, 'default' => 0),
'OrderId' => Array ('type' => 'int', 'default' => 0),
'Status' => Array('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array ( 1 => 'la_Enabled', 2 => 'la_Used', 0 => 'la_Disabled' ), 'use_phrases' => 1, 'not_null' => 1, 'default' => 2 ),
'SendVia' => Array('type' => 'int', 'formatter' => 'kOptionsFormatter', 'options' => Array ( 0 => 'la_opt_Email', 1 => 'la_opt_PostalMail'), 'use_phrases' => 1, 'not_null' => 1, 'default' => 0 ),
'Purchaser' => Array('type'=>'string','required'=>1,'default'=>null, 'max_len'=>255),
'Recipient' => Array('type'=>'string','required'=>1,'default'=>null, 'max_len'=>255),
'RecipientEmail' => Array('type' => 'string', 'formatter'=>'kFormatter', 'regexp'=>'/^(' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . ')$/i', 'sample_value' => 'email@domain.com', 'not_null' => '1', 'required'=>1, 'default' => '', 'error_msgs' => Array('invalid_format'=>'!la_invalid_email!') ),
'RecipientFirstname' => Array('type'=>'string','required'=>1,'default'=>null, 'max_len'=>255),
'RecipientLastname' => Array('type'=>'string','required'=>1,'default'=>null, 'max_len'=>255),
'RecipientAddress1' => Array('type'=>'string','required'=>1,'default'=>null, 'max_len'=>255),
'RecipientAddress2' => Array('type'=>'string', 'default'=>null, 'max_len'=>255),
'RecipientCity' => Array('type'=>'string','required'=>1,'default'=>null, 'max_len'=>255),
'RecipientState' => Array('type'=>'string','required'=>1,'default'=>null, 'max_len'=>255),
'RecipientZipcode' => Array('type'=>'string','required'=>1,'default'=>null, 'max_len'=>255),
'RecipientCountry' => Array(
'type' => 'string',
'formatter' => 'kOptionsFormatter',
'options_sql' => ' SELECT IF(l%2$s_Name = "", l%3$s_Name, l%2$s_Name) AS Name, IsoCode
FROM '.TABLE_PREFIX.'CountryStates
WHERE Type = ' . DESTINATION_TYPE_COUNTRY . '
ORDER BY Name',
'option_key_field' => 'IsoCode', 'option_title_field' => 'Name',
'not_null' => 1, 'required' => 1, 'default' => 'USA'
),
'RecipientPhone' => Array('type'=>'string','default'=>null, 'max_len'=>255),
'Message' => Array('type' => 'string', 'formatter' => 'kFormatter', 'using_fck' => 1, 'default' => null),
'Code' => Array('type'=>'string','required'=>1,'default'=>null, 'max_len'=>255, 'unique'=>Array('Code')),
'AddDate' => Array('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => '#NOW#',),
'Expiration' => Array('type' => 'int', 'formatter' => 'kDateFormatter', 'default' => null,),
'Amount' => Array('type'=>'double', 'default' => null, 'required' => 1, 'min_value_exc' => 0),
'Debit' => Array('type'=>'double', 'default' => null),
),
'Grids' => Array (
'Default' => Array (
'Icons' => Array(
'default' => 'icon16_item.png',
0 => 'icon16_disabled.png',
1 => 'icon16_item.png',
2 => 'icon16_pending.png',
'module' => 'core',
),
'Fields' => Array (
'GiftCertificateId' => Array ('title' => 'column:la_fld_Id', 'data_block' => 'grid_checkbox_td', 'filter_block' => 'grid_range_filter', 'width' => 60, ),
'Code' => Array ('filter_block' => 'grid_like_filter', 'width' => 100, ),
'Status' => Array ('filter_block' => 'grid_options_filter', 'width' => 100, ),
'Debit' => Array ('title' => 'column:la_fld_RemainingAmount', 'filter_block' => 'grid_float_range_filter', 'width' => 160, ),
'Amount' => Array ('filter_block' => 'grid_float_range_filter', 'width' => 100, ),
'Expiration' => Array ('filter_block' => 'grid_date_range_filter', 'width' => 145, ),
),
),
),
);
\ No newline at end of file

Event Timeline