Page MenuHomeIn-Portal Phabricator

orders_event_handler.php
No OneTemporary

File Metadata

Created
Thu, Oct 29, 7:17 AM

orders_event_handler.php

<?php
class OrdersEventHandler extends InpDBEventHandler
{
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();
$object->IgnoreValidation = true;
$event->CallSubEvent('OnPreSave');
$object->IgnoreValidation = false;
}
/**
* Sets new address to order
*
* @param kEvent $event
*/
function OnSelectAddress(&$event)
{
if( $this->Application->IsAdmin() ) return true;
$object = &$event->getObject();
$this->Application->setUnitOption('addr','AutoLoad',false);
$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');
$address =& $this->Application->recallObject('addr.-item','addr');
$addr_list =& $this->Application->recallObject('addr', 'addr_List', Array('per_page'=>-1, 'skip_counting'=>true) );
$addr_list->prepareAddresses();
$object =& $event->getObject();
}
if($shipping_address_id)
{
$addr_list->CopyAddress($shipping_address_id, 'shipping');
$address->Load($shipping_address_id);
$address->MarkAsShipping();
$cs_helper->PopulateStates($event, 'ShippingState', 'ShippingCountry');
}
if($billing_address_id)
{
$addr_list->CopyAddress($billing_address_id, 'billing');
$address->Load($billing_address_id);
$address->MarkAsBilling();
$cs_helper->PopulateStates($event, 'BillingState', 'BillingCountry');
}
if($shipping_address_id || $billing_address_id) $this->Application->setUnitOption('addr','AutoLoad',true);
$event->redirect = false;
}
/**
* Updates order with registred user id
*
* @param kEvent $event
*/
function OnUserCreate(&$event)
{
if( !($event->MasterEvent->status == 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');
}
}
function OnUserLogin(&$event)
{
if( !($event->MasterEvent->status == erSUCCESS) ) return false;
$ses_id = $this->Application->RecallVar('ord_id');
if($ses_id) $this->updateUserID($ses_id, $event);
$user_id = $this->Application->GetVar('u_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->GetVar('u_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') : false;
}
/**
* Completes order if possible
*
* @param kEvent $event
* @return bool
*/
function OnCompleteOrder(&$event)
{
$this->LockTables();
if (!$this->CheckQuantites($event)) return;
$this->ReserveItems($event);
$order =& $event->getObject();
$gw_data = $order->getGatewayData();
$this->Application->registerClass( $gw_data['ClassName'], GW_CLASS_PATH.'/'.$gw_data['ClassFile'] );
$gateway_object =& $this->Application->recallObject( $gw_data['ClassName'] );
$payment_result = $gateway_object->DirectPayment($order->FieldValues, $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() );
if(!$payment_result)
{
$this->FreeItems($event);
$gw_responce = $gateway_object->parseGWResponce(null, $gw_data['gw_params']);
$this->Application->StoreVar('gw_error', $gw_responce['responce_reason_text'] );
$event->redirect = $this->Application->GetVar('failure_template');
return false;
}
$billing_email = $order->GetDBField('BillingEmail');
$user_email = $this->Conn->GetOne(' SELECT Email FROM '.$this->Application->getUnitOption('u', 'TableName').'
WHERE PortalUserId = '.$this->Application->GetVar('u_id'));
$email_params = Array();
$email_params['to_email'] = $billing_email ? $billing_email : $user_email;
$email_params['to_name'] = $order->GetDBField('BillingTo');
$email_event_user =& $this->Application->EmailEventUser('ORDER.SUBMIT', $order->GetDBField('PortalUserId'), $email_params);
$email_event_admin =& $this->Application->EmailEventAdmin('ORDER.SUBMIT');
$shipping_control = getArrayValue($gw_data, 'gw_params', 'shipping_control');
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->IsAdmin() )
{
// 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');
}
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');
$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');
}
function OnProceedToPreview(&$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 = $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->redirect_params['checkout_error']);
}
else
{
$object =& $event->getObject();
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->redirect_params['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->Conn->GetOne('SELECT PaymentTypeId FROM '.TABLE_PREFIX.'PaymentTypes WHERE IsPrimary = 1');
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');
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnAfterItemUpdate(&$event)
{
$object =& $event->getObject();
$cvv2 = $object->GetDBField('PaymentCVV2');
if($cvv2 !== false) $this->Application->StoreVar('CVV2Code', $cvv2);
$this->CreateMissingAddresses($event);
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnUpdate(&$event)
{
$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
$object =& $event->getObject();
if( $object->HasTangibleItems() )
{
$cs_helper->CheckStateField($event, 'ShippingState', 'ShippingCountry');
}
$cs_helper->CheckStateField($event, 'BillingState', 'BillingCountry');
parent::OnUpdate($event);
if( $this->Application->IsAdmin() )
{
return ;
}
else
{
$event->redirect_params = Array('opener' => 's');
}
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnPreSave(&$event)
{
$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
$object =& $event->getObject();
if ( $object->GetID() !== false)
{
if( $object->HasTangibleItems() )
{
$cs_helper->CheckStateField($event, 'ShippingState', 'ShippingCountry');
}
$cs_helper->CheckStateField($event, 'BillingState', 'BillingCountry');
}
parent::OnPreSave($event);
}
function OnUpdateCart(&$event)
{
$this->Application->HandleEvent($items_event, 'orditems:OnUpdate');
return $event->CallSubEvent('OnRecalculateItems');
}
/**
* Adds item to cart
*
* @param kEvent $event
*/
function OnAddToCart(&$event)
{
$this->StoreContinueShoppingLink();
$item_id = $this->Application->GetVar('p_id');
$item_data = Array();
$this->Application->setUnitOption('p', 'AutoLoad', false);
$product =& $this->Application->recallObject('p');
$product->Load($item_id);
if( $product->GetDBField('Type') == 2 ) // subscriptions
{
$item_data = $this->BuildSubscriptionItemData($item_id);
$event->setEventParam('ItemData', serialize($item_data) );
}
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['PackageContent'] = $product->GetPackageContentIds();
$item_data['PackageItemsItemData'] = $package_item_data;
$event->setEventParam('ItemData', serialize($item_data) );
}
$this->AddItemToOrder($event, $item_id);
}
function BuildSubscriptionItemData($item_id){
$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');
$sql = 'SELECT AccessDuration, AccessUnit FROM %s WHERE %s = %s';
$pricing_id = $this->GetPricingId($item_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 => 2419200, 7 => 29030400);
$item_data['Duration'] = $pricing_info['AccessDuration'] * $unit_secs[ $pricing_info['AccessUnit'] ];
return $item_data;
}
function OnRemoveCoupon(&$event)
{
$object =& $event->getObject();
$this->RemoveCoupon($object);
$event->CallSubEvent('OnRecalculateItems');
$event->SetRedirectParam('checkout_error', 7);
}
function RemoveCoupon(&$object)
{
$coupon_id = $object->GetDBField('CouponId');
$this->Application->setUnitOption('coup', 'AutoLoad', false);
$coupon =& $this->Application->recallObject('coup');
$res = $coupon->Load($coupon_id);
$uses = $coupon->GetDBField('NumberOfUses');
if($res && isset($uses))
{
$coupon->SetDBField('NumberOfUses', $uses + 1);
$coupon->SetDBField('Status', 1);
$coupon->Update();
}
$object->SetDBField('CouponId', 0);
$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->redirect = $this->Application->GetVar('shopcart_template');
$event->setEventParam('ItemData', serialize($item_data));
$this->AddItemToOrder($event, $item_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();
$ord_id = $this->getPassedId($event);
$this->Conn->Query('DELETE FROM '.TABLE_PREFIX.'OrderItems WHERE OrderId = '.$ord_id);
$this->RemoveCoupon($object);
$this->OnRecalculateItems($event);
}
function getPassedId(&$event)
{
$passed = parent::getPassedID($event);
if( $this->Application->IsAdmin() ) return $passed;
$ses_id = $this->Application->RecallVar( $event->getPrefixSpecial(true).'_id' );
if ( $passed && ($passed != $ses_id) )
{
$query = 'SELECT PortalUserId FROM '.TABLE_PREFIX.'Orders WHERE OrderId = '.$passed;
$user_id = $this->Conn->GetOne($query);
if ($user_id != $this->Application->GetVar('u_id'))
{
$this->Application->SetVar($event->getPrefixSpecial().'_id', 0);
return 0;
}
else
{
return $passed;
}
}
else // not passed or equals to ses_id
{
if (!$ses_id)
{
$this->CreateNewCart($event);
return $this->Application->RecallVar($event->getPrefixSpecial(true).'_id');
}
else
{
return $ses_id;
}
}
return $passed;
}
function CreateNewCart(&$event)
{
$this->Application->setUnitOption($event->Prefix,'AutoLoad',false);
$object =& $event->getObject();
$new_number = $this->getNextOrderNumber($event);
$object->SetDBField('Number', $new_number);
$object->SetDBField('SubNumber', 0);
$object->SetDBField('Type', 0); //incomplete
$object->SetDBField('VisitId', $this->Application->RecallVar('visit_id') );
$user_id = $this->Application->RecallVar('user_id');
if (!$user_id) $user_id = -2; //Guest
$object->SetDBField('PortalUserId', $user_id);
$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 ? $this->Application->RecallVar('affiliate_id') : $this->Application->GetVar('affiliate_id') );
}
$default_type = $this->Conn->GetOne('SELECT PaymentTypeId FROM '.TABLE_PREFIX.'PaymentTypes WHERE IsPrimary = 1');
if($default_type)
{
$object->SetDBField('PaymentType', $default_type);
}
$object->Create();
$this->Application->SetVar($event->getPrefixSpecial(true).'_id', $object->GetId());
$this->Application->StoreVar($event->getPrefixSpecial(true).'_id', $object->GetId());
}
function StoreContinueShoppingLink()
{
$last_t = $this->Application->recallVar('last_env');
$this->Application->StoreVar('continue_shopping', $last_t);
}
function SetStepRequiredFields(&$event)
{
$order =& $event->getObject();
// !!! Do not use switch here, since all cases may be on the same form simultaniously
$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
if($items_info)
{
list($id, $field_values) = each($items_info);
if( $this->Application->GetVar('check_shipping_address') )
{
$has_tangibles = $order->HasTangibleItems();
$req_fields = array('ShippingTo', 'ShippingAddress1', 'ShippingCity', 'ShippingZip', 'ShippingCountry', 'ShippingPhone');
foreach ($req_fields as $field) $order->Fields[$field]['required'] = $has_tangibles;
if( $cs_helper->CountryHasStates( getArrayValue($field_values, 'ShippingCountry') ) ) $order->Fields['ShippingState']['required'] = $has_tangibles && true;
}
if( $this->Application->GetVar('check_billing_address') )
{
$req_fields = array('BillingTo', 'BillingAddress1', 'BillingCity', 'BillingZip', 'BillingCountry', 'BillingPhone');
foreach ($req_fields as $field) $order->Fields[$field]['required'] = true;
if( $cs_helper->CountryHasStates( getArrayValue($field_values, 'BillingCountry') ) ) $order->Fields['BillingState']['required'] = true;
}
if( $this->Application->GetVar('check_credit_card') && ($field_values['PaymentType'] == $order->GetDBField('PaymentType') ) )
{
if( $this->Application->IsAdmin() )
{
$req_fields = array('PaymentCardType', 'PaymentAccount', 'PaymentNameOnCard', 'PaymentCCExpDate');
}
else
{
$req_fields = array('PaymentCardType', 'PaymentAccount', 'PaymentNameOnCard', 'PaymentCCExpDate', 'PaymentCVV2');
}
foreach ($req_fields as $field) $order->Fields[$field]['required'] = true;
}
}
}
/**
* Set's order's user_id to user from session or Guest otherwise
*
* @param kEvent $event
*/
function CheckUser(&$event)
{
if ( $this->Application->IsAdmin() ) return;
$order =& $event->GetObject();
$ses_user = $this->Application->RecallVar('user_id');
if ($order->GetDBField('PortalUserId') != $ses_user) {
if ($ses_user == 0) $ses_user = -2; //Guest
$order->SetDBField('PortalUserId', $ses_user);
// since CheckUser is called in OnBeforeItemUpdate, we don't need to call udpate here, just set the field
}
}
/**
* Creates new addresses if not found in user addresses
*
* @param kEvent $event
* @return bool
*/
function CreateMissingAddresses(&$event)
{
if( !$this->Application->IsAdmin() )
{
$user_id = $this->Application->GetVar('u_id');
if(!$user_id) return false;
$object =& $event->getObject();
$address_list =& $this->Application->recallObject('addr','addr_List', Array('per_page'=>-1, 'skip_counting'=>true) );
$address_list->prepareAddresses();
$order_fields = Array('To','Company','Phone','Fax','Email','Address1','Address2','City','State','Zip','Country');
$billing_address_crc = $address_list->getAddressCRC($object->FieldValues, 'Billing');
$shipping_address_crc = $address_list->getAddressCRC($object->FieldValues, 'Shipping');
$this->Application->setUnitOption('addr','AutoLoad',false);
$address =& $this->Application->recallObject('addr.-item','addr');
$shipping_address_found = false;
$billing_address_found = false;
if($shipping_address_crc || $billing_address_crc)
{
$address_list->GoFirst();
while( !$address_list->EOL() )
{
$current_address_crc = $address_list->getAddressCRC( $address_list->getCurrentRecord() );
if($current_address_crc == $shipping_address_crc) $shipping_address_found = true;
if($current_address_crc == $billing_address_crc) $billing_address_found = true;
$address_list->GoNext();
}
}
if(!$billing_address_found && $address_list->CheckAddress($object->FieldValues,'Billing') )
{
foreach($order_fields as $order_field)
{
$address->SetDBField($order_field, $object->GetDBField('Billing'.$order_field) );
}
$address->SetDBField('PortalUserId', $user_id);
$address->Create();
$address->MarkAsBilling();
}
if(!$shipping_address_found && $address_list->CheckAddress($object->FieldValues,'Shipping') )
{
foreach($order_fields as $order_field)
{
$address->SetDBField($order_field, $object->GetDBField('Shipping'.$order_field) );
}
$address->SetDBField('PortalUserId', $user_id);
$address->Create();
$address->MarkAsShipping();
}
}
return true;
}
/* ======================== ADMIN ONLY ======================== */
/**
* Prepare temp tables and populate it
* with items selected in the grid
*
* @param kEvent $event
*/
function OnPreCreate(&$event)
{
parent::OnPreCreate($event);
$object =& $event->getObject();
$new_number = $this->getNextOrderNumber($event);
$object->SetDBField('Number', $new_number);
$object->SetDBField('SubNumber', 0);
$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
*/
function OnBeforeClone(&$event)
{
$object =& $this->Application->recallObject($event->Prefix.'.-item', $event->Prefix);
$new_number = $this->getNextOrderNumber($event);
$object->SetDBField('Number', $new_number);
$object->SetDBField('SubNumber', 0);
$object->SetDBField('OriginalAmount', 0);
}
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();
$this->Application->setUnitOption('p', 'AutoLoad', false);
$product_object =& $this->Application->recallObject('p');
$product_object->SwitchToLive();
$this->Application->setUnitOption('orditems', 'AutoLoad', false);
$order_item =& $this->Application->recallObject('orditems.-item');
$event->status = erSUCCESS;
while( !$order_items->EOL() )
{
$rec = $order_items->getCurrentRecord();
$product_object->Load( $rec['ProductId'] );
if (!$product_object->GetDBField('InventoryStatus')) {
$order_items->GoNext();
continue;
}
$lack = $rec['Quantity'] - $rec['QuantityReserved'];
//reserve lack or what is available
$to_reserve = min($lack, $product_object->GetDBField('QtyInStock')-$product_object->GetDBField('QtyInStockMin'));
if ($to_reserve < $lack) $event->status = erFAIL; // if we can't reserve the full lack
//reserve in order
$order_item->SetFieldsFromHash($rec);
$order_item->SetDBField('QuantityReserved', $rec['QuantityReserved'] + $to_reserve);
$order_item->SetId($rec['OrderItemId']);
$order_item->Update();
//update product - increase reserved, decrease in stock
$product_object->SetDBField('QtyReserved', $product_object->GetDBField('QtyReserved') + $to_reserve);
$product_object->SetDBField('QtyInStock', $product_object->GetDBField('QtyInStock') - $to_reserve);
$product_object->SetDBField('QtyBackOrdered', $product_object->GetDBField('QtyBackOrdered') - $to_reserve);
$product_object->Update();
$order_items->GoNext();
}
return true;
}
function OnOrderPrint(&$event)
{
$event->redirect_params = Array('opener'=>'s');
}
/**
* 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;
}
/**
* Validates passed search parameters and stores them in session if all is ok
*
* @param kEvent $event
*/
function OnSearch(&$event)
{
if($event->Special != 'sitem') parent::OnSearch($event);
$this->Application->setUnitOption($event->Prefix,'AutoLoad',false);
$object =& $event->getObject();
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
if($items_info)
{
$id = 0;
$object->Load($id);
$field_values = $items_info[$id];
$object->SetFieldsFromHash($field_values);
if( $object->Validate() )
{
$fields = Array('OrderNumber','OrderSearchId','Username','CustomerName',
'FromDateTime','ToDateTime','FromAmount','ToAmount',
'AffiliateUser', 'Status', 'OrderIP', 'PaymentAccount');
$search_filter = Array();
foreach($fields as $field)
{
$value = $object->GetDBField($field);
if( strlen($value) == 0 || ($field == 'Status' && $value == -1) ) continue; // because 0 is normal order status
$field_options = $object->GetFieldOptions($field);
$search_field = getArrayValue($field_options,'filter_field');
if($search_field === false) $search_field = $field;
$filter_type = isset($object->VirtualFields[$search_field]) ? 'having' : 'where';
$table_name = ($filter_type == 'where') ? '`'.$object->TableName.'`.' : '';
$filter_name_map = Array('range_from' => '`%s` >= \'%s\'', 'range_to' => '`%s` <= \'%s\'', 'equals' => '`%s` = \'%s\'', 'like' => '`%s` LIKE \'%%%s%%\'');
if($field_options['filter_type'] == 'like')
{
$value = preg_replace( '/\'(.*)\'/U', '\\1', $this->Conn->qstr( str_replace('\\','\\\\', $value) ) );
}
$search_value = $table_name.sprintf($filter_name_map[ $field_options['filter_type'] ], $search_field, $value);
$search_filter[$field] = Array('search_value'=> $object->GetField($field), 'type' => $filter_type, 'value' => $search_value);
}
if(!$search_filter)
{
$event->Init($event->Prefix, 'search');
$this->OnSearchReset($event);
$event->Init($event->Prefix, 'sitem');
return false;
}
$this->Application->StoreVar('ord.search_search_filter', serialize($search_filter) );
$this->Application->StoreVar('ord.search_search_keyword', 'orders search :)');
}
else
{
// echo 'failed';
}
}
$this->Application->setEvent('ord.sitem',''); // not to submit 2 event at one in temp table
$event->redirect = false;
}
/**
* Processes item selection from popup item selector
*
* @param kEvent $event
* @return int
*/
function OnSelectItems(&$event)
{
$item_prefix = 'p'; // in future get this prefix from submit based on item beeing selected
$items_info = $this->Application->GetVar($item_prefix);
if($items_info)
{
$resource_ids = array_keys($items_info);
$item_ids = $this->Conn->GetCol('SELECT ProductId, ResourceId FROM '.TABLE_PREFIX.'Products WHERE ResourceId IN ('.implode(',', $resource_ids).')');
foreach ($item_ids as $id) {
$this->AddItemToOrder($event, $id);
}
}
$this->finalizePopup($event,'ord','in-commerce/orders/orders_edit_items');
}
function OnMassPlaceOrder(&$event)
{
$this->Application->setUnitOption($event->Prefix,'AutoLoad',false);
$object =& $event->getObject();
$this->StoreSelectedIDs($event);
$ids=$this->getSelectedIDs($event);
if($ids)
{
foreach($ids as $id)
{
$object->Load($id);
$this->DoPlaceOrder($event);
}
}
$event->status = erSUCCESS;
}
/**
* Universal
* Checks if QtyInStock is enough to fullfill lack (Qty - QtyReserved in order)
*
* @param int $ord_id
* @return bool
*/
function ReadyToProcess($ord_id)
{
$query = 'SELECT SUM(IF('.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
WHERE OrderId = '.$ord_id.'
GROUP BY OrderId';
return ($this->Conn->GetOne($query) == 0);
}
/**
* Approve order ("Pending" tab)
*
* @param kDBList $order_items
* @return int new status of order if any
*/
function approveOrder(&$order_items)
{
$this->Application->setUnitOption('p', 'AutoLoad', false);
$product_object =& $this->Application->recallObject('p');
$this->Application->setUnitOption('orditems', 'AutoLoad', false);
$order_item =& $this->Application->recallObject('orditems.-item');
while( !$order_items->EOL() )
{
$rec = $order_items->getCurrentRecord();
$order_item->SetFieldsFromHash($rec);
$order_item->SetId($rec['OrderItemId']);
$order_item->SetDBField('QuantityReserved', 0);
$order_item->Update();
$product_object->Load( $rec['ProductId'] );
if (!$product_object->GetDBField('InventoryStatus')) {
$order_items->GoNext();
continue;
}
// decrease QtyReserved by amount of product used in order
$QtyReserved = $product_object->GetDBField('QtyReserved') - $rec['Quantity'];
$product_object->SetDBField('QtyReserved', $QtyReserved);
$product_object->Update();
$order_items->GoNext();
}
return true;
}
function restoreOrder(&$order_items)
{
$this->Application->setUnitOption('p', 'AutoLoad', false);
$product_object =& $this->Application->recallObject('p');
$product_object->SwitchToLive();
$this->Application->setUnitOption('orditems', 'AutoLoad', false);
$order_item =& $this->Application->recallObject('orditems.-item');
while( !$order_items->EOL() )
{
$rec = $order_items->getCurrentRecord();
$product_object->Load( $rec['ProductId'] );
if (!$product_object->GetDBField('InventoryStatus')) {
$order_items->GoNext();
continue;
}
// cancelling backorderd qty if any
$lack = $rec['Quantity'] - $rec['QuantityReserved'];
if ($lack > 0 && $rec['BackOrderFlag'] > 0) { // lack should have been recorded as QtyBackOrdered
$product_object->SetDBField('QtyBackOrdered', $product_object->GetDBField('QtyBackOrdered') - $lack);
}
// canceling reservation in stock
$product_object->SetDBField('QtyReserved', $product_object->GetDBField('QtyReserved') - $rec['QuantityReserved']);
// putting remaining freed qty back to stock
$product_object->SetDBField('QtyInStock', $product_object->GetDBField('QtyInStock') + $rec['QuantityReserved']);
$product_object->Update();
// using freed qty to fullfill possible backorders
$product_h = $this->Application->recallObject('p_EventHandler');
$freed_qty = $product_h->FullfillBackOrders($product_object, $rec['QuantityReserved']);
$order_item->SetFieldsFromHash($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', 0)) {
return;
}
// process order products
$this->Application->setUnitOption($event->Prefix,'AutoLoad',false);
$object =& $this->Application->recallObject($event->Prefix.'.-inv');
$this->StoreSelectedIDs($event);
$ids = $this->getSelectedIDs($event);
if($ids)
{
foreach($ids as $id)
{
$object->Load($id);
$this->InventoryAction($event);
}
}
}
function InventoryAction(&$event)
{
if ($this->Application->CheckPermission('SYSTEM_ACCESS.READONLY', 0)) {
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');
// preparing new status, but not setting it yet
$object->SetDBField('Status', $event_status_map[$event->Name]);
$set_new_status = false;
$event->status = erSUCCESS;
$billing_email = $object->GetDBField('BillingEmail');
$user_email = $this->Conn->GetOne(' SELECT Email FROM '.$this->Application->getUnitOption('u', 'TableName').'
WHERE PortalUserId = '.$this->Application->GetVar('u_id'));
$email_params = Array();
$email_params['to_email'] = $billing_email ? $billing_email : $user_email;
$email_params['to_name'] = $object->GetDBField('BillingTo');
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
// charge user for order in case if we user 2step changing (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->FieldValues, $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) {
foreach($order_items->Records as $product_item)
{
if (!$product_item['ProductId']) continue; // product may have been deleted
$product_object =& $this->Application->recallObject('p');
$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->SplitOrder($event, $object);
$email_event_user =& $this->Application->EmailEventUser('ORDER.APPROVE', $object->GetDBField('PortalUserId'), $email_params);
}
break;
case 'OnMassOrderDeny':
case 'OnOrderDeny':
$email_event_user =& $this->Application->EmailEventUser('ORDER.DENY', $object->GetDBField('PortalUserId'), $email_params);
// 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':
$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 : $user_email;
$email_event_user =& $this->Application->EmailEventUser('ORDER.SHIP', $object->GetDBField('PortalUserId'), $email_params);
break;
case 'OnMassOrderProcess':
case 'OnOrderProcess':
if ($this->ReadyToProcess($object->GetID())) {
$event->CallSubEvent('OnReserveItems');
if ($event->status == erSUCCESS) $set_new_status = true;
$email_event_user =& $this->Application->EmailEventUser('BACKORDER.PROCESS', $object->GetDBField('PortalUserId'), $email_params);
}
else {
$event->status = erFAIL;
}
break;
}
if ($set_new_status) $object->Update();
}
/**
* Get next free order number
*
* @param kEvent $event
*/
function getNextOrderNumber(&$event)
{
$object =& $event->getObject();
$sql = 'SELECT MAX(Number) FROM '.kTempTablesHandler::GetLiveName($object->TableName);
return $this->Conn->GetOne($sql) + 1;
}
/**
* 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');
$from_tab = substr($event->Name,strlen('OnResetTo'));
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' ? WHERE_FILTER : HAVING_FILTER;
$object->addFilter($filter_name, $filter_params['value'], $filter_type, 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');
$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
* @access public
*/
function OnAfterItemLoad(&$event)
{
// get user fields
$object =& $event->getObject();
$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);
//'UserCompany','UserFax','UserAddress2',
$fields = Array('UserTo'=>'UserTo','UserPhone'=>'Phone','UserEmail'=>'Email',
'UserAddress1'=>'Street','UserCity'=>'City','UserState'=>'State',
'UserZip'=>'Zip','UserCountry'=>'Country');
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');
$cs_helper->PopulateStates($event, 'ShippingState', 'ShippingCountry');
$cs_helper->PopulateStates($event, 'BillingState', 'BillingCountry');
$ord_event = $this->Application->GetVar( $event->getPrefixSpecial().'_event');
if( ($ord_event !== 'OnProceedToPreview') && !$this->Application->IsAdmin() )
{
$this->Application->SetVar('check_credit_card', 0);
}
$this->SetStepRequiredFields($event);
// needed in OnAfterItemUpdate
$this->Application->SetVar('OriginalShippingOption', $object->GetDBField('ShippingOption'));
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnBeforeItemUpdate(&$event)
{
$cs_helper =& $this->Application->recallObject('CountryStatesHelper');
$cs_helper->PopulateStates($event, 'ShippingState', 'ShippingCountry');
$cs_helper->PopulateStates($event, 'BillingState', 'BillingCountry');
$object = &$event->getObject();
$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->UpdateShippingTotal($event);
}
/**
* Apply any custom changes to list's sql query
*
* @param kEvent $event
* @access protected
* @see OnListBuild
*/
function SetCustomQuery(&$event)
{
$object =& $event->getObject();
$types = $event->getEventParam('types');
if($types == 'myorders')
{
$user_id = $this->Application->GetVar('u_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
{
$special = $event->Special ? $event->Special : $this->Application->GetVar('order_type');
if ($special == 'search') return ;
$object->addFilter( 'status_filter', '%1$s.Status='.$this->getTypeBySpecial($special) );
if( $event->getEventParam('selected_only') )
{
$this->StoreSelectedIDs($event);
$ids = $this->getSelectedIDs($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);
}
function CheckQuantites(&$event)
{
if ($this->OnRecalculateItems($event)) { // if something has changed in the order
if ( $this->Application->IsAdmin() )
{
if ($this->Application->GetVar('ord_mode' == 't')) {
$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 ReserveItems(&$event)
{
$table_prefix = $this->TablePrefix($event);
$order =& $event->getObject();
$ord_id = $order->GetId();
// TABLE_PREFIX and $table_prefix are NOT the same !!!
$query = 'SELECT
BackOrderFlag, '.
$table_prefix.'OrderItems.OrderItemId, '.
$table_prefix.'OrderItems.Quantity, '.
$table_prefix.'OrderItems.QuantityReserved, '.
TABLE_PREFIX.'Products.QtyInStock, '.
TABLE_PREFIX.'Products.QtyInStockMin, '.
$table_prefix.'OrderItems.ProductId, '.
TABLE_PREFIX.'Products.InventoryStatus
FROM '.$table_prefix.'OrderItems
LEFT JOIN '.TABLE_PREFIX.'Products
ON '.TABLE_PREFIX.'Products.ProductId = '.$table_prefix.'OrderItems.ProductId
WHERE OrderId = '.$ord_id.' AND '.TABLE_PREFIX.'Products.Type = 1
ORDER BY BackOrderFlag ASC';
$items = $this->Conn->Query($query);
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;
$query = 'UPDATE '.TABLE_PREFIX.'Products
SET
QtyInStock = QtyInStock - '.$to_reserve.',
QtyReserved = QtyReserved + '.$to_reserve.',
QtyBackOrdered = QtyBackOrdered + '.$to_backorder.'
WHERE ProductId = '.$an_item['ProductId'];
$this->Conn->Query($query);
}
}
function FreeItems(&$event)
{
$table_prefix = $this->TablePrefix($event);
$order =& $event->getObject();
$ord_id = $order->GetId();
// TABLE_PREFIX and $table_prefix are NOT the same !!!
$query = 'SELECT
BackOrderFlag, '.
$table_prefix.'OrderItems.OrderItemId, '.
$table_prefix.'OrderItems.Quantity, '.
$table_prefix.'OrderItems.QuantityReserved, '.
TABLE_PREFIX.'Products.QtyInStock, '.
TABLE_PREFIX.'Products.QtyInStockMin, '.
$table_prefix.'OrderItems.ProductId, '.
TABLE_PREFIX.'Products.InventoryStatus
FROM '.$table_prefix.'OrderItems
LEFT JOIN '.TABLE_PREFIX.'Products
ON '.TABLE_PREFIX.'Products.ProductId = '.$table_prefix.'OrderItems.ProductId
WHERE OrderId = '.$ord_id.' AND '.TABLE_PREFIX.'Products.Type = 1
ORDER BY BackOrderFlag ASC';
$items = $this->Conn->Query($query);
foreach ($items as $an_item) {
if ($an_item['QuantityReserved'] > 0 ) {
$to_free = $an_item['QuantityReserved'];
$to_backorder_free = 0;
if ($an_item['InventoryStatus']) {
$query = 'UPDATE '.TABLE_PREFIX.'Products
SET
QtyInStock = QtyInStock + '.$to_free.',
QtyReserved = QtyReserved - '.$to_free.',
QtyBackOrdered = QtyBackOrdered + '.$to_backorder_free.'
WHERE ProductId = '.$an_item['ProductId'];
$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';
$query = 'SELECT
'.$backorder_select.',
ProductName,
ShippingTypeId,
CONCAT('.TABLE_PREFIX.'Products.Type, "_", IF ('.TABLE_PREFIX.'Products.Type <> 1 AND '.TABLE_PREFIX.'Products.Type <> 4, CONCAT(OrderItemId, "_", '.TABLE_PREFIX.'Products.ProductId), "")) 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 ProductType ASC, BackOrderFlag ASC';
$sub_orders = $this->Conn->Query($query);
$next_sub_number = 0;
foreach ($sub_orders as $sub_order_data) {
$sub_order =& $this->Application->makeClass('ord');
$sub_order->Init($this->Prefix, $this->Special);
$sub_order->Configure();
if ($this->UseTempTables($event) && $next_sub_number == 0) {
$sub_order =& $order;
}
$sub_order->SetDBFieldsFromHash($order->FieldValues);
$sub_order->SetDBField('SubNumber', $next_sub_number);
$sub_order->SetDBField('SubTotal', $sub_order_data['TotalAmount']);
$grouping_data = explode('_', $sub_order_data['Grouping']);
if($grouping_data[0] == 1)
{
$sub_order->SetDBField('ShippingCost', getArrayValue( unserialize($order->GetDBField('ShippingInfo')), $sub_order_data['BackOrderFlag'], 'TotalCost') );
}
else
{
$sub_order->SetDBField('ShippingCost', 0);
}
// tax stuff: begin
$tax = $sub_order->getTaxPercent();
$sub_order->SetDBField( 'VATPercent', $tax );
$amount_without_vat = $sub_order->GetDBField('ShippingCost') + $sub_order->GetDBField('SubTotal');
$sub_order->SetDBField( 'VAT', $amount_without_vat * $tax / 100 );
// tax stuff: end
$original_amount = $sub_order->GetDBField('SubTotal') + $sub_order->GetDBField('ShippingCost') + $sub_order->GetDBField('VAT');
$sub_order->SetDBField('OriginalAmount', $original_amount);
// proportional affiliate commission splitting
if( $order->GetDBField('AffiliateCommission') > 0 )
{
// $order->GetDBField('AffiliateCommission')
$amount_percent = $sub_order->getTotalAmount() * 100 / $order->getTotalAmount();
$sub_order->SetDBField('AffiliateCommission', $order->GetDBField('AffiliateCommission') * $amount_percent / 100 );
}
if ($grouping_data[0] == 1 && ($sub_order_data['BackOrderFlag'] > 0 || ($sub_order_data['TotalItems'] != $sub_order_data['TotalReserved'])) ) {
$sub_order->SetDBField('Status', ORDER_STATUS_BACKORDERS);
$billing_email = $sub_order->GetDBField('BillingEmail');
$user_email = $this->Conn->GetOne(' SELECT Email FROM '.$this->Application->getUnitOption('u', 'TableName').'
WHERE PortalUserId = '.$this->Application->GetVar('u_id'));
$email_params = Array();
$email_params['to_email'] = $billing_email ? $billing_email : $user_email;
$email_params['to_name'] = $sub_order->GetDBField('BillingTo');
$email_event_user =& $this->Application->EmailEventUser('BACKORDER.ADD', $sub_order->GetDBField('PortalUserId'), $email_params);
$email_event_admin =& $this->Application->EmailEventAdmin('BACKORDER.ADD');
}
else
{
if($grouping_data[0] == 4) // downloadable products
{
$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 = 4';
$downl_products = $this->Conn->Query( sprintf($sql, $ord_id) );
$product_ids = Array();
foreach($downl_products as $downl_product)
{
$this->processApproveEvent($downl_product['ProductId'], $downl_product);
$product_ids[] = $downl_product['ProductId'];
}
}
elseif($grouping_data[0] != 1)
{
$order_item_fields = $this->Conn->GetRow('SELECT * FROM '.TABLE_PREFIX.'OrderItems WHERE OrderItemId = '.$grouping_data[1]);
$this->processApproveEvent($grouping_data[2], $order_item_fields);
}
// 1 - tangible, 2 - subscription, 3 - service, 4 - downloadable product
$sub_order->SetDBField('Status', $grouping_data[0] == '1' ? ORDER_STATUS_TOSHIP : ORDER_STATUS_PROCESSED);
}
if($next_sub_number == 0)
{
$sub_order->SetId($order->GetId());
$sub_order->Update();
}
else
{
$sub_order->Create();
}
if($grouping_data[0] == '1') // tangible
{
$query = 'UPDATE '.$table_prefix.'OrderItems SET OrderId = %s WHERE OrderId = %s AND BackOrderFlag = %s';
$query = sprintf($query, $sub_order->GetId(), $ord_id, $sub_order_data['BackOrderFlag']);
}
elseif($grouping_data[0] == '4') // 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) );
}
else
{
$query = 'UPDATE '.$table_prefix.'OrderItems SET OrderId = %s WHERE OrderId = %s AND OrderItemId = %s';
$query = sprintf($query, $sub_order->GetId(), $ord_id, $grouping_data[1]);
}
$this->Conn->Query($query);
// update DiscountTotal field
$sql = 'SELECT SUM(FlatPrice - Price) FROM '.$table_prefix.'OrderItems WHERE OrderId = '.$sub_order->GetID();
$discount_total = $this->Conn->GetOne($sql);
$sql = 'UPDATE '.$sub_order->TableName.'
SET DiscountTotal = '.$this->Conn->qstr($discount_total).'
WHERE OrderId = '.$sub_order->GetID();
$this->Conn->Query($sql);
$next_sub_number++;
}
}
function processApproveEvent($product_id, $order_item_fields)
{
// query processing data from product and run approve event
$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);
$approve_event = new kEvent($processing_data['ApproveEvent']);
$approve_event->setEventParam('field_values', $order_item_fields);
$this->Application->HandleEvent($approve_event);
}
}
function UpdateOrderItem(&$event, $prod_id, $back_order, $qty, $price, $discounted_price, $discount_type, $discount_id, $order_item_id = 0)
{
$price = (float) $price;
$discounted_price = (float) $discounted_price;
$qty = (int) $qty;
$ord_id = $this->getPassedId($event);
$table_prefix = $this->TablePrefix($event);
if($order_item_id)
{
$query = ' SELECT OrderItemId, Quantity, FlatPrice, Price, BackOrderFlag, ItemData FROM '.$table_prefix.'OrderItems
WHERE OrderItemId = '.$order_item_id;
}
else
{
// try to load specified Product by its Id and BackOrderFlag in the order
$query = 'SELECT OrderItemId, Quantity, FlatPrice, Price, BackOrderFlag, ItemData FROM '.$table_prefix.'OrderItems
WHERE
OrderId = '.$ord_id.'
AND
ProductId = '.$prod_id.'
AND
BackOrderFlag '.($back_order ? ' >= 1' : ' = 0');
}
$item_row = $this->Conn->GetRow($query);
$item_id = $item_row['OrderItemId'];
$this->Application->setUnitOption('orditems','AutoLoad',false);
$object =& $this->Application->recallObject('orditems.-item');
$item_data = $item_row['ItemData'];
if($item_data)
{
$item_data = unserialize($item_data);
}
$orig_discount_type = (int)getArrayValue($item_data, 'DiscountType');
$orig_discount_id = (int)getArrayValue($item_data, 'DiscountId');
if ($item_id) { // if Product already exists in the order
if ($qty > 0 &&
$item_row['Quantity'] == $qty &&
round($item_row['FlatPrice'], 3) == round($price, 3) &&
round($item_row['Price'], 3) == round($discounted_price, 3) &&
$orig_discount_type == $discount_type &&
$orig_discount_id == $discount_id)
{
return false;
}
$object->Load($item_id);
if ($qty > 0) { // Update Price by _TOTAL_ qty
$object->SetDBField('Quantity', $qty);
$object->SetDBField('FlatPrice', $price );
$object->SetDBField('Price', $discounted_price );
if($item_data = $object->GetDBField('ItemData'))
{
$item_data = unserialize($item_data);
}
else
{
$item_data = Array();
}
$item_data['DiscountType'] = $discount_type;
$item_data['DiscountId'] = $discount_id;
$object->SetDBField('ItemData', serialize($item_data));
$object->Update();
}
else { // delete products with 0 qty
$object->Delete();
}
}
elseif ($qty > 0) { // if we are adding product
$product = $this->Application->recallObject('p');
$product->Load($prod_id);
$object->SetDBField('ProductId', $prod_id);
$object->SetDBField('ProductName', $product->GetField('Name'));
$object->SetDBField('Quantity', $qty);
$object->SetDBField('FlatPrice', $price );
$object->SetDBField('Price', $discounted_price );
$object->SetDBField('OrderId', $ord_id);
$object->SetDBField('BackOrderFlag', $back_order);
$item_data = Array('DiscountType' => $discount_type, 'DiscountId' => $discount_id);
$object->SetDBField('ItemData', serialize($item_data));
$object->Create();
if( $this->UseTempTables($event) ) $object->SetTempId();
}
else {
return false; // item requiring to set qty to 0, meaning already does not exist
}
return true;
}
/**
* Enter description here...
*
* @param kEvent $event
* @param int $item_id
*/
function AddItemToOrder(&$event, $item_id)
{
// Loading product to add
$product = $this->Application->recallObject('p.toadd');
$product->Load($item_id);
$qty = 1;
$this->Application->setUnitOption('orditems','AutoLoad',false);
$object =& $this->Application->recallObject('orditems'.'.-item');
$order =& $this->Application->recallObject('ord');
$ord_id = $order->GetID();
// Checking if such product already exists in the cart
$keys['OrderId'] = $ord_id;
$keys['ProductId'] = $product->GetId();
$exists = $object->Load($keys);
$object->SetDBField('ProductId', $product->GetId());
$object->SetDBField('ProductName', $product->GetField('l'.$this->Application->GetDefaultLanguageId().'_Name'));
$object->SetDBField('Weight', $product->GetDBField('Weight'));
if($product->GetDBField('Type') == 1)
{
$object->SetDBField('Quantity', $object->GetDBField('Quantity') + $qty);
}
else // Types: 2,3,4
{
$object->SetDBField('Quantity', $qty); // 1
$exists = false;
}
$price = $this->GetPlainProductPrice($product->GetId(), $object->GetDBField('Quantity'), $product->GetDBField('Type'));
$object->SetDBField('FlatPrice', $price);
$couponed_price = $this->GetCouponDiscountedPrice($order->GetDBField('CouponId'), $product->GetId(), $price);
$discounted_price = $this->GetDiscountedProductPrice($product->GetId(), $price, $discount_id, $order);
if( $couponed_price && $couponed_price < $discounted_price )
{
$discounted_price = $couponed_price;
$discount_type = 'coupon';
$discount_id = $order->GetDBField('CouponId');
}
else
{
$discount_type = 'discount';
$discount_id = $discount_id;
}
if($item_data = $event->getEventParam('ItemData'))
{
$item_data = unserialize($item_data);
}
else
{
$item_data = Array();
}
$item_data['DiscountType'] = $discount_type;
$item_data['DiscountId'] = $discount_id;
$object->SetDBField('ItemData', serialize($item_data));
$object->SetDBField('Price', $discounted_price); // will be retrieved later
$object->SetDBField('BackOrderFlag', 0); // it will be updated in OnRecalculateItems later if needed
$object->SetDBField('OrderId', $ord_id);
if ($exists) {
if ($qty > 0) {
$object->Update();
}
else {
$object->Delete();
}
}
else {
$object->Create();
if ($this->UseTempTables($event)) {
$object->setTempID();
}
}
$this->Application->HandleEvent($ord_event, 'ord:OnRecalculateItems');
/*if ($ord_event->getEventParam('RecalculateChangedCart') && !$this->Application->IsAdmin() ) {
$event->SetRedirectParam('checkout_error', $ord_event->redirect_params['checkout_error']);
}*/
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function UpdateShippingTotal(&$event)
{
$object =& $event->getObject();
$ord_id = $object->GetId();
$shipping_option = $object->GetDBField('ShippingOption');
$backorder_select = $shipping_option == 0 ? '0 As BackOrderFlag' : 'BackOrderFlag';
$table_prefix = $this->TablePrefix($event);
$shipping_info = unserialize( $object->GetDBField('ShippingInfo') );
/* $query = 'SELECT
'.$backorder_select.',
ProductName,
ShippingTypeId,
SUM(Quantity) AS TotalItems,
SUM(Weight*Quantity) AS TotalWeight,
SUM(Price * Quantity) AS TotalAmount
FROM '.$table_prefix.'OrderItems
WHERE OrderId = '.$ord_id.'
GROUP BY BackOrderFlag
ORDER BY BackOrderFlag ASC';
$shippings = $this->Conn->Query($query);*/
$shipping_total = 0;
if( is_array($shipping_info) )
{
foreach ($shipping_info as $a_shipping)
{
$id_elements = explode('_', $a_shipping['ShippingTypeId']);
$shipping_total += $a_shipping['TotalCost'];
}
}
$object->SetField('ShippingCost', $shipping_total);
// 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.
* 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($checkout_error = $this->Application->GetVar('set_checkout_error'))
{
$event->SetRedirectParam('checkout_error', $checkout_error);
}
$order =& $event->getObject();
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;
}
$table_prefix = $this->TablePrefix($event);
// process only tangible products here
$query = 'SELECT oi.ProductId, SUM(oi.Quantity) AS Quantity, p.QtyInStock, p.QtyInStockMin, p.BackOrder, p.InventoryStatus FROM '.$table_prefix.'OrderItems AS oi
LEFT JOIN '.TABLE_PREFIX.'Products AS p
ON oi.ProductId = p.ProductId
WHERE (oi.OrderId = '.$ord_id.') AND (p.Type = 1)
GROUP BY oi.ProductId';
$items = $this->Conn->Query($query);
$result = false;
$sub_total = 0;
$sub_total_flat = 0;
$coupon_discount = 0;
$pending_operations = Array();
$backordering = $this->Application->ConfigValue('Comm_Enable_Backordering');
$coupon_id = $order->GetDBField('CouponId');
foreach ($items as $row) {
$min_qty = $this->GetMinQty($row['ProductId']);
if ($row['Quantity'] > 0 && $row['Quantity'] < $min_qty) {
$row['Quantity'] = $min_qty;
$event->SetRedirectParam('checkout_error', 6);
}
$back_order = 0;
$to_order = 0;
if (!$row['InventoryStatus']) {
$available = $row['Quantity']*2; // always available;
}
else {
// if there are not enough qty AND backorder is auto or backorder is always
$available = $row['QtyInStock'] - $row['QtyInStockMin'];
$available = max(0, $available); // just in case
}
if (
$backordering && // backordering generally enabled
(
($row['Quantity'] > $available)
&&
($row['BackOrder'] == 2) //auto
)
||
$row['BackOrder'] == 1 // always
)
{ // split order into order & backorder
if ($row['BackOrder'] == 1) { //Always backorder
$available = 0;
$to_order = 0;
$back_order = $row['Quantity'];
}
else { //Auto
$to_order = $available;
$back_order = $row['Quantity'] - $available;
}
$price = $this->GetPlainProductPrice( $row['ProductId'], $to_order + $back_order, 1 );
$discounted_price = $this->GetDiscountedProductPrice( $row['ProductId'], $price, $discount_id, $order );
$couponed_price = $this->GetCouponDiscountedPrice( $coupon_id, $row['ProductId'], $price );
if($couponed_price && $couponed_price < $discounted_price)
{
$discounted_price = $couponed_price;
$coupon_discount += ($price - $couponed_price) * ($to_order + $back_order);
$discount_type = 'coupon';
$discount_id = $coupon_id;
}
else
{
$discount_type = 'discount';
}
$pending_operations[] = Array( $row['ProductId'], 0, $to_order, $price, $discounted_price, $discount_type, $discount_id, 0 );
$pending_operations[] = Array( $row['ProductId'], 1, $back_order, $price, $discounted_price, $discount_type, $discount_id, 0 );
}
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($row['Quantity'], $available);
$price = $this->GetPlainProductPrice( $row['ProductId'], $to_order + $back_order, 1 );
$discounted_price = $this->GetDiscountedProductPrice( $row['ProductId'], $price, $discount_id, $order );
$couponed_price = $this->GetCouponDiscountedPrice( $coupon_id, $row['ProductId'], $price );
if($couponed_price && $couponed_price < $discounted_price)
{
$discounted_price = $couponed_price;
$coupon_discount += ($price - $couponed_price) * ($to_order + $back_order);
$discount_type = 'coupon';
$discount_id = $coupon_id;
}
else
{
$discount_type = 'discount';
}
$pending_operations[] = Array( $row['ProductId'], 0, $to_order, $price, $discounted_price, $discount_type, $discount_id, 0 );
$pending_operations[] = Array( $row['ProductId'], 1, 0, $price, $discounted_price, $discount_type, $discount_id, 0 ); // this removes backorders
if ($to_order < $row['Quantity']) { // has changed
if ($to_order > 0) {
$event->SetRedirectParam('checkout_error', 2);
}
else {
$event->SetRedirectParam('checkout_error', 3);
}
$result = true;
}
}
$sub_total_flat += ($to_order + $back_order) * $price;
$sub_total += ($to_order + $back_order) * $discounted_price;
}
// process subscriptions, services and downloadable: begin
$query = ' SELECT oi.OrderItemId, oi.ProductId, oi.Quantity, p.QtyInStock, p.QtyInStockMin, p.BackOrder, p.InventoryStatus, p.Type
FROM '.$table_prefix.'OrderItems AS oi
LEFT JOIN '.TABLE_PREFIX.'Products AS p
ON oi.ProductId = p.ProductId
WHERE (oi.OrderId = '.$ord_id.') AND (p.Type IN (2,3,4,5))';
$items = $this->Conn->Query($query);
foreach ($items as $row)
{
$price = $this->GetPlainProductPrice( $row['ProductId'], $row['Quantity'], $row['Type'] );
$discounted_price = $this->GetDiscountedProductPrice( $row['ProductId'], $price, $discount_id, $order );
$couponed_price = $this->GetCouponDiscountedPrice( $coupon_id, $row['ProductId'], $price );
if($couponed_price && $couponed_price < $discounted_price)
{
$discounted_price = $couponed_price;
$coupon_discount += ($price - $couponed_price);
$discount_type = 'coupon';
$discount_id = $coupon_id;
}
else
{
$discount_type = 'discount';
}
$pending_operations[] = Array( $row['ProductId'], 0, $row['Quantity'], $price, $discounted_price, $discount_type, $discount_id, $row['OrderItemId'] );
$sub_total_flat += $price * $row['Quantity'];
$sub_total += $discounted_price * $row['Quantity'];
}
// process subscriptions, services and downloadable: end
$flat_discount = $this->GetWholeOrderPlainDiscount($global_discount_id, $order);
$flat_discount = ($flat_discount < $sub_total_flat) ? $flat_discount : $sub_total_flat;
$coupon_flat_discount = $this->GetWholeOrderCouponDiscount($coupon_id);
$coupon_flat_discount = ($coupon_flat_discount < $sub_total_flat) ? $coupon_flat_discount : $sub_total_flat;
if($coupon_flat_discount && $coupon_flat_discount > $flat_discount)
{
$flat_discount = $coupon_flat_discount;
$global_discount_type = 'coupon';
$global_discount_id = $coupon_id;
}
else
{
$global_discount_type = 'discount';
}
if($sub_total_flat - $sub_total < $flat_discount)
{
$coupon_discount = ($flat_discount == $coupon_flat_discount) ? $flat_discount : 0;
$sub_total = $sub_total_flat - $flat_discount;
foreach ($pending_operations as $operation_row)
{
list($product_id, $backorder, $qty, $price, $discounted_price, $dummy, $dummy, $order_item_id) = $operation_row;
$new_price = ($price / $sub_total_flat) * $sub_total;
$result = $this->UpdateOrderItem($event, $product_id, $backorder, $qty, $price, $new_price, $global_discount_type, $global_discount_id, $order_item_id) || $result;
}
}
else
{
foreach ($pending_operations as $operation_row)
{
list($product_id, $backorder, $qty, $price, $discounted_price, $discount_type, $discount_id, $order_item_id) = $operation_row;
$result = $this->UpdateOrderItem($event, $product_id, $backorder, $qty, $price, $discounted_price, $discount_type, $discount_id, $order_item_id) || $result;
}
}
$order->SetField('SubTotal', $sub_total);
// $this->CalculateDiscount($event);
$order->SetField('DiscountTotal', $sub_total_flat - $sub_total);
if($coupon_id && $coupon_discount == 0)
{
$this->RemoveCoupon($order);
$event->SetRedirectParam('checkout_error', 8);
}
$order->SetField('CouponDiscount', $coupon_discount);
if ($result) $this->UpdateShippingOption($event);
$this->UpdateShippingTotal($event);
$this->RecalculateTax($event);
if ($event->Name != 'OnAfterItemUpdate') $order->Update();
$event->setEventParam('RecalculateChangedCart', $result);
if (is_object($event->MasterEvent)) {
$event->MasterEvent->setEventParam('RecalculateChangedCart', $result);
}
if ($result && !getArrayValue($event->redirect_params, 'checkout_error')) {
$event->SetRedirectParam('checkout_error', 1);
}
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);
$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'];
}*/
function GetMinQty($p_id)
{
$query = 'SELECT
MIN(pp.MinQty)
FROM '.TABLE_PREFIX.'ProductsPricing AS pp
WHERE pp.ProductId = '.$p_id;
$min_qty = $this->Conn->GetOne($query);
if (!$min_qty) return 1;
return $min_qty;
}
/**
* Return product price for given qty, taking no discounts into account
*
* @param int $p_id ProductId
* @param int $qty Quantity
* @return float
*/
function GetPlainProductPrice($p_id, $qty, $product_type)
{
if($product_type == 1)
{
$where_clause = 'pp.ProductId = '.$p_id.' AND pp.MinQty <= '.$qty;
$orderby_clause = 'ORDER BY ('.$qty.' - pp.MinQty) ASC';
}
else
{
$price_id = $this->GetPricingId($p_id);
$where_clause = 'pp.ProductId = '.$p_id.' AND pp.PriceId = '.$price_id;
$orderby_clause = '';
}
$sql = 'SELECT Price
FROM '.TABLE_PREFIX.'ProductsPricing AS pp
LEFT JOIN '.TABLE_PREFIX.'Products AS p
ON p.ProductId = pp.ProductId
WHERE '.$where_clause.'
GROUP BY pp.ProductId
'.$orderby_clause;
$price = $this->Conn->GetOne($sql);
if (!$price) return 0;
return max($price, 0);
}
/**
* Return product price for given qty, taking possible discounts into account
*
* @param int $p_id ProductId
* @param int $qty Quantity
* @return float
*/
function GetDiscountedProductPrice($p_id, $price, &$discount_id, &$order_object)
{
$discount_id = 0;
$user_id = $order_object->GetDBField('PortalUserId');
$user_groups = $this->Application->getUserGroups($user_id);
$sql = '
SELECT
IF(pd.Type = 1,
'.$price.' - pd.Amount,
IF(pd.Type = 2,
('.$price.' * (1-pd.Amount/100)),
'.$price.'
)
) AS DiscountedPrice,
pd.DiscountId
FROM '.TABLE_PREFIX.'Products AS p
LEFT JOIN '.TABLE_PREFIX.'ProductsDiscountItems AS pdi ON
pdi.ItemResourceId = p.ResourceId OR pdi.ItemType = 0
LEFT JOIN '.TABLE_PREFIX.'ProductsDiscounts AS pd ON
pd.DiscountId = pdi.DiscountId
AND
(pdi.ItemType = 1 OR (pdi.ItemType = 0 AND pd.Type = 2))
AND
pd.Status = 1
AND
( pd.GroupId IN ('.$user_groups.') AND
( (pd.Start IS NULL OR pd.Start < UNIX_TIMESTAMP())
AND
(pd.End IS NULL OR pd.End > UNIX_TIMESTAMP())
)
)
WHERE p.ProductId = '.$p_id.' AND pd.DiscountId IS NOT NULL
';
$pricing = $this->Conn->GetCol($sql, 'DiscountId');
if (!$pricing) return $price;
$discounted_price = min($pricing);
$pricing = array_flip($pricing);
$discount_id = $pricing[$discounted_price];
$discounted_price = min($discounted_price, $price);
return max($discounted_price, 0);
}
function GetCouponDiscountedPrice($coupon_id, $p_id, $price)
{
if(!$coupon_id) return 0;
$sql = '
SELECT
'.$price.' AS Price,
MIN(IF(pc.Type = 1,
'.$price.' - pc.Amount,
IF(pc.Type = 2,
('.$price.' * (1-pc.Amount/100)),
'.$price.'
)
)) AS DiscountedPrice
FROM '.TABLE_PREFIX.'Products AS p
LEFT JOIN '.TABLE_PREFIX.'ProductsCouponItems AS pci ON
pci.ItemResourceId = p.ResourceId OR pci.ItemType = 0
LEFT JOIN '.TABLE_PREFIX.'ProductsCoupons AS pc ON
pc.CouponId = pci.CouponId
AND
(pci.ItemType = 1 OR (pci.ItemType = 0 AND pc.Type = 2))
WHERE p.ProductId = '.$p_id.' AND pci.CouponId = '.$coupon_id.'
GROUP BY p.ProductId
';
$pricing = $this->Conn->GetRow($sql);
if (!$pricing) return 0;
$price = min($pricing['Price'], $pricing['DiscountedPrice']);
return max($price, 0);
}
function GetWholeOrderPlainDiscount(&$discount_id, &$order_object)
{
$user_id = $order_object->GetDBField('PortalUserId');
$user_groups = $this->Application->getUserGroups($user_id);
$sql = '
SELECT pd.Amount AS Discount, pd.DiscountId
FROM '.TABLE_PREFIX.'ProductsDiscountItems AS pdi
LEFT JOIN '.TABLE_PREFIX.'ProductsDiscounts AS pd
ON
pd.DiscountId = pdi.DiscountId
AND
pdi.ItemType = 0 AND pd.Type = 1
AND
pd.Status = 1
AND
( pd.GroupId IN ('.$user_groups.') AND
( (pd.Start IS NULL OR pd.Start < UNIX_TIMESTAMP())
AND
(pd.End IS NULL OR pd.End > UNIX_TIMESTAMP())
)
)
WHERE pd.DiscountId IS NOT NULL
';
$pricing = $this->Conn->GetCol($sql, 'DiscountId');
if (!$pricing) return 0;
$discounted_price = min($pricing);
$pricing = array_flip($pricing);
$discount_id = $pricing[$discounted_price];
$discounted_price = min($discounted_price, $price);
return max($discounted_price, 0);
}
function GetWholeOrderCouponDiscount($coupon_id)
{
$sql = 'SELECT Amount
FROM '.TABLE_PREFIX.'ProductsCoupons
WHERE CouponId = '.$coupon_id.' AND Type = 1';
return $this->Conn->GetOne($sql);
}
/**
* 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) {
$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++;
}
}
}
function UpdateShippingTypes(&$event)
{
$object =& $this->Application->recallObject($event->getPrefixSpecial());
$ord_id = $object->GetId();
$order_info = $this->Application->GetVar('ord');
$shipping_ids = getArrayValue($order_info, $ord_id, 'ShippingTypeId');
if (!$shipping_ids)
{
return;
}
$last_shippings = unserialize($this->Application->RecallVar('LastShippings'));
$shipping_types = Array();
foreach($shipping_ids as $package => $id)
{
$shipping_types[$package] = $last_shippings[$package][$id];
}
$object->SetDBField('ShippingInfo', serialize($shipping_types));
return;
}
/*function shipOrder(&$order_items)
{
$this->Application->setUnitOption('p', 'AutoLoad', false);
$product_object =& $this->Application->recallObject('p');
$order_item =& $this->Application->recallObject('orditems.-item');
while( !$order_items->EOL() )
{
$rec = $order_items->getCurrentRecord();
$order_item->SetFieldsFromHash($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();
$tax = $object->getTaxPercent();
$object->SetDBField( 'VATPercent', $tax );
$subtotal = $object->GetDBField('AmountWithoutVAT');
$object->SetDBField( 'VAT', $subtotal * $tax / 100 );
}
function CalculateDiscount(&$event)
{
$object =& $event->getObject();
$this->Application->setUnitOption('coup', 'AutoLoad', false);
$coupon =& $this->Application->recallObject('coup');
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)
{
$this->StoreSelectedIDs($event);
$id = array_shift( $this->getSelectedIDs($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)
{
$this->Application->setUnitOption($event->Prefix,'AutoLoad',false);
$object =& $event->getObject();
$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=erSUCCESS;
}
else
{
$event->status=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;
}
}
?>

Event Timeline