Page MenuHomeIn-Portal Phabricator

in-commerce
No OneTemporary

File Metadata

Created
Tue, Jun 17, 10:16 AM

in-commerce

This file is larger than 256 KB, so syntax highlighting was skipped.
Index: branches/5.2.x/units/gateways/gw_classes/atosorigin.php
===================================================================
--- branches/5.2.x/units/gateways/gw_classes/atosorigin.php (revision 15567)
+++ branches/5.2.x/units/gateways/gw_classes/atosorigin.php (revision 15568)
@@ -1,192 +1,192 @@
<?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.
*/
require_once GW_CLASS_PATH.'/gw_base.php';
$class_name = 'kAtosOriginGW'; // for automatic installation
class kAtosOriginGW extends kGWBase
{
function InstallData()
{
$data = array(
'Gateway' => Array('Name' => 'Atos Origin', 'ClassName' => 'kAtosOriginGW', 'ClassFile' => 'atosorigin.php', 'RequireCCFields' => 0),
'ConfigFields' => Array(
'request_binary' => Array('Name' => 'API Request Executable', 'Type' => 'text', 'ValueList' => '', 'Default' => ''),
'response_binary' => Array('Name' => 'API Response Executable', 'Type' => 'text', 'ValueList' => '', 'Default' => ''),
'pathfile' => Array('Name' => 'Pathfile', 'Type' => 'text', 'ValueList' => '', 'Default' => ''),
'merchant_id' => Array('Name' => 'Merchant ID', 'Type' => 'text', 'ValueList' => '', 'Default' => ''),
'merchant_country' => Array('Name' => 'Merchant Country Code', 'Type' => 'text', 'ValueList' => '', 'Default' => ''),
'currency_code' => Array('Name' => 'Currency Code', 'Type' => 'text', 'ValueList' => '', 'Default' => '978'),
'shipping_control' => Array('Name' => 'Shipping Control', 'Type' => 'select', 'ValueList' => '3=la_CreditDirect,4=la_CreditPreAuthorize', 'Default' => '3'),
)
);
return $data;
}
/**
* Returns payment form submit url
*
* @param Array $gw_params gateway params from payment type config
* @return string
*/
function getFormAction($gw_params)
{
return $gw_params['submit_url'];
}
/**
* Processed input data and convets it to fields understandable by gateway
*
* @param Array $item_data
* @param Array $tag_params additional params for gateway passed through tag
* @param Array $gw_params gateway params from payment type config
* @return Array
*/
function getHiddenFields($item_data, $tag_params, $gw_params)
{
$params['pathfile'] = $gw_params['pathfile'];
$params['merchant_id'] = $gw_params['merchant_id'];
$params['merchant_country'] = $gw_params['merchant_country'];
$params['currency_code'] = $gw_params['currency_code'];
$params['currency_code'] = $gw_params['currency_code'];
$params['normal_return_url'] = $this->Application->HREF($tag_params['return_template'],'',Array('pass'=>'m'));
$params['cancel_return_url'] = $this->Application->HREF($tag_params['cancel_template'],'',Array('pass'=>'m'));
$params['automatic_response_url'] = $this->getNotificationUrl('units/gateways/gw_classes/notify_scripts/atosorigin_notify.php');
$txt_amount = sprintf("%.2f", $item_data['TotalAmount']);
$params['amount'] = str_replace( Array('.', ','), '', $txt_amount);
$params['caddie'] = $this->Application->GetSID() . ',' . MD5($item_data['OrderId']);
$params['order_id'] = $item_data['OrderId'];
- $params['customer_ip_address'] = $_SERVER['REMOTE_ADDR'];
+ $params['customer_ip_address'] = $this->Application->getClientIp();
$params['customer_id'] = $item_data['PortalUserId'];
$billing_email = $item_data['BillingEmail'];
if (!$billing_email) {
$billing_email = $this->Conn->GetOne(' SELECT Email FROM '.$this->Application->getUnitOption('u', 'TableName').'
WHERE PortalUserId = '.$this->Application->RecallVar('user_id'));
}
$params['customer_email'] = $billing_email;
$params_str = '';
foreach ($params as $key => $val) {
$params_str .= ' '.$key . '="'.$val.'"';
}
$run_line = $gw_params['request_binary'].' '.$params_str;
// $run_line = escapeshellcmd($run_line);
// echo $run_line;
exec ($run_line, $rets);
$ret = $rets[0];
$ret = preg_replace('/^(.*)!!/is', '', $ret);
$ret = rtrim($ret, '!');
return '</form>'.$ret.'<form>';
}
function NeedPlaceButton($item_data, $tag_params, $gw_params)
{
return false;
}
function processNotification($gw_params)
{
$params['pathfile'] = $gw_params['pathfile'];
$params['message'] = $_POST['DATA'];
$params_str = '';
foreach ($params as $key => $val) {
$params_str .= ' '.$key . '="'.$val.'"';
}
$run_line = $gw_params['response_binary'].' '.$params_str;
exec ($run_line, $rets);
$ret = $rets[0];
$result = $this->parseGWResponce($ret, $gw_params);
list ($sid, $auth_code) = explode(',', $result['caddie']);
$session = $this->Application->recallObject('Session');
$session->SID = $sid;
$order_id = $this->Conn->GetOne('SELECT OrderId FROM '.TABLE_PREFIX.'Orders WHERE md5(OrderId) = '.$this->Conn->qstr($auth_code));
$this->Application->SetVar('ord_id', $order_id);
$order = $this->Application->recallObject('ord');
$order->Load($order_id);
return $result['response_code'] === '00' ? 1 : 0;
}
function parseGWResponce($str, $gw_params)
{
$response = explode ("!", $str);
$result = Array (
'code' => $response[1],
'error' => $response[2],
'merchant_id' => $response[3],
'merchant_country' => $response[4],
'amount' => $response[5],
'transaction_id' => $response[6],
'payment_means' => $response[7],
'transmission_date' => $response[8],
'payment_time' => $response[9],
'payment_date' => $response[10],
'response_code' => $response[11],
'payment_certificate' => $response[12],
'authorisation_id' => $response[13],
'currency_code' => $response[14],
'card_number' => $response[15],
'cvv_flag' => $response[16],
'cvv_response_code' => $response[17],
'bank_response_code' => $response[18],
'complementary_code' => $response[19],
'complementary_info' => $response[20],
'return_context' => $response[21],
'caddie' => $response[22],
'receipt_complement' => $response[23],
'merchant_language' => $response[24],
'language' => $response[25],
'customer_id' => $response[26],
'order_id' => $response[27],
'customer_email' => $response[28],
'customer_ip_address' => $response[29],
'capture_day' => $response[30],
'capture_mode' => $response[31],
'data' => $response[32],
);
$this->parsed_responce = $result;
return $result;
}
function getGWResponce()
{
return serialize($this->parsed_responce);
}
function getErrorMsg()
{
$msg = $this->parsed_responce['error'];
if (!$msg) {
if ($this->parsed_responce['response_code'] != '00') {
$msg = 'Transaction failed';
}
}
return $msg;
}
}
\ No newline at end of file
Index: branches/5.2.x/units/gateways/gw_classes/paypal_direct.php
===================================================================
--- branches/5.2.x/units/gateways/gw_classes/paypal_direct.php (revision 15567)
+++ branches/5.2.x/units/gateways/gw_classes/paypal_direct.php (revision 15568)
@@ -1,241 +1,241 @@
<?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.
*/
require_once GW_CLASS_PATH.'/gw_base.php';
$class_name = 'kGWPaypalDirect'; // for automatic installation
class kGWPaypalDirect extends kGWBase
{
function InstallData()
{
$data = array(
'Gateway' => Array('Name' => 'PayPal Pro', 'ClassName' => 'kGWPaypalDirect', 'ClassFile' => 'paypal_direct.php', 'RequireCCFields' => 1),
'ConfigFields' => Array(
'submit_url' => Array('Name' => 'Submit URL', 'Type' => 'text', 'ValueList' => '', 'Default' => 'https://api-3t.paypal.com/nvp'),
'api_username' => Array('Name' => 'PayPal Pro API Username', 'Type' => 'text', 'ValueList' => '', 'Default' => ''),
'api_password' => Array('Name' => 'PayPal Pro API Password', 'Type' => 'text', 'ValueList' => '', 'Default' => ''),
'signature' => Array('Name' => 'PayPal Pro API Signature', 'Type' => 'text', 'ValueList' => '', 'Default' => ''),
'shipping_control' => Array('Name' => 'Shipping Control', 'Type' => 'select', 'ValueList' => '3=la_CreditDirect', 'Default' => '3'),
)
);
return $data;
}
function DirectPayment($item_data, $gw_params)
{
$post_fields = Array();
// -- Login Information --
$post_fields['METHOD'] = 'DoDirectPayment';
$post_fields['VERSION'] = '52.0';
- $post_fields['IPADDRESS'] = $_SERVER['REMOTE_ADDR'];
+ $post_fields['IPADDRESS'] = $this->Application->getClientIp();
$post_fields['USER'] = $gw_params['api_username'];
$post_fields['PWD'] = $gw_params['api_password'];
$post_fields['SIGNATURE'] = $gw_params['signature'];
$post_fields['PAYMENTACTION'] = 'Sale';
$post_fields['AMT'] = sprintf('%.2f', $item_data['TotalAmount']);
switch ($item_data['PaymentCardType']) {
case 1:
$post_fields['CREDITCARDTYPE'] = 'Visa';
break;
case 2:
$post_fields['CREDITCARDTYPE'] = 'MasterCard';
break;
case 3:
$post_fields['CREDITCARDTYPE'] = 'Amex';
break;
case 4:
$post_fields['CREDITCARDTYPE'] = 'Discover';
break;
default:
$this->parsed_responce['responce_reason_text'] = 'Invalid Credit Card Type';
return false;
}
$post_fields['ACCT'] = $item_data['PaymentAccount'];
$date_parts = explode('/', $item_data['PaymentCCExpDate']);
$post_fields['EXPDATE'] = $date_parts[0].'20'.$date_parts[1];
$post_fields['CVV2'] = $item_data['PaymentCVV2'];
$names = explode(' ', $item_data['PaymentNameOnCard'], 2);
$post_fields['FIRSTNAME'] = getArrayValue($names, 0);
$post_fields['LASTNAME'] = getArrayValue($names, 1);
$post_fields['STREET'] = $item_data['BillingAddress1'];
$post_fields['STREET2'] = $item_data['BillingAddress2'];
$post_fields['CITY'] = $item_data['BillingCity'];
$post_fields['STATE'] = $item_data['BillingState'];
$cs_helper = $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$post_fields['COUNTRYCODE'] = $cs_helper->getCountryIso( $item_data['BillingCountry'] );
$post_fields['ZIP'] = $item_data['BillingZip'];
$post_fields['INVNUM'] = $item_data['OrderNumber'];
$post_fields['CUSTOM'] = $item_data['PortalUserId'];
/*
$post_fields['x_encap_char'] = $gw_params['encapsulate_char'];
$post_fields['x_relay_response'] = 'False';
$post_fields['x_type'] = $gw_params['shipping_control'] == SHIPPING_CONTROL_PREAUTH ? 'AUTH_ONLY' : 'AUTH_CAPTURE';
$post_fields['x_login'] = $gw_params['user_account'];
$post_fields['x_tran_key'] = $gw_params['transaction_key'];
if( $this->IsTestMode() ) $post_fields['x_test_request'] = 'True';
// -- Payment Details --
$names = explode(' ', $item_data['PaymentNameOnCard'], 2);
$post_fields['x_first_name'] = getArrayValue($names, 0);
$post_fields['x_last_name'] = getArrayValue($names, 1);
$post_fields['x_amount'] = sprintf('%.2f', $item_data['TotalAmount']);
$post_fields['x_company'] = $item_data['BillingCompany'];
$post_fields['x_card_num'] = $item_data['PaymentAccount'];
$post_fields['x_card_code'] = $item_data['PaymentCVV2'];
$post_fields['x_exp_date'] = $item_data['PaymentCCExpDate'];
$post_fields['x_address'] = $item_data['BillingAddress1'].' '.$item_data['BillingAddress2'];
$post_fields['x_city'] = $item_data['BillingCity'];
$post_fields['x_state'] = $item_data['BillingState'];
$post_fields['x_zip'] = $item_data['BillingZip'];
$recurring = getArrayValue($item_data, 'IsRecurringBilling') ? 'YES' : 'NO';
$post_fields['x_recurring_billing'] = $recurring;
$billing_email = $item_data['BillingEmail'];
if (!$billing_email) {
$billing_email = $this->Conn->GetOne(' SELECT Email FROM '.$this->Application->getUnitOption('u', 'TableName').'
WHERE PortalUserId = '.$this->Application->RecallVar('user_id'));
}
$post_fields['x_email'] = $billing_email;
$post_fields['x_phone'] = $item_data['BillingPhone'];
$post_fields['x_country'] = $cs_helper->getCountryIso( $item_data['BillingCountry'] );
$post_fields['x_cust_id'] = $item_data['PortalUserId'];
$post_fields['x_invoice_num'] = $item_data['OrderNumber'];
$post_fields['x_description'] = 'Invoice #'.$item_data['OrderNumber'];
$post_fields['x_email_customer'] = 'FALSE';
*/
// echo '<pre>';
// print_r($post_fields);
// exit;
$curl_helper = $this->Application->recallObject('CurlHelper');
/* @var $curl_helper kCurlHelper */
$curl_helper->SetPostData($post_fields);
$this->gw_responce = $curl_helper->Send($gw_params['submit_url']);
// echo $this->gw_responce;
// exit;
$gw_responce = $this->parseGWResponce(null, $gw_params);
// gw_error_msg: $gw_response['responce_reason_text']
// gw_error_code: $gw_response['responce_reason_code']
// echo '<pre>';
// print_r($this->parsed_responce);
// exit;
return (isset($gw_responce['ACK']) && (substr($gw_responce['ACK'], 0, 7) == 'Success')) ? true : false;
}
/**
* Perform SALE type transaction direct from php script wihtout redirecting to 3rd-party website
*
* @param Array $item_data
* @param Array $gw_params
* @return bool
*/
/*
function Charge($item_data, $gw_params)
{
$gw_responce = unserialize( $item_data['GWResult1'] );
if( $item_data['PortalUserId'] != $gw_responce['customer_id'] ) return false;
if( ( strtolower($gw_responce['transaction_type']) == 'auth_only') )
{
$post_fields = Array();
// -- Login Information --
$post_fields['x_version'] = '3.1';
$post_fields['x_delim_data'] = 'True';
$post_fields['x_encap_char'] = $gw_params['encapsulate_char'];
$post_fields['x_relay_response'] = 'False';
$post_fields['x_type'] = 'PRIOR_AUTH_CAPTURE'; // $gw_params['shipping_control'] == SHIPPING_CONTROL_PREAUTH ? 'PRIOR_AUTH_CAPTURE' : 'AUTH_CAPTURE'; // AUTH_CAPTURE not fully impletemnted/needed here
$post_fields['x_login'] = $gw_params['user_account'];
$post_fields['x_tran_key'] = $gw_params['transaction_key'];
$post_fields['x_trans_id'] = $gw_responce['transaction_id'];
if( $this->IsTestMode() ) $post_fields['x_test_request'] = 'True';
$curl_helper = $this->Application->recallObject('CurlHelper');
$curl_helper->SetPostData($post_fields);
$this->gw_responce = $curl_helper->Send($gw_params['submit_url']);
$gw_responce = $this->parseGWResponce(null, $gw_params);
// gw_error_msg: $gw_response['responce_reason_text']
// gw_error_code: $gw_response['responce_reason_code']
return (is_numeric($gw_responce['responce_code']) && $gw_responce['responce_code'] != 1 && !$this->IsTestMode()) ? false : true;
}
else
{
return true;
}
}
*/
/**
* Parse previosly saved gw responce into associative array
*
* @param string $gw_responce
* @param Array $gw_params
* @return Array
*/
function parseGWResponce($gw_responce = null, $gw_params)
{
if( !isset($gw_responce) ) $gw_responce = $this->gw_responce;
if ($this->Application->isDebugMode()) {
$this->Application->Debugger->appendHTML('Curl Error #'.$GLOBALS['curl_errorno'].'; Error Message: '.$GLOBALS['curl_error']);
$this->Application->Debugger->appendHTML('Authorize.Net Responce:');
$this->Application->Debugger->dumpVars($gw_responce);
}
$a_responce = explode('&', $gw_responce);
$ret = Array();
foreach($a_responce as $field)
{
$pos = strpos($field, '=');
if ($pos) {
$ret[substr($field, 0, $pos)] = urldecode(substr($field, $pos + 1));
}
}
$this->parsed_responce = $ret;
// $ret['unparsed'] = $gw_responce;
return $ret;
}
function getGWResponce()
{
return serialize($this->parsed_responce);
}
function getErrorMsg()
{
return $this->parsed_responce['L_LONGMESSAGE0'];
}
function GetTestCCNumbers()
{
return array('370000000000002', '6011000000000012', '5424000000000015', '4007000000027', '4222222222222');
}
}
\ No newline at end of file
Index: branches/5.2.x/units/gateways/gw_classes/paybox.php
===================================================================
--- branches/5.2.x/units/gateways/gw_classes/paybox.php (revision 15567)
+++ branches/5.2.x/units/gateways/gw_classes/paybox.php (revision 15568)
@@ -1,182 +1,182 @@
<?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.
*/
require_once GW_CLASS_PATH.'/gw_base.php';
$class_name = 'kPayboxGW'; // for automatic installation
class kPayboxGW extends kGWBase
{
function InstallData()
{
$data = array(
'Gateway' => Array('Name' => 'Paybox.com', 'ClassName' => 'kPayboxGW', 'ClassFile' => 'paybox.php', 'RequireCCFields' => 0),
'ConfigFields' => Array(
'binary' => Array('Name' => 'API Executable', 'Type' => 'text', 'ValueList' => '', 'Default' => ''),
'params_file' => Array('Name' => 'OPT Params File', 'Type' => 'text', 'ValueList' => '', 'Default' => ''),
'site_number' => Array('Name' => 'Site Number', 'Type' => 'text', 'ValueList' => '', 'Default' => ''),
'rank_number' => Array('Name' => 'Rank Number', 'Type' => 'text', 'ValueList' => '', 'Default' => ''),
'identity_number' => Array('Name' => 'Identity Number', 'Type' => 'text', 'ValueList' => '', 'Default' => ''),
'currency_code' => Array('Name' => 'Currency Code', 'Type' => 'text', 'ValueList' => '', 'Default' => '978'),
'shipping_control' => Array('Name' => 'Shipping Control', 'Type' => 'select', 'ValueList' => '3=la_CreditDirect,4=la_CreditPreAuthorize', 'Default' => '3'),
)
);
return $data;
}
/**
* Returns payment form submit url
*
* @param Array $gw_params gateway params from payment type config
* @return string
*/
function getFormAction($gw_params)
{
return $gw_params['submit_url'];
}
/**
* Processed input data and convets it to fields understandable by gateway
*
* @param Array $item_data
* @param Array $tag_params additional params for gateway passed through tag
* @param Array $gw_params gateway params from payment type config
* @return Array
*/
function getHiddenFields($item_data, $tag_params, $gw_params)
{
$params['PBX_MODE'] = $gw_params['params_file'] ? '34' : '4';
$params['PBX_OUTPUT'] = 'B';
$params['PBX_SITE'] = $gw_params['site_number'];
$params['PBX_RANG'] = $gw_params['rank_number'];
$params['PBX_IDENTIFIANT'] = $gw_params['identity_number'];
$params['PBX_OPT'] = $gw_params['params_file'];
$params['PBX_DEVISE'] = $gw_params['currency_code']; // 978 for the euro. 840 for the US dollar.
$params['PBX_RETOUR'] = 'amount:M;reference:R;trans:T;autorization:A;subscription:B;payment:P;card:C;trans_id:S;country:Y;error:E;expiration:D';
$params['PBX_EFFECTUE'] = $this->Application->HREF($tag_params['return_template'],'',Array('pass'=>'m'));
$params['PBX_REFUSE'] = $this->Application->HREF($tag_params['cancel_template'],'',Array('pass'=>'m'));
$txt_amount = sprintf("%.2f", $item_data['TotalAmount']);
$params['PBX_TOTAL'] = str_replace( Array('.', ','), '', $txt_amount);
$params['PBX_CMD'] = $this->Application->GetSID().','.MD5($item_data['OrderId']);
/*$params['order_id'] = $item_data['OrderId'];
- $params['customer_ip_address'] = $_SERVER['REMOTE_ADDR'];
+ $params['customer_ip_address'] = $this->Application->getClientIp();
$params['customer_id'] = $item_data['PortalUserId'];*/
$billing_email = $item_data['BillingEmail'];
if (!$billing_email) {
$billing_email = $this->Conn->GetOne(' SELECT Email FROM '.$this->Application->getUnitOption('u', 'TableName').'
WHERE PortalUserId = '.$this->Application->RecallVar('user_id'));
}
$params['PBX_PORTEUR'] = $billing_email;
$params_str = '';
foreach ($params as $key => $val) {
$params_str .= ' '.$key . '="'.$val.'"';
}
$run_line = $gw_params['binary'].' '.$params_str;
// $run_line = escapeshellcmd($run_line);
// echo escapeshellcmd($run_line);
exec ($run_line, $rets);
$ret = implode("\n", $rets);
$ret = preg_replace('/^(.*)!!/is', '', $ret);
$ret = rtrim($ret, '!');
return '</form>'.$ret.'<form>';
}
function NeedPlaceButton($item_data, $tag_params, $gw_params)
{
return false;
}
function processNotification($gw_params)
{
$result = $this->Application->HttpQuery->GetParams();
$this->parsed_responce = $result;
list ($sid, $auth_code) = explode(',', $result['reference']);
$session = $this->Application->recallObject('Session');
$session->SID = $sid;
$order_id = $this->Conn->GetOne('SELECT OrderId FROM '.TABLE_PREFIX.'Orders WHERE md5(OrderId) = '.$this->Conn->qstr($auth_code));
$this->Application->SetVar('ord_id', $order_id);
$order = $this->Application->recallObject('ord');
$order->Load($order_id);
return $result['error'] === '00000' ? 1 : 0;
}
function parseGWResponce($str, $gw_params)
{
$response = explode ("!", $str);
$result = Array (
'code' => $response[1],
'error' => $response[2],
'merchant_id' => $response[3],
'merchant_country' => $response[4],
'amount' => $response[5],
'transaction_id' => $response[6],
'payment_means' => $response[7],
'transmission_date' => $response[8],
'payment_time' => $response[9],
'payment_date' => $response[10],
'response_code' => $response[11],
'payment_certificate' => $response[12],
'authorisation_id' => $response[13],
'currency_code' => $response[14],
'card_number' => $response[15],
'cvv_flag' => $response[16],
'cvv_response_code' => $response[17],
'bank_response_code' => $response[18],
'complementary_code' => $response[19],
'complementary_info' => $response[20],
'return_context' => $response[21],
'caddie' => $response[22],
'receipt_complement' => $response[23],
'merchant_language' => $response[24],
'language' => $response[25],
'customer_id' => $response[26],
'order_id' => $response[27],
'customer_email' => $response[28],
'customer_ip_address' => $response[29],
'capture_day' => $response[30],
'capture_mode' => $response[31],
'data' => $response[32],
);
$this->parsed_responce = $result;
return $result;
}
function getGWResponce()
{
return serialize($this->parsed_responce);
}
function getErrorMsg()
{
$msg = '';
if ($this->parsed_responce['response_code'] != '00000') {
$msg = 'Transaction failed';
}
return $msg;
}
}
\ No newline at end of file
Index: branches/5.2.x/units/gateways/gw_classes/rightconnect.php
===================================================================
--- branches/5.2.x/units/gateways/gw_classes/rightconnect.php (revision 15567)
+++ branches/5.2.x/units/gateways/gw_classes/rightconnect.php (revision 15568)
@@ -1,224 +1,224 @@
<?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.
*/
require_once GW_CLASS_PATH.'/gw_base.php';
class kGWRightConnect extends kGWBase
{
/* function Install()
{
$query = "INSERT INTO `".TABLE_PREFIX."Gateways` ( `GatewayId` , `Name` , `ClassName` , `ClassFile` , `RequireCCFields` )
VALUES (
'', 'RightConnect', 'kGWRightConnect', 'rightconnect.php', '1'
)";
$this->Conn->Query($query);
$id = $this->Conn->getInsertID();
$query = "INSERT INTO `".TABLE_PREFIX."GatewayConfigFields` ( `GWConfigFieldId` , `SystemFieldName` , `FieldName` , `ElementType` , `ValueList` , `GatewayId` )
VALUES (
'', 'submit_url', 'Submit URL', 'text', '', '$id'
), (
'', 'user_account', 'User Account', 'text', '', '$id'
), (
'', 'password', 'User Account', 'text', '', '$id'
),
(
'', 'encapsulate_char', 'Encapsualte Char', 'text', '', '$id'
)";
$this->Conn->Query($query);
}*/
function DirectPayment($item_data, $gw_params)
{
/*$post_fields["card_holder"] = "test test";
$post_fields["cardtype"] = "Visa";
$post_fields["card_number"] = "4444333322221111";
$post_fields["cvv2"] = "123";
$post_fields["exp_mon"] = "05";
$post_fields["exp_year"] = "05";
$post_fields["address1"] = "test";
$post_fields["city"] = "test";
$post_fields["state"] = "CA";
$post_fields["zip"] = "90069";
$post_fields["country"] = "USA";
$post_fields["firstname"] = "test";
$post_fields["lastname"] = "test";
$post_fields["email"] = "customer@yourdomain.com";
$post_fields["merchant_account"] = "demo";
$post_fields["trans_amount"] = "8.98";
$post_fields["user1"] = "DELIM";
$post_fields["ALIAS"] = "www.yourdomain.com";
- $post_fields["customer_ip"] = $_SERVER['REMOTE_ADDR'];
+ $post_fields["customer_ip"] = $this->Application->getClientIp();
$post_fields["ADMIN_EMAIL"] = "admin@yourdomain.com";*/
$post_fields = Array();
// -- Login Information --
//$post_fields['x_version'] = '3.1';
$post_fields["user1"] = "DELIM";
//$post_fields['x_type'] = $gw_params['shipping_control'] == SHIPPING_CONTROL_PREAUTH ? 'AUTH_ONLY' : 'AUTH_CAPTURE';
$post_fields['merchant_account'] = $gw_params['user_account'];
$post_fields['merchant_pass'] = $gw_params['password'];
if( $this->IsTestMode() ) $post_fields['x_test_request'] = 'True';
// -- Payment Details --
$names = explode(' ', $item_data['BillingTo'], 2);
$post_fields['firstname'] = getArrayValue($names, 0);
$post_fields['lastname'] = getArrayValue($names, 1);
$post_fields['trans_amount'] = sprintf('%.2f', $item_data['TotalAmount']);
//$post_fields['x_company'] = $item_data['BillingCompany'];
$post_fields['card_number'] = $item_data['PaymentAccount'];
$post_fields['card_holder'] = $item_data['PaymentNameOnCard'];
$post_fields['cvv2'] = $item_data['PaymentCVV2'];
list($exp_mon,$exp_year) = explode('/', $item_data['PaymentCCExpDate']);
$post_fields['exp_mon'] = $exp_mon;
$post_fields['exp_year'] = $exp_year;
$post_fields['address1'] = $item_data['BillingAddress1'];
$post_fields['address2'] = $item_data['BillingAddress2'];
$post_fields['city'] = $item_data['BillingCity'];
$post_fields['state'] = $item_data['BillingState'];
$post_fields['zip'] = $item_data['BillingZip'];
$post_fields['country'] = $item_data['BillingCountry'];
$post_fields['user2'] = $item_data['PortalUserId'];
$post_fields['user3'] = $item_data['OrderNumber'];
$post_fields['customer_email'] = 'FALSE';
$post_fields['customer_addr'] = $item_data['OrderIP']; // according to fields list in doc
$post_fields['customer_ip'] = $item_data['OrderIP']; // according to example from doc
$post_fields["email"] = $item_data['BillingEmail'];
$post_fields["ADMIN_EMAIL"] = $this->Application->ConfigValue('DefaultEmailSender');
$curl_helper = $this->Application->recallObject('CurlHelper');
/* @var $curl_helper kCurlHelper */
$curl_helper->SetPostData($post_fields);
$this->gw_responce = $curl_helper->Send($gw_params['submit_url']);
$gw_responce = $this->parseGWResponce(null, $gw_params);
// gw_error_msg: $gw_response['responce_reason_text']
// gw_error_code: $gw_response['responce_reason_code']
return ($gw_responce['responce_code'] != 1) ? false : true;
}
/**
* Captures Authorized transaction by transaction ID
*
* @param Array $item_data
* @param Array $gw_params
* @return bool
*/
function Charge($item_data, $gw_params)
{
return true;
}
/**
* Parse previosly saved gw responce into associative array
*
* @param string $gw_responce
* @param Array $gw_params
* @return Array
*/
function parseGWResponce($gw_responce = null, $gw_params)
{
if( !isset($gw_responce) ) $gw_responce = $this->gw_responce;
$fields = Array(
'responce_code', // 0
'responce_sub_code', // 1
'responce_reason_code', // 2
'responce_reason_text', // 3
'approval_code', // 4
'avs_result_code', // 5
'transaction_id', // 6
'fraud_score', // 7
'not_used8', // 8
'amount', // 9
'not_used10', // 10
'transaction_type', // 11
'not_used_customer_id', // 12
'first_name', // 13
'last_name', // 14
'not_documented_empty_field', // 15 !!!!!!!!!!! DOES NOT MATCH DOCUMENTATION
'address', // 16
'city', // 17
'state', // 18
'zip', // 19
'country', // 20
'phone', // 21
'fax', // 22
'email', // 23
'ship_name', // 24
'not_used25', // 25
'not_used26', // 26
'ship_address', // 27
'ship_city', // 28
'ship_state', // 29
'ship_zip', // 30
'ship_country', // 31
'tax', // 32
'not_used32', // 33
'not_used33', // 34
'not_used34', // 35
'not_used35', // 36
'not_used36', // 37
'cvv_responce', // 38
'receipt_number', // 39
'passthru1', // 40
'passthru2', // 41
'passthru3', // 42
'passthru4', // 43
'passthru5', // 44
'passthru6', // 45
'passthru7', // 46
'passthru8', // 47
'passthru9', // 48
'passthru10', // 49
);
$encapsulate_char = $gw_params['encapsulate_char'];
if($encapsulate_char)
{
$ec_length = strlen($encapsulate_char);
$gw_responce = substr($gw_responce, $ec_length, $ec_length * -1);
}
$gw_responce = explode($encapsulate_char.','.$encapsulate_char, $gw_responce);
$ret = Array();
foreach($fields as $field_index => $field_name)
{
$ret[$field_name] = $gw_responce[$field_index];
unset($gw_responce[$field_index]);
}
$this->parsed_responce = $ret;
return kUtil::array_merge_recursive($ret, $gw_responce); // returns unparsed fields with they original indexes together with parsed ones
}
function getGWResponce()
{
return serialize($this->parsed_responce);
}
}
\ No newline at end of file
Index: branches/5.2.x/units/downloads/download_helper.php
===================================================================
--- branches/5.2.x/units/downloads/download_helper.php (revision 15567)
+++ branches/5.2.x/units/downloads/download_helper.php (revision 15568)
@@ -1,77 +1,77 @@
<?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 DownloadHelper extends kHelper {
function CheckAccess($file_id, $product_id)
{
$sql = 'SELECT FileAccessId FROM '.TABLE_PREFIX.'UserFileAccess
WHERE PortalUserId = '.$this->Application->RecallVar('user_id').'
AND ProductId = '.$product_id;
return $this->Conn->GetOne($sql);
}
function SendFile($file_id, $product_id)
{
$file_object = $this->Application->recallObject('file', null, Array('skip_autoload' => true));
/* @var $file_object kDBItem */
$sql = $file_id ?
'SELECT FileId, FilePath, RealPath, MIMEType FROM '.$this->Application->getUnitOption('file', 'TableName').'
WHERE FileId = '.$file_id :
'SELECT FileId, FilePath, RealPath, MIMEType FROM '.$this->Application->getUnitOption('file', 'TableName').'
WHERE ProductId = '.$product_id.' AND IsPrimary = 1';
$file_info = $this->Conn->GetRow($sql);
$field_options = $file_object->GetFieldOptions('RealPath');
$file_info['real_path'] = FULL_PATH.$field_options['upload_dir'].'/'.$file_info['RealPath'];
$file_info = $this->DoSendFile($file_info);
return $file_info;
}
function DoSendFile($file_info)
{
$this->Application->setContentType(kUtil::mimeContentType($file_info['real_path']), false);
header('Content-Disposition: attachment; filename="' . $file_info['FilePath'] . '"');
header('Content-Length: ' . filesize($file_info['real_path']));
$file_info['download_start'] = adodb_mktime();
readfile($file_info['real_path']);
flush();
$file_info['download_end'] = adodb_mktime(); // this is incorrect
define('SKIP_OUT_COMPRESSION', 1);
return $file_info;
}
function LogDownload($product_id, $file_info)
{
$down_object = $this->Application->recallObject('down', null, Array('skip_autoload' => true));
$user_object = $this->Application->recallObject('u.current');
$product_object = $this->Application->recallObject( 'p' );
$down_object->SetDBField('PortalUserId', $this->Application->RecallVar('user_id'));
$down_object->SetDBField('Username', $user_object->GetDBField('Username'));
$down_object->SetDBField('ProductId', $product_id);
$down_object->SetDBField('ProductName', $product_object->GetField('Name'));
$down_object->SetDBField('FileId', $file_info['FileId']);
$down_object->SetDBField('Filename', $file_info['FilePath']);
- $down_object->SetDBField('IPAddress', $_SERVER['REMOTE_ADDR']);
+ $down_object->SetDBField('IPAddress', $this->Application->getClientIp());
$down_object->SetDBField('StartedOn_date', $file_info['download_start']);
$down_object->SetDBField('StartedOn_time', $file_info['download_start']);
$down_object->Create();
}
}
\ No newline at end of file
Index: branches/5.2.x/units/shipping_quote_engines/usps.php
===================================================================
--- branches/5.2.x/units/shipping_quote_engines/usps.php (revision 15567)
+++ branches/5.2.x/units/shipping_quote_engines/usps.php (revision 15568)
@@ -1,1339 +1,1339 @@
<?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!');
define('MODULE_SHIPPING_USPS_TEXT_TITLE', 'United States Postal Service');
define('MODULE_SHIPPING_USPS_TEXT_DESCRIPTION', 'You will need to have registered an account with USPS. Click <a target="_blank" href="https://secure.shippingapis.com/registration/"><strong>HERE</strong></a> for registration details. USPS expects you to use pounds as weight measure for your products.');
define('MODULE_SHIPPING_USPS_TEXT_ERROR', 'An error occured with the USPS shipping calculations.<br>If you prefer to use USPS as your shipping method, please contact the store owner.');
define('MODULE_SHIPPING_USPS_TEXT_DAY', 'Day');
define('MODULE_SHIPPING_USPS_TEXT_DAYS', 'Days');
define('MODULE_SHIPPING_USPS_TEXT_WEEKS', 'Weeks');
define('MODULE_SHIPPING_USPS_STATUS', 'True'); // Do you want to offer USPS shipping?
define('MODULE_SHIPPING_USPS_SERVER', 'production'); // An account at USPS is needed to use the Production server // production othervise value may be 'test'
define('MODULE_SHIPPING_USPS_HANDLING', '0'); // Handling fee for this shipping method
define('MODULE_SHIPPING_USPS_TAX_CLASS', '0'); // Use the following tax class on the shipping fee
define('MODULE_SHIPPING_USPS_ZONE', '0'); // If a zone is selected, only enable this shipping method for that zone.
define('MODULE_SHIPPING_USPS_SORT_ORDER', '0'); // Sort order of display.
define('MODULE_SHIPPING_USPS_TYPES', 'PRIORITY, PARCEL'); // EXPRESS, FIRST CLASS, BMP, MEDIA 'Select the domestic services to be offered:
define('MODULE_SHIPPING_USPS_TYPES_INTL', 'EXPRESS MAIL INTERNATIONAL (EMS), EXPRESS MAIL INT, EXPRESS MAIL INT FLAT RATE ENV, PRIORITY MAIL INT, PRIORITY MAIL INT FLAT RATE ENV, PRIORITY MAILINT FLAT RATE BOX, FIRST-CLASS MAIL INT');// 'GLOBAL EXPRESS, GLOBAL EXPRESS NON-DOC RECT, GLOBAL EXPRESS NON-DOC NON-RECT, Select the international services to be offered:
define('MODULE_SHIPPING_USPS_OPTIONS', 'Display weight, Display transit time'); //
//configuration values for insurance
define('MODULE_SHIPPING_USPS_INS1', '1.65');// 'US/Canada insurance for totals $.01-$50.00
define('MODULE_SHIPPING_USPS_INS2', '2.05');// 'US/Canada insurance for totals $50.01-$100
define('MODULE_SHIPPING_USPS_INS3', '2.45');// 'US/Canada insurance for totals $100.01-$200
define('MODULE_SHIPPING_USPS_INS4', '4.60');// 'US/Canada insurance for totals $200.01-$300
define('MODULE_SHIPPING_USPS_INS5', '.90');// 'US/Canada insurance for every $100 over $300 (add)
define('MODULE_SHIPPING_USPS_INS6', '2.40');// 'International insurance for totals $.01-$50.00
define('MODULE_SHIPPING_USPS_INS7', '3.30');// 'International insurance for totals $50.01-$100
define('MODULE_SHIPPING_USPS_INS8', '4.20');// 'International insurance for totals $100.01-$200
define('MODULE_SHIPPING_USPS_INS9', '5.10');// 'International insurance for totals $200.01-$300
define('MODULE_SHIPPING_USPS_INS10', '.90');// 'International insurance for every $100 over $300 (add)
define('MODULE_SHIPPING_USPS_INSURE', 'True');// 'Insure packages shipped by USPS?
define('MODULE_SHIPPING_USPS_INSURE_TAX', 'True');// 'Insure tax on packages shipped by USPS?
class USPS extends ShippingQuoteEngine
{
var $countries, $pounds, $ounces, $insurance_cost = 0, $shipping_origin_country, $store_first_name, $store_last_name, $company_name, $store_name, $store_address1, $store_address2, $store_city, $store_state, $store_zip5, $store_zip4, $store_phone, $usps_userid;
var $order = Array();
var $types = Array();
var $intl_types = Array();
/**
* Path to a request log file
*
* @var string
*/
var $logFilePath = '';
/**
* Creates USPS processing class
*
*/
public function __construct()
{
parent::__construct();
$this->logFilePath = (defined('RESTRICTED') ? RESTRICTED : WRITEABLE . '/user_files') . '/usps.log';
// EXPRESS, FIRST CLASS, PRIORITY, PARCEL, BMP, MEDIA
$this->types = Array(
'EXPRESS' => 'Express Mail',
'FIRST CLASS' => 'First Class Mail',
'PRIORITY' => 'Priority Mail',
'PARCEL' => 'Parcel Post',
'BPM' => 'Bound Printed Matter',
'MEDIA' => 'Media Mail'
);
$this->intl_types = Array(
// 'GLOBAL EXPRESS' => 'Global Express Guaranteed',
// 'GLOBAL EXPRESS NON-DOC RECT' => 'Global Express Guaranteed Non-Document Rectangular',
// 'GLOBAL EXPRESS NON-DOC NON-RECT' => 'Global Express Guaranteed Non-Document Non-Rectangular',
'EXPRESS MAIL INT' => 'Express Mail International (EMS)',
'EXPRESS MAIL INT FLAT RATE ENV' => 'Express Mail International (EMS) Flat Rate Envelope',
'PRIORITY MAIL INT' => 'Priority Mail International',
'PRIORITY MAIL INT FLAT RATE ENV' => 'Priority Mail International Flat Rate Envelope',
'PRIORITY MAIL INT FLAT RATE BOX' => 'Priority Mail International Flat Rate Box',
'FIRST-CLASS MAIL INT' => 'First-Class Mail International'
);
// get 2-symbol country code
$country = $this->Application->ConfigValue('Comm_Shipping_Country');
if ($country != '') {
$this->shipping_origin_country = $this->GetUSPSCountry($country, '');
}
$contact_name = trim($this->_prepare_xml_param($this->Application->ConfigValue('Comm_Contacts_Name')));
$split_pos = strpos($contact_name, ' ');
if ($split_pos === false) {
$this->store_first_name = $contact_name;
$this->store_last_name = '';
} else {
$this->store_first_name = substr($contact_name, 0, $split_pos);
$this->store_last_name = trim(substr($contact_name, $split_pos));
}
$this->company_name = $this->_prepare_xml_param($this->Application->ConfigValue('Comm_CompanyName'));
$this->store_name = $this->_prepare_xml_param($this->Application->ConfigValue('Comm_StoreName'));
$this->store_address1 = $this->_prepare_xml_param($this->Application->ConfigValue('Comm_Shipping_AddressLine1'));
$this->store_address2 = $this->_prepare_xml_param($this->Application->ConfigValue('Comm_Shipping_AddressLine2'));
if ($this->store_address2 == '') {
$this->store_address2 = $this->store_address1;
$this->store_address1 = '';
}
$this->store_city = $this->_prepare_xml_param($this->Application->ConfigValue('Comm_Shipping_City'));
$this->store_state = $this->_prepare_xml_param($this->Application->ConfigValue('Comm_Shipping_State'));
$zip = $this->_prepare_xml_param($this->Application->ConfigValue('Comm_Shipping_ZIP'));
$this->store_zip5 = substr($zip, 0, 5);
$this->store_zip4 = trim(substr($zip, 6), '-');
$this->store_phone = $this->_prepare_xml_param($this->Application->ConfigValue('Comm_Contacts_Phone'));
// get username and password fron config.
$a_params = $this->LoadParams();
$this->usps_userid = $a_params['AccountLogin'];
// Note by Erik: DO NOT CHANGE THIS ARRAY. It's values are sent to USPS service and any changes may impact class main functionality.
$this->countries = array(
'AF' => 'Afghanistan',
'AL' => 'Albania',
'DZ' => 'Algeria',
'AD' => 'Andorra',
'AO' => 'Angola',
'AI' => 'Anguilla',
'AG' => 'Antigua and Barbuda',
'AR' => 'Argentina',
'AM' => 'Armenia',
'AW' => 'Aruba',
'AU' => 'Australia',
'AT' => 'Austria',
'AZ' => 'Azerbaijan',
'BS' => 'Bahamas',
'BH' => 'Bahrain',
'BD' => 'Bangladesh',
'BB' => 'Barbados',
'BY' => 'Belarus',
'BE' => 'Belgium',
'BZ' => 'Belize',
'BJ' => 'Benin',
'BM' => 'Bermuda',
'BT' => 'Bhutan',
'BO' => 'Bolivia',
'BA' => 'Bosnia-Herzegovina',
'BW' => 'Botswana',
'BR' => 'Brazil',
'VG' => 'British Virgin Islands',
'BN' => 'Brunei Darussalam',
'BG' => 'Bulgaria',
'BF' => 'Burkina Faso',
'MM' => 'Burma',
'BI' => 'Burundi',
'KH' => 'Cambodia',
'CM' => 'Cameroon',
'CA' => 'Canada',
'CV' => 'Cape Verde',
'KY' => 'Cayman Islands',
'CF' => 'Central African Republic',
'TD' => 'Chad',
'CL' => 'Chile',
'CN' => 'China',
'CX' => 'Christmas Island (Australia)',
'CC' => 'Cocos Island (Australia)',
'CO' => 'Colombia',
'KM' => 'Comoros',
'CG' => 'Congo (Brazzaville),Republic of the',
'ZR' => 'Congo, Democratic Republic of the',
'CK' => 'Cook Islands (New Zealand)',
'CR' => 'Costa Rica',
'CI' => 'Cote d\'Ivoire (Ivory Coast)',
'HR' => 'Croatia',
'CU' => 'Cuba',
'CY' => 'Cyprus',
'CZ' => 'Czech Republic',
'DK' => 'Denmark',
'DJ' => 'Djibouti',
'DM' => 'Dominica',
'DO' => 'Dominican Republic',
'TP' => 'East Timor (Indonesia)',
'EC' => 'Ecuador',
'EG' => 'Egypt',
'SV' => 'El Salvador',
'GQ' => 'Equatorial Guinea',
'ER' => 'Eritrea',
'EE' => 'Estonia',
'ET' => 'Ethiopia',
'FK' => 'Falkland Islands',
'FO' => 'Faroe Islands',
'FJ' => 'Fiji',
'FI' => 'Finland',
'FR' => 'France',
'GF' => 'French Guiana',
'PF' => 'French Polynesia',
'GA' => 'Gabon',
'GM' => 'Gambia',
'GE' => 'Georgia, Republic of',
'DE' => 'Germany',
'GH' => 'Ghana',
'GI' => 'Gibraltar',
'GB' => 'Great Britain and Northern Ireland',
'GR' => 'Greece',
'GL' => 'Greenland',
'GD' => 'Grenada',
'GP' => 'Guadeloupe',
'GT' => 'Guatemala',
'GN' => 'Guinea',
'GW' => 'Guinea-Bissau',
'GY' => 'Guyana',
'HT' => 'Haiti',
'HN' => 'Honduras',
'HK' => 'Hong Kong',
'HU' => 'Hungary',
'IS' => 'Iceland',
'IN' => 'India',
'ID' => 'Indonesia',
'IR' => 'Iran',
'IQ' => 'Iraq',
'IE' => 'Ireland',
'IL' => 'Israel',
'IT' => 'Italy',
'JM' => 'Jamaica',
'JP' => 'Japan',
'JO' => 'Jordan',
'KZ' => 'Kazakhstan',
'KE' => 'Kenya',
'KI' => 'Kiribati',
'KW' => 'Kuwait',
'KG' => 'Kyrgyzstan',
'LA' => 'Laos',
'LV' => 'Latvia',
'LB' => 'Lebanon',
'LS' => 'Lesotho',
'LR' => 'Liberia',
'LY' => 'Libya',
'LI' => 'Liechtenstein',
'LT' => 'Lithuania',
'LU' => 'Luxembourg',
'MO' => 'Macao',
'MK' => 'Macedonia, Republic of',
'MG' => 'Madagascar',
'MW' => 'Malawi',
'MY' => 'Malaysia',
'MV' => 'Maldives',
'ML' => 'Mali',
'MT' => 'Malta',
'MQ' => 'Martinique',
'MR' => 'Mauritania',
'MU' => 'Mauritius',
'YT' => 'Mayotte (France)',
'MX' => 'Mexico',
'MD' => 'Moldova',
'MC' => 'Monaco (France)',
'MN' => 'Mongolia',
'MS' => 'Montserrat',
'MA' => 'Morocco',
'MZ' => 'Mozambique',
'NA' => 'Namibia',
'NR' => 'Nauru',
'NP' => 'Nepal',
'NL' => 'Netherlands',
'AN' => 'Netherlands Antilles',
'NC' => 'New Caledonia',
'NZ' => 'New Zealand',
'NI' => 'Nicaragua',
'NE' => 'Niger',
'NG' => 'Nigeria',
'KP' => 'North Korea (Korea, Democratic People\'s Republic of)',
'NO' => 'Norway',
'OM' => 'Oman',
'PK' => 'Pakistan',
'PA' => 'Panama',
'PG' => 'Papua New Guinea',
'PY' => 'Paraguay',
'PE' => 'Peru',
'PH' => 'Philippines',
'PN' => 'Pitcairn Island',
'PL' => 'Poland',
'PT' => 'Portugal',
'QA' => 'Qatar',
'RE' => 'Reunion',
'RO' => 'Romania',
'RU' => 'Russia',
'RW' => 'Rwanda',
'SH' => 'Saint Helena',
'KN' => 'Saint Kitts (St. Christopher and Nevis)',
'LC' => 'Saint Lucia',
'PM' => 'Saint Pierre and Miquelon',
'VC' => 'Saint Vincent and the Grenadines',
'SM' => 'San Marino',
'ST' => 'Sao Tome and Principe',
'SA' => 'Saudi Arabia',
'SN' => 'Senegal',
'YU' => 'Serbia-Montenegro',
'SC' => 'Seychelles',
'SL' => 'Sierra Leone',
'SG' => 'Singapore',
'SK' => 'Slovak Republic',
'SI' => 'Slovenia',
'SB' => 'Solomon Islands',
'SO' => 'Somalia',
'ZA' => 'South Africa',
'GS' => 'South Georgia (Falkland Islands)',
'KR' => 'South Korea (Korea, Republic of)',
'ES' => 'Spain',
'LK' => 'Sri Lanka',
'SD' => 'Sudan',
'SR' => 'Suriname',
'SZ' => 'Swaziland',
'SE' => 'Sweden',
'CH' => 'Switzerland',
'SY' => 'Syrian Arab Republic',
'TW' => 'Taiwan',
'TJ' => 'Tajikistan',
'TZ' => 'Tanzania',
'TH' => 'Thailand',
'TG' => 'Togo',
'TK' => 'Tokelau (Union) Group (Western Samoa)',
'TO' => 'Tonga',
'TT' => 'Trinidad and Tobago',
'TN' => 'Tunisia',
'TR' => 'Turkey',
'TM' => 'Turkmenistan',
'TC' => 'Turks and Caicos Islands',
'TV' => 'Tuvalu',
'UG' => 'Uganda',
'UA' => 'Ukraine',
'AE' => 'United Arab Emirates',
'UY' => 'Uruguay',
'UZ' => 'Uzbekistan',
'VU' => 'Vanuatu',
'VA' => 'Vatican City',
'VE' => 'Venezuela',
'VN' => 'Vietnam',
'WF' => 'Wallis and Futuna Islands',
'WS' => 'Western Samoa',
'YE' => 'Yemen',
'ZM' => 'Zambia',
'ZW' => 'Zimbabwe'
);
$this->countryinsure = array(
'AF' => 0,
'AL' => 0,
'DZ' => 2185,
'AD' => 5000,
'AO' => 0,
'AI' => 415,
'AG' => 60,
'AR' => 5000,
'AM' => 1350,
'AW' => 830,
'AU' => 3370,
'AT' => 5000,
'AZ' => 5000,
'BS' => 2795,
'BH' => 0,
'BD' => 5000,
'BB' => 220,
'BY' => 1323,
'BE' => 5000,
'BZ' => 1600,
'BJ' => 170,
'BM' => 440,
'BT' => 440,
'BO' => 0,
'BA' => 5000,
'BW' => 145,
'BR' => 5000,
'VG' => 165,
'BN' => 4405,
'BG' => 1030,
'BF' => 530,
'MM' => 4045,
'BI' => 790,
'KH' => 0,
'CM' => 5000,
'CA' => 675,
'CV' => 0,
'KY' => 0,
'CF' => 4405,
'TD' => 440,
'CL' => 0,
'CN' => 1130,
'CX' => 3370,
'CC' => 3370,
'CO' => 0,
'KM' => 690,
'CG' => 1685,
'ZR' => 0,
'CK' => 980,
'CR' => 0,
'CI' => 5000,
'HR' => 5000,
'CU' => 0,
'CY' => 5000,
'CZ' => 5000,
'DK' => 5000,
'DJ' => 880,
'DM' => 0,
'DO' => 0,
'TP' => 0,
'EC' => 0,
'EG' => 1685,
'SV' => 0,
'GQ' => 0,
'ER' => 0,
'EE' => 2020,
'ET' => 1000,
'FK' => 510,
'FO' => 5000,
'FJ' => 600,
'FI' => 5000,
'FR' => 5000,
'GF' => 5000,
'PF' => 1015,
'GA' => 485,
'GM' => 2575,
'GE' => 1350,
'DE' => 5000,
'GH' => 5000,
'GI' => 5000,
'GB' => 857,
'GR' => 5000,
'GL' => 5000,
'GD' => 350,
'GP' => 5000,
'GT' => 0,
'GN' => 875,
'GW' => 21,
'GY' => 10,
'HT' => 0,
'HN' => 0,
'HK' => 5000,
'HU' => 5000,
'IS' => 5000,
'IN' => 2265,
'ID' => 0,
'IR' => 0,
'IQ' => 0,
'IE' => 5000,
'IL' => 0,
'IT' => 5000,
'JM' => 0,
'JP' => 5000,
'JO' => 0,
'KZ' => 5000,
'KE' => 815,
'KI' => 0,
'KW' => 1765,
'KG' => 1350,
'LA' => 0,
'LV' => 1350,
'LB' => 440,
'LS' => 440,
'LR' => 440,
'LY' => 0,
'LI' => 5000,
'LT' => 5000,
'LU' => 5000,
'MO' => 4262,
'MK' => 2200,
'MG' => 675,
'MW' => 50,
'MY' => 1320,
'MV' => 0,
'ML' => 950,
'MT' => 5000,
'MQ' => 5000,
'MR' => 635,
'MU' => 270,
'YT' => 5000,
'MX' => 0,
'MD' => 1350,
'MC' => 5000,
'MN' => 440,
'MS' => 2200,
'MA' => 5000,
'MZ' => 0,
'NA' => 4405,
'NR' => 220,
'NP' => 0,
'NL' => 5000,
'AN' => 830,
'NC' => 1615,
'NZ' => 980,
'NI' => 440,
'NE' => 810,
'NG' => 205,
'KP' => 0,
'NO' => 0,
'OM' => 575,
'PK' => 270,
'PA' => 0,
'PG' => 445,
'PY' => 0,
'PE' => 0,
'PH' => 270,
'PN' => 0,
'PL' => 1350,
'PT' => 5000,
'QA' => 2515,
'RE' => 5000,
'RO' => 5000,
'RU' => 5000,
'RW' => 0,
'SH' => 170,
'KN' => 210,
'LC' => 400,
'PM' => 5000,
'VC' => 130,
'SM' => 5000,
'ST' => 440,
'SA' => 0,
'SN' => 865,
'YU' => 5000,
'SC' => 0,
'SL' => 0,
'SG' => 4580,
'SK' => 5000,
'SI' => 4400,
'SB' => 0,
'SO' => 440,
'ZA' => 1760,
'GS' => 510,
'KR' => 5000,
'ES' => 5000,
'LK' => 35,
'SD' => 0,
'SR' => 535,
'SZ' => 560,
'SE' => 5000,
'CH' => 5000,
'SY' => 3080,
'TW' => 1350,
'TJ' => 1350,
'TZ' => 230,
'TH' => 1350,
'TG' => 2190,
'TK' => 295,
'TO' => 515,
'TT' => 930,
'TN' => 2200,
'TR' => 880,
'TM' => 675,
'TC' => 0,
'TV' => 4715,
'UG' => 0,
'UA' => 5000,
'AE' => 5000,
'UY' => 0,
'UZ' => 5000,
'VU' => 0,
'VA' => 5000,
'VE' => 0,
'VN' => 0,
'WF' => 1615,
'WS' => 295,
'YE' => 0,
'ZM' => 540,
'ZW' => 600,
'US' => 5000
);
}
function SetInsurance()
{
$this->insurance_cost = 0;
// Insurance module by Kevin Shelton
// divide the value of the order among the packages based on the order total or subtotal depending on whether or not you have configured to insure tax
$shipping_weight = $this->order['ShippingWeight'];
$shipping_num_boxes = $this->order['ShippingNumBoxes'];
$costperpkg = $this->order['SubTotal'] / $shipping_num_boxes;
// retrieve the maximum allowed insurance for the destination country and if the package value exceeds it then set package value to the maximum allowed
$maxins = $this->countryinsure[$this->order['ShippingCountry']];
if ($costperpkg > $maxins) $costperpkg = $maxins;
// if insurance not allowed for destination or insurance is turned off add nothing to shipping cost
if (($maxins == 0) || (MODULE_SHIPPING_USPS_INSURE == 'False')) {
$insurance = 0;
}
// US and Canada share the same insurance calculation (though not the same maximum)
else if (($this->order['ShippingCountry'] == 'US') || ($this->order['ShippingCountry'] == 'CA'))
{
if ($costperpkg<=50) {
$insurance=MODULE_SHIPPING_USPS_INS1;
}
else if ($costperpkg<=100) {
$insurance=MODULE_SHIPPING_USPS_INS2;
}
else if ($costperpkg<=200) {
$insurance=MODULE_SHIPPING_USPS_INS3;
}
else if ($costperpkg<=300) {
$insurance=MODULE_SHIPPING_USPS_INS4;
}
else {
$insurance = MODULE_SHIPPING_USPS_INS4 + ((ceil($costperpkg/100) -3) * MODULE_SHIPPING_USPS_INS5);
}
}
// if insurance allowed and is not US or Canada then calculate international insurance
else {
if ($costperpkg<=50) {
$insurance=MODULE_SHIPPING_USPS_INS6;
}
else if ($costperpkg<=100) {
$insurance=MODULE_SHIPPING_USPS_INS7;
}
else if ($costperpkg<=200) {
$insurance=MODULE_SHIPPING_USPS_INS8;
}
else if ($costperpkg<=300) {
$insurance=MODULE_SHIPPING_USPS_INS9;
}
else {
$insurance = MODULE_SHIPPING_USPS_INS9 + ((ceil($costperpkg/100) - 3) * MODULE_SHIPPING_USPS_INS10);
}
}
// usps doesnt accept zero weight
$shipping_weight = ($shipping_weight < 0.1 ? 0.1 : $shipping_weight);
$shipping_pounds = floor ($shipping_weight);
$shipping_ounces = round(16 * ($shipping_weight - floor($shipping_weight)));
$this->_setWeight($shipping_pounds, $shipping_ounces);
// Added by Kevin Chen (kkchen@uci.edu); Fixes the Parcel Post Bug July 1, 2004
// Refer to http://www.usps.com/webtools/htm/Domestic-Rates.htm documentation
// Thanks Ryan
if($shipping_pounds > 35 || ($shipping_pounds == 0 && $shipping_ounces < 6)){
$this->_setMachinable('False');
}
else{
$this->_setMachinable('True');
}
$this->insurance_cost = $insurance;
// End Kevin Chen July 1, 2004
}
function _setService($service)
{
$this->service = $service;
}
function _setWeight($pounds, $ounces=0)
{
$this->pounds = $pounds;
$this->ounces = $ounces;
}
function _setContainer($container)
{
$this->container = $container;
}
function _setSize($size)
{
$this->size = $size;
}
function _setMachinable($machinable)
{
$this->machinable = $machinable;
}
function PhoneClean($phone)
{
$res = preg_replace('/[(]|[)]|[\-]|[ ]|[#]|[\.]|[a-z](.*)|[A-Z](.*)/g', '', $phone);
if ( strlen($res) > 10 ) {
$res = substr($res, 0, 10);
}
return $res != '' ? $res : $phone;
}
function GetQuote($method = '')
{
if ( isset($this->types[$method]) || in_array($method, $this->intl_types)) {
$this->_setService($method);
}
$this -> _setContainer('None');
$this -> _setSize('REGULAR');
$this -> SetInsurance(); // ???
if ($this->order['ShippingCountry'] == $this->shipping_origin_country) {
$request='<?xml version="1.0"?>';
// PASSWORD="'.$this->usps_password.'"
$request.= '<RateV3Request USERID="'.$this->usps_userid.'">';
$services_count = 0;
if (isset($this->service)) {
$this->types = array($this->service => $this->types[$this->service]);
}
$dest_zip = str_replace(' ', '', $this->order['ShippingZip']);
$dest_zip = substr($dest_zip, 0, 5);
reset($this->types);
$allowed_types = explode(", ", MODULE_SHIPPING_USPS_TYPES);
while (list($key, $value) = each($this->types))
{
if ( !in_array($key, $allowed_types) ) continue;
$request .= '<Package ID="'.$services_count.'">'.
'<Service>'.$key.'</Service>'.
'<ZipOrigination>'.$this->store_zip5.'</ZipOrigination>'.
'<ZipDestination>'.$dest_zip.'</ZipDestination>'.
'<Pounds>'.$this->pounds.'</Pounds>'.
'<Ounces>'.$this->ounces.'</Ounces>'.
'<Size>'.$this->size.'</Size>'.
'<Machinable>'.$this->machinable.'</Machinable>'.
'</Package>';
$services_count++;
}
$request .= '</RateV3Request>';
$api_query = 'RateV3';
}
else {
$request = '<IntlRateRequest USERID="'.$this->usps_userid.'">'.
'<Package ID="0">'.
'<Pounds>'.$this->pounds.'</Pounds>'.
'<Ounces>'.$this->ounces.'</Ounces>'.
'<MailType>Package</MailType>'.
'<Country>'.$this->countries[$this->order['ShippingCountry']].'</Country>'.
'</Package>'.
'</IntlRateRequest>';
$api_query = 'IntlRate';
}
$request = 'API='.$api_query.'&XML=' . urlencode($request);
$body = $this->PostQuery($request);
$body = str_replace(chr(146), '', $body); // for bad `
// check for errors
if (strpos($body, '<Error>') !== false) {
$errors = Array ();
preg_match_all('/<Number>(.*?)<\/Number>/s', $body, $error_numbers);
preg_match_all('/<Description>(.*?)<\/Description>/s', $body, $error_descriptions);
foreach ($error_numbers[1] as $index => $error_number) {
$errors[$index] = $error_descriptions[1][$index];
if ($this->Application->isDebugMode()) {
$errors[$index] .= ' (' . $error_number . ')';
}
}
$errors = array_unique($errors); // we may have same errors on many packages, so don't show duplicates
return Array('error' => implode('<br/>', $errors));
}
// parse response
$xml_helper = $this->Application->recallObject('kXMLHelper');
/* @var $xml_helper kXMLHelper */
$root_node =& $xml_helper->Parse($body);
/* @var $root_node kXMLNode */
$rates = Array();
// Domestic shipping
if ($this->order['ShippingCountry'] == $this->shipping_origin_country) {
$i = 0;
$postage_node =& $root_node->FindChild('Package');
do {
// $parcel_node =& $postage_node->firstChild;
$service = $postage_node->FindChildValue('MailService');
if ( $service != '' ) {
$i++;
$rates[$i] = Array();
$rates[$i]['Title'] = $service;
$rates[$i]['Rate'] = $this->insurance_cost + $postage_node->FindChildValue('Rate');
}
}
while ( $postage_node =& $postage_node->NextSibling());
}
else {
// for International Rates !!!
$allowed_types = array();
foreach( explode(", ", MODULE_SHIPPING_USPS_TYPES_INTL) as $value ) {
$allowed_types[$value] = $this->intl_types[$value];
}
$i = 0;
$service_node =& $root_node->FindChild('Service');
do {
$service = trim($service_node->FindChildValue('SvcDescription'));
if( !in_array($service, $allowed_types) ) continue;
$i++;
if ( $service_node->FindChildValue('MaxWeight') >= $this->pounds ) {
$rates[$i] = Array();
$rates[$i]['Title'] = $service;
$rates[$i]['MaxDimensions'] = $service_node->FindChildValue('MaxDimensions');
$rates[$i]['MaxWeight'] = $service_node->FindChildValue('MaxWeight');
$rates[$i]['SvcCommitments'] = $service_node->FindChildValue('SvcCommitments');
$rates[$i]['Rate'] = $this->insurance_cost + $service_node->FindChildValue('Postage');
}
}
while ( $service_node =& $service_node->NextSibling());
}
// print_r($rates);
// die('here');
return $rates;
}
function PostOrder()
{
$request='';
$base_request = '';
$this->SetInsurance();
// $this->order['ShippingCountry'] = $this->GetUSPSCountry($this->order['ShippingCountry']);
// Domestic Order
if ($this->order['ShippingCountry'] == $this->shipping_origin_country) {
// $dest_zip = str_replace(' ', '', $this->order['ShippingZip5']);
$this->order['ShippingZip5'] = substr($this->order['ShippingZip5'], 0, 5);
$WeightInOunces = floor($this->pounds * 16 + $this->ounces);
$base_request ='
<Option>1</Option>
<ImageParameters></ImageParameters>
<FromName>'.$this->store_name.'</FromName>
<FromFirm>'.$this->company_name.'</FromFirm>
<FromAddress1>'.$this->store_address1.'</FromAddress1>
<FromAddress2>'.$this->store_address2.'</FromAddress2>
<FromCity>'.$this->store_city.'</FromCity>
<FromState>'.$this->store_state.'</FromState>
<FromZip5>'.$this->store_zip5.'</FromZip5>
<FromZip4>'.$this->store_zip4.'</FromZip4>
<ToName>'.$this->order['FirstName'].' '.$this->order['LastName'].'</ToName>
<ToFirm>'.$this->order['ShippingCompany'].'</ToFirm>
<ToAddress1>'.$this->order['ShippingAddress2'].'</ToAddress1>
<ToAddress2>'.$this->order['ShippingAddress1'].'</ToAddress2>
<ToCity>'.$this->order['ShippingCity'].'</ToCity>
<ToState>'.$this->order['ShippingState'].'</ToState>
<ToZip5>'.$this->order['ShippingZip5'].'</ToZip5>
<ToZip4>'.$this->order['ShippingZip4'].'</ToZip4>
<WeightInOunces>'.$WeightInOunces.'</WeightInOunces>
<ServiceType>'.$this->order['ShippingService'].'</ServiceType>
<ImageType>PDF</ImageType>
<LabelDate>'.date('m/d/Y',time()).'</LabelDate>
<CustomerRefNo></CustomerRefNo>
<AddressServiceRequested></AddressServiceRequested>
<SenderName></SenderName><SenderEMail></SenderEMail>
<RecipientName></RecipientName>
<RecipientEMail></RecipientEMail>
';
$api_query = 'DeliveryConfirmationV3';
$xml_request = 'DeliveryConfirmationV3.0Request';
}
else {
// International Order(s)
$shipping_service = strtolower($this->order['ShippingService']);
$base_request = '<Option/>
<ImageParameters/>
<FromFirstName>'.$this->store_first_name.'</FromFirstName>
<FromLastName>'.$this->store_last_name.'</FromLastName>
<FromFirm>'.$this->company_name.'</FromFirm>
<FromAddress1>'.$this->store_address1.'</FromAddress1>
<FromAddress2>'.$this->store_address2.'</FromAddress2>
<FromCity>'.$this->store_city.'</FromCity>
<FromState>'.$this->store_state.'</FromState>
<FromZip5>'.$this->store_zip5.'</FromZip5>
<FromPhone>'.$this->PhoneClean($this->store_phone).'</FromPhone>
<ToName>'.$this->order['FirstName'].' '.$this->order['LastName'].'</ToName>
<ToFirm>'.$this->order['ShippingCompany'].'</ToFirm>
<ToAddress1></ToAddress1>
<ToAddress2>'.$this->order['ShippingAddress2'].'</ToAddress2>
<ToAddress3>'.$this->order['ShippingAddress1'].'</ToAddress3>
<ToCity>'.$this->order['ShippingCity'].'</ToCity>';
if ( $this->order['ShippingProvince'] != '' ) {
$base_request.='
<ToProvince>'.$this->order['ShippingProvince'].'</ToProvince>';
}
$base_request.='
<ToCountry>'.$this->countries[$this->order['ShippingCountry']].'</ToCountry>
<ToPostalCode>'.$this->order['ShippingZip'].'</ToPostalCode>
<ToPOBoxFlag>N</ToPOBoxFlag>
<ToPhone>'.$this->PhoneClean($this->order['ShippingPhone']).'</ToPhone>
<ToFax>'.$this->PhoneClean($this->order['ShippingFax']).'</ToFax>
<ToEmail>'.$this->order['Email'].'</ToEmail>
<ShippingContents>';
// add items
foreach ( $this->order['Items'] as $k => $value ) {
$base_request.='
<ItemDetail>
<Description>Computer Parts</Description>
<Quantity>'.$value['Qty'].'</Quantity>
<Value>'.($value['Price'] * $value['Qty']).'</Value>
<NetPounds>'.$value['NetPounds'].'</NetPounds>
<NetOunces>'.$value['NetOunces'].'</NetOunces>
<HSTariffNumber>123456</HSTariffNumber>
<CountryOfOrigin>United States</CountryOfOrigin>
</ItemDetail>';
}
// end add items
$base_request.='
</ShippingContents>
<GrossPounds>'.$this->pounds.'</GrossPounds>
<GrossOunces>'.$this->ounces.'</GrossOunces>
<ContentType>MERCHANDISE</ContentType>
<Agreement>Y</Agreement>
<InvoiceNumber>'.$this->order['InvoiceNumber'].'</InvoiceNumber>
<ImageType>PDF</ImageType>
<ImageLayout>ALLINONEFILE</ImageLayout>
<LabelDate>'.date('m/d/Y',time()).'</LabelDate>
';
if (strpos($shipping_service, 'express') !== false) {
$xml_request = 'ExpressMailIntlRequest';
$api_query = 'ExpressMailIntl';
}
elseif (strpos($shipping_service, 'priority') !== false) {
$xml_request = 'PriorityMailIntlRequest';
$api_query = 'PriorityMailIntl';
}
else {
$xml_request = 'FirstClassMailIntlRequest';
$api_query = 'FirstClassMailIntl';
}
}
$request.= '<'.$xml_request.' USERID="'.$this->usps_userid.'">';
$request.= $base_request;
$request.= '</'.$xml_request.'>';
// die($request);
$request = 'API='.$api_query.'&XML='.urlencode($request);
$body = $this->PostQuery($request, 1);
// check for errors
if (strpos($body, '<Error>') !== false) {
$errors = Array ();
preg_match_all('/<Number>(.*?)<\/Number>/s', $body, $error_numbers);
preg_match_all('/<Description>(.*?)<\/Description>/s', $body, $error_descriptions);
foreach ($error_numbers[1] as $index => $error_number) {
$errors[$index] = Array ('error_number' => $error_number, 'error_description' => $error_descriptions[1][$index]);
}
// TODO: find a way to return other error messages in same package as well
return $errors[0];
}
// parse response
$xml_helper = $this->Application->recallObject('kXMLHelper');
$root_node =& $xml_helper->Parse($body);
/* @var $root_node kXMLNode */
$Postage = 0;
$label_file = $TrackingNumber = $PostnetBarCode = '';
// Domestic shipping
if ($this->order['ShippingCountry'] == $this->shipping_origin_country ) {
$delivery_node =& $root_node->FindChild('DeliveryConfirmationV3.0Response');
do {
$TrackingNumber = $delivery_node->FindChildValue('DeliveryConfirmationNumber');
$PostnetBarCode = $delivery_node->FindChildValue('Postnet');
$DeliveryConfirmationLabel = base64_decode($delivery_node->FindChildValue('DeliveryConfirmationLabel'));
}
while ( $delivery_node =& $delivery_node->NextSibling());
}
else {
if (strpos($shipping_service, 'express') !== false) {
$node_title = 'ExpressMailIntlResponse';
}
elseif (strpos($shipping_service, 'priority') !== false) {
$node_title = 'PriorityMailIntlResponse';
}
else {
$node_title = 'FirstClassMailIntlResponse';
}
$delivery_node =& $root_node->FindChild($node_title);
$PostnetBarCode = $delivery_node->FindChildValue('BarcodeNumber');
$Postage = $delivery_node->FindChildValue('Postage');
$DeliveryConfirmationLabel = base64_decode($delivery_node->FindChildValue('LabelImage'));
}
if ( $TrackingNumber != '' ) {
$label_file = USPS_LABEL_FOLDER.$TrackingNumber.".pdf";
}
elseif ( $PostnetBarCode != '' ) {
$label_file = USPS_LABEL_FOLDER.$PostnetBarCode.".pdf";
}
if ( $label_file != '' ) {
$file_helper = $this->Application->recallObject('FileHelper');
/* @var $file_helper FileHelper */
$file_helper->CheckFolder(USPS_LABEL_FOLDER);
if (!$handle = fopen($label_file, 'a')) echo "Cannot open file ($label_file)";
if ( @fwrite($handle, $DeliveryConfirmationLabel) === FALSE) echo "Cannot write to file ($label_file)";
}
return array('TrackingNumber' => $TrackingNumber, 'PostnetBarCode' => $PostnetBarCode, 'Postage' => $Postage);
}
function GetUSPSCountry($country, $default = 'US')
{
$cs_helper = $this->Application->recallObject('CountryStatesHelper');
/* @var $cs_helper kCountryStatesHelper */
$country = $cs_helper->getCountryIso($country);
return $country == '' ? $default : $country;
}
function GetShippingQuotes($params = null)
{
$weights = kUtil::Kg2Pounds($params['packages']['0']['weight']);
$weight = '';
$weight = $weights[0];
if ( $weights[1] != '' ) {
$weight.='.'.$weights[1];
}
$country = $this->GetUSPSCountry($params['dest_country']);
$this->order = Array();
$this->order['ShippingWeight'] = $weight;
$this->order['ShippingNumBoxes'] = 1;
$this->order['ShippingZip'] = $params['dest_postal'];
$this->order['SubTotal'] = $params['amount'];
$this->order['ShippingCountry'] = $country;
$shipping_types = Array();
$rates = $this->GetQuote();
if ( !isset($rates['error']) ) {
$this->Application->RemoveVar('sqe_error');
$i = 1;
foreach ($rates as $k => $rate ) {
$shipping_types['USPS_'.$i] = Array(
'ShippingId' => 'USPS_'.$i,
'TotalCost' => $rate['Rate'],
'ShippingName' => $rate['Title'],
'Type' => '1',
'CODFlat' => '0',
'CODPercent' => '0',
'PortalGroups' => ',15,',
'InsuranceFee' => '',
'COD' => '0',
'SelectedOnly' => '0',
'Code' => $rate['Title']
);
$i++;
}
}
else {
// for Front-End (shipping screen) and Admin (Shipping Tab on editing order)
$this->Application->StoreVar('sqe_error', $rates['error']);
}
$this->Application->StoreVar('current_usps_shipping_types', serialize($shipping_types));
return $shipping_types;
}
function TrackOrder($TrackingNumber='')
{
if ( $TrackingNumber != '' ) {
// http://testing.shippingapis.com/ShippingAPITest.dll?API=TrackV2&XML=<TrackFieldRequest USERID="402INTEC7634"><TrackID ID="EJ958083578US"></TrackID></TrackFieldRequest>
$request = '<TrackRequest USERID="'.$this->usps_userid.'"><TrackID ID="'.$TrackingNumber.'"></TrackID></TrackRequest>';
$api_query = 'TrackV2';
$request = 'API='.$api_query.'&XML='.urlencode($request);
$body = $this->PostQuery($request);
// check for errors
if (strpos($body, '<Error>') !== false) {
$errors = Array ();
preg_match_all('/<Number>(.*?)<\/Number>/s', $body, $error_numbers);
preg_match_all('/<Description>(.*?)<\/Description>/s', $body, $error_descriptions);
foreach ($error_numbers[1] as $index => $error_number) {
$errors[$index] = $error_descriptions[1][$index];
if ($this->Application->isDebugMode()) {
$errors[$index] .= ' (' . $error_number . ')';
}
}
$errors = array_unique($errors); // we may have same errors on many packages, so don't show duplicates
return Array('error' => implode('<br/>', $errors));
}
$xml_helper = $this->Application->recallObject('kXMLHelper');
$root_node =& $xml_helper->Parse($body);
/* @var $root_node kXMLNode */
// Tracking Shipping
$delivery_node =& $root_node->FindChild('TrackInfo');
$TrackSummary = $delivery_node->FindChildValue('TrackSummary');
// echo ' TrackSummary ('.$TrackingNumber.') = '.$TrackSummary.'<br>';
return strpos($TrackSummary, 'delivered') !== false ? 1 : 0;
}
else
return false;
}
function ProcessTrackOrders()
{
$sql = sprintf('SELECT `OrderId`, `ShippingTracking` FROM %s WHERE `Status` > 3 AND `Delivered` = 0', TABLE_PREFIX.'Orders');
$orders = $this->Application->Conn->Query($sql);
foreach ( $orders as $k => $order ) {
// try to track order
if ( $order['ShippingTracking'] != '' && $this->TrackOrder($order['ShippingTracking']) ) {
$update_order = sprintf("UPDATE %s SET `Delivered` = 1 WHERE %s = %s",
TABLE_PREFIX.'Orders',
$this->Application->getUnitOption('ord', 'IDField'),
$order[$this->Application->getUnitOption('ord', 'IDField')]
);
$this->Application->Conn->Query($update_order);
}
}
}
function PostQuery($request, $secure=0)
{
switch (MODULE_SHIPPING_USPS_SERVER) {
case 'production':
$usps_server = $secure > 0 ? 'https://secure.shippingapis.com' : 'http://production.shippingapis.com' ;
$api_dll = 'ShippingAPI.dll';
break;
case 'test':
$usps_server = $secure > 0 ? 'https://secure.shippingapis.com' : 'http://testing.shippingapis.com';
$api_dll = 'ShippingAPITest.dll';
break;
}
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $usps_server.'/'.$api_dll.'?'.$request);
curl_setopt($curl, CURLOPT_HEADER, 0);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$body = curl_exec($curl);
curl_close($curl);
if ($this->logFilePath) {
$fp = fopen($this->logFilePath, 'a');
if ($fp) {
$request_url = sprintf("Date %s : IP %s\n\nPost\n\n%s\n\nReplay\n\n%s\n\n",
adodb_date('m/d/Y H:i:s'),
- $_SERVER['REMOTE_ADDR'],
+ $this->Application->getClientIp(),
$usps_server . '/' . $api_dll . '?' . urldecode($request),
$body
);
fwrite($fp, $request_url);
fclose($fp);
}
}
return $body;
}
/**
* Returns available shipping types
*
* @return Array
* @todo Get possible shipping types based on MODULE_SHIPPING_USPS_TYPES and MODULE_SHIPPING_USPS_TYPES_INTL consntants
*/
function GetAvailableTypes()
{
return Array (
Array (
'_ClassName' => get_class($this),
'_Id' => 'USPS',
'_Name' => 'USPS (Default)'
)
);
}
function LoadParams()
{
$sql = 'SELECT Properties FROM '.$this->Application->getUnitOption('sqe', 'TableName').'
WHERE ClassName="USPS"';
$params = $this->Conn->GetOne($sql);
return unserialize($params);
}
function _prepare_xml_param($value) {
return strip_tags($value);
}
/**
* Returns virtual field names, that will be saved as properties
*
* @return Array
*/
function GetEngineFields()
{
return Array ('AccountLogin');
}
/**
* Creates new USPS order
*
* @param OrdersItem $object
* @param bool $dry_run
* @return Array
*/
function MakeOrder(&$object, $dry_run = false)
{
$ShippingInfo = unserialize($object->GetDBField('ShippingInfo'));
$ShippingCode = $USPSMethod = '';
$ShippingCountry = $this->GetUSPSCountry($object->GetDBField('ShippingCountry'));
$UserName = explode(" ", $object->GetDBField('ShippingTo'));
$item_table = TABLE_PREFIX.'OrderItems';
if ($this->Application->isAdminUser) {
// this strange contraption actually uses temp table from object (when in temp mode)
$order_table = $object->TableName;
$item_table = str_replace('Orders', 'OrderItems', $order_table);
}
$sOrder = Array (
'FirstName' => $UserName[0],
'LastName' => $UserName[1],
'ShippingCompany' => $object->GetDBField('ShippingCompany'),
'ShippingAddress1' => $object->GetDBField('ShippingAddress1'),
'ShippingAddress2' => $object->GetDBField('ShippingAddress2'),
'ShippingCity' => $object->GetDBField('ShippingCity'),
'ShippingZip' => $object->GetDBField('ShippingZip'),
'ShippingCountry' => $ShippingCountry,
'ShippingPhone' => $this->PhoneClean($object->GetDBField('ShippingPhone')),
'ShippingFax' => $this->PhoneClean($object->GetDBField('ShippingFax')),
'ShippingNumBoxes' => '1',
);
$sql = 'SELECT SUM(`Quantity` * `Weight`)
FROM ' . $item_table . '
WHERE ' . $object->IDField . ' = ' . $object->GetID();
$weight = $this->Application->Conn->GetOne($sql);
$f_weight = kUtil::Kg2Pounds($weight);
$sOrder['ShippingWeight'] = $f_weight[0].'.'.$f_weight[1];
foreach ($ShippingInfo as $k => $ShippingRow) {
$ShippingCode = $ShippingRow['Code'];
}
if ( $object->GetDBField('ShippingCountry') == 'USA' ) {
$sOrder['ShippingState'] = $object->GetDBField('ShippingState');
$USPSMethod = $ShippingCode;
unset($sOrder['ShippingZip']);
$sOrder['ShippingZip5'] = substr(trim($object->GetDBField('ShippingZip')), 0, 5);
$sOrder['ShippingZip4'] = '';
$sOrder['SubTotal'] = $object->GetDBField('SubTotal');
}
else {
$USPSMethod = array_search($ShippingCode, $this->intl_types);
$sOrder['ShippingProvince'] = '';
if ( $ShippingCountry == 'CA' ) {
$sOrder['ShippingProvince'] = $object->GetField('ShippingState');
}
// add items
$sql = 'SELECT `Quantity`, `Weight`, `Price`
FROM ' . $item_table . '
WHERE ' . $object->IDField . ' = ' . $object->GetID();
$order_items = $this->Application->Conn->Query($sql);
$i = 1;
$Items = Array();
foreach ($order_items as $k => $order_item) {
$p_weight = Array();
$p_weight = kUtil::Kg2Pounds($order_item['Weight']);
$Items[$i] = Array('Qty' => $order_item['Quantity'], 'Price' => $order_item['Price'], 'NetPounds' => $p_weight[0], 'NetOunces' => $p_weight[1]);
$i++;
}
$sOrder['Items'] = $Items;
$sOrder['InvoiceNumber'] = $object->GetDBField('OrderNumber');
}
$sOrder['ShippingService'] = $USPSMethod;
// make USPS order
$this->order = $sOrder;
$usps_data = $this->PostOrder();
// if errors
if ( array_key_exists('error_number', $usps_data) ) {
return $usps_data;
}
if ( array_key_exists('Postage', $usps_data) ) {
$ShippingPrice = '';
$ShippingPrice = $usps_data['Postage'];
}
$ShippingTracking = '';
if ( isset($usps_data['TrackingNumber']) && $usps_data['TrackingNumber'] != '' ) {
$ShippingTracking = $usps_data['TrackingNumber'];
}
if ( isset($usps_data['PostnetBarCode']) && $usps_data['PostnetBarCode'] != '' && $ShippingTracking == '' ) {
$ShippingTracking = $usps_data['PostnetBarCode'];
}
if ($dry_run == false) {
$object->SetDBField('ShippingTracking', $ShippingTracking);
$object->Update();
}
else {
$full_path = USPS_LABEL_FOLDER . $ShippingTracking . ".pdf";
if (file_exists($full_path)) {
unlink($full_path);
}
}
return $usps_data;
}
}
\ 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 15567)
+++ branches/5.2.x/units/orders/orders_event_handler.php (revision 15568)
@@ -1,3985 +1,3985 @@
<?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(kEvent $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));
/* @var $order_dummy OrdersItem */
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 = $order_dummy->getStatusField();
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 standard permission mapping
*
* @return void
* @access protected
* @see kEventHandler::$permMapping
*/
protected 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),
'OnUpdateAjax' => 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);
}
/**
* Define alternative event processing method names
*
* @return void
* @see kEventHandler::$eventMethods
* @access protected
*/
protected 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 OrdersItem */
$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));
/* @var $address AddressesItem */
$addr_list = $this->Application->recallObject('addr', 'addr_List', Array('per_page'=>-1, 'skip_counting'=>true) );
/* @var $addr_list AddressesList */
$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');
}
}
/**
* Updates shopping cart with logged-in user details
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnUserLogin($event)
{
if ( ($event->MasterEvent->status != kEvent::erSUCCESS) || kUtil::constOn('IS_INSTALL') ) {
// login failed OR login during installation
return;
}
$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');
}
/**
* Puts ID of just logged-in user into current order
*
* @param int $order_id
* @param kEvent $event
* @return void
*/
function updateUserID($order_id, $event)
{
$user = $this->Application->recallObject('u.current');
/* @var $user UsersItem */
$affiliate_id = $this->isAffiliate( $user->GetID() );
$fields_hash = Array (
'PortalUserId' => $user->GetID(),
'BillingEmail' => $user->GetDBField('Email'),
);
if ( $affiliate_id ) {
$fields_hash['AffiliateId'] = $affiliate_id;
}
$id_field = $this->Application->getUnitOption($event->Prefix, 'IDField');
$table_name = $this->Application->getUnitOption($event->Prefix, 'TableName');
$this->Conn->doUpdate($fields_hash, $table_name, $id_field . ' = ' . $order_id);
$object = $event->getObject();
/* @var $object kDBItem */
// set user id to object, since it will be used during order update from OnRecalculateItems event
$object->SetDBField('PortalUserId', $user->GetID());
}
function isAffiliate($user_id)
{
$affiliate_user = $this->Application->recallObject('affil.-item', null, Array('skip_autoload' => true) );
/* @var $affiliate_user kDBItem */
$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());
}
/**
* Returns parameters, used to send order-related e-mails
*
* @param OrdersItem $order
* @return array
*/
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 InventoryAction
$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();
/* @var $order OrdersItem */
$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) );
/* @var $order_items kDBList */
$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);
}
$this->Application->EmailEventUser('ORDER.SUBMIT', $order->GetDBField('PortalUserId'), $this->OrderEmailParams($order));
$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');
$this->Application->Session->SetCookie('shop_cart_cookie', '', strtotime('-1 month'));
}
/**
* Set billing address same as shipping
*
* @param kEvent $event
*/
function setBillingAddress($event)
{
$object = $event->getObject();
/* @var $object OrdersItem */
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)
{
$order_helper = $this->Application->recallObject('OrderHelper');
/* @var $order_helper OrderHelper */
$template = $this->Application->GetVar('continue_shopping_template');
$event->redirect = $order_helper->getContinueShoppingTemplate($template);
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnCheckout($event)
{
$this->OnUpdateCart($event);
if ( !$event->getEventParam('RecalculateChangedCart') ) {
$object = $event->getObject();
/* @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);
}
}
}
/**
* Restores order from cookie
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnRestoreOrder(kEvent $event)
{
if ( $this->Application->isAdmin || $this->Application->RecallVar('ord_id') ) {
// admin OR there is an active order -> don't restore from cookie
return;
}
$shop_cart_cookie = $this->Application->GetVarDirect('shop_cart_cookie', 'Cookie');
if ( !$shop_cart_cookie ) {
return;
}
$user_id = $this->Application->RecallVar('user_id');
$sql = 'SELECT OrderId
FROM ' . TABLE_PREFIX . 'Orders
WHERE (OrderId = ' . (int)$shop_cart_cookie . ') AND (Status = ' . ORDER_STATUS_INCOMPLETE . ') AND (PortalUserId = ' . $user_id . ')';
$order_id = $this->Conn->GetOne($sql);
if ( $order_id ) {
$this->Application->StoreVar('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();
/* @var $object kDBItem */
$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');
}
/**
* Removes reoccurring mark from the order
*
* @param kEvent $event
* @return void
*/
protected function OnCancelRecurring($event)
{
$order = $event->getObject();
/* @var $order OrdersItem */
$order->SetDBField('IsRecurringBilling', 0);
$order->Update();
if ( $this->Application->GetVar('cancelrecurring_ok_template') ) {
$event->redirect = $this->Application->GetVar('cancelrecurring_ok_template');
}
}
/**
* Occurs after updating item
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemUpdate(kEvent $event)
{
parent::OnAfterItemUpdate($event);
$object = $event->getObject();
/* @var $object OrdersItem */
$cvv2 = $object->GetDBField('PaymentCVV2');
if ( $cvv2 !== false ) {
$this->Application->StoreVar('CVV2Code', $cvv2);
}
}
/**
* Updates kDBItem
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnUpdate(kEvent $event)
{
$this->setBillingAddress($event);
parent::OnUpdate($event);
if ($this->Application->isAdminUser) {
return ;
}
else {
$event->SetRedirectParam('opener', 's');
}
if ($event->status == kEvent::erSUCCESS) {
$this->createMissingAddresses($event);
}
else {
// strange: recalculate total amount on error
$object = $event->getObject();
/* @var $object OrdersItem */
$object->SetDBField('TotalAmount', $object->getTotalAmount());
}
}
/**
* Creates new address
*
* @param kEvent $event
*/
function createMissingAddresses($event)
{
if ( !$this->Application->LoggedIn() ) {
return ;
}
$object = $event->getObject();
/* @var $object kDBItem */
$addr_list = $this->Application->recallObject('addr', 'addr_List', Array ('per_page' => -1, 'skip_counting' => true));
/* @var $addr_list kDBList */
$addr_list->Query();
$address_dummy = $this->Application->recallObject('addr.-item', null, Array ('skip_autoload' => true));
/* @var $address_dummy AddressesItem */
$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();
}
}
/**
* Updates shopping cart content
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnUpdateCart($event)
{
$this->Application->HandleEvent(new kEvent('orditems:OnUpdate'));
$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(new kEvent('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));
/* @var $product ProductsItem */
$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');
/* @var $product_package_item ProductsItem */
$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');
}
}
}
/**
* Returns table prefix from event (temp or live)
*
* @param kEvent $event
* @return string
* @todo Needed? Should be refactored (by Alex)
*/
function TablePrefix(kEvent $event)
{
return $this->UseTempTables($event) ? $this->Application->GetTempTablePrefix('prefix:' . $event->Prefix) . TABLE_PREFIX : TABLE_PREFIX;
}
/**
* 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;
$shop_cart_template = $this->Application->GetVar('shop_cart_template');
$event->redirect = $this->Application->isAdminUser || !$shop_cart_template ? true : $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');
}
/**
* Removes coupon from a given order
*
* @param OrdersItem $object
*/
function RemoveCoupon(&$object)
{
$coupon = $this->Application->recallObject('coup', null, Array('skip_autoload' => true));
/* @var $coupon kDBItem */
$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);
$shop_cart_template = $this->Application->GetVar('shop_cart_template');
if ( $shop_cart_template ) {
$event->redirect = $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
* @access public
*/
public function getPassedID(kEvent $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;
}
}
// 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"
}
/**
* Load item if id is available
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function LoadItem(kEvent $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
if ( $this->Application->LoggedIn() ) {
$user = $this->Application->recallObject('u.current');
/* @var $user UsersItem */
$user_id = $user->GetID();
$object->SetDBField('BillingEmail', $user->GetDBField('Email'));
}
else {
$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');
if ( $affiliate_storage_method == 1 ) {
$object->SetDBField('AffiliateId', (int)$this->Application->RecallVar('affiliate_id'));
}
else {
$object->SetDBField('AffiliateId', (int)$this->Application->GetVar('affiliate_id'));
}
}
// get payment type
$default_type = $this->_getDefaultPaymentType();
if ( $default_type ) {
$object->SetDBField('PaymentType', $default_type);
}
// vat setting
$object->SetDBField('VATIncluded', $this->Application->ConfigValue('OrderVATIncluded'));
$created = $object->Create();
if ( $created ) {
$id = $object->GetID();
$this->Application->SetVar($event->getPrefixSpecial(true) . '_id', $id);
$this->Application->StoreVar($event->getPrefixSpecial(true) . '_id', $id);
$this->Application->Session->SetCookie('shop_cart_cookie', $id, strtotime('+1 month'));
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 simultaneously
*
* @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',*/ 'BillingEmail');
$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', 'BillingEmail');
$order->setRequired($req_fields);
$order->setRequired('BillingState', $cs_helper->CountryHasStates( $field_values['BillingCountry'] ));
}
$check_cc = $this->Application->GetVar('check_credit_card');
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();
/* @var $order OrdersItem */
$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(kEvent $event)
{
parent::OnPreCreate($event);
$object = $event->getObject();
/* @var $object kDBItem */
$this->setNextOrderNumber($event);
- $object->SetDBField('OrderIP', $_SERVER['REMOTE_ADDR']);
+ $object->SetDBField('OrderIP', $this->Application->getClientIp());
$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(kEvent $event)
{
parent::OnBeforeClone($event);
$object = $event->getObject();
/* @var $object OrdersItem */
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) );
/* @var $order_items kDBList */
$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));
/* @var $product_object kCatDBItem */
$product_object->SwitchToLive();
$order_item = $this->Application->recallObject('orditems.-item', null, Array('skip_autoload' => true));
/* @var $order_item kDBItem */
$combination_item = $this->Application->recallObject('poc.-item', null, Array('skip_autoload' => true));
/* @var $combination_item kDBItem */
$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->SetRedirectParam('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();
/* @var $object kDBItem */
// update values from submit
$field_values = $this->getSubmittedFields($event);
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($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);
}
}
$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;
}
/**
* Restores reserved items in the order
*
* @param kDBList $order_items
* @return bool
*/
function restoreOrder(&$order_items)
{
$product_object = $this->Application->recallObject('p', null, Array('skip_autoload' => true));
/* @var $product_object kCatDBItem */
$product_object->SwitchToLive();
$order_item = $this->Application->recallObject('orditems.-item', null, Array('skip_autoload' => true));
/* @var $order_item kDBItem */
$combination_item = $this->Application->recallObject('poc.-item', null, Array('skip_autoload' => true));
/* @var $combination_item kDBItem */
$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');
/* @var $product_h ProductsEventHandler */
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 fulfill possible backorders
$product_h->FullfillBackOrders($product_object, $inv_object->GetID());
}
else {
// using freed qty to fulfill 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));
/* @var $object kDBItem */
$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) );
/* @var $order_items kDBList */
$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 ;
}
// save original order status
$original_order_status = $object->GetDBField('Status');
// 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 successful 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'] );
/* @var $gateway_object kGWBase */
$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));
/* @var $product_object ProductsItem */
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) {
$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);
}
if ( ($original_order_status != ORDER_STATUS_INCOMPLETE ) && ($event->Name == 'OnMassOrderDeny' || $event->Name == 'OnOrderDeny') ) {
$this->Application->EmailEventUser('ORDER.DENY', $object->GetDBField('PortalUserId'), $email_params);
// inform payment gateway that order was declined
$gw_data = $object->getGatewayData();
if ( $gw_data ) {
$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'];
$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;
$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 these 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 status incomplete to all cloned orders
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterClone(kEvent $event)
{
parent::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(kEvent $event)
{
parent::OnAfterItemLoad($event);
$object = $event->getObject();
/* @var $object kDBItem */
// get user fields
$user_id = $object->GetDBField('PortalUserId');
if ( $user_id ) {
$sql = 'SELECT *, CONCAT(FirstName,\' \',LastName) AS UserTo
FROM ' . TABLE_PREFIX . 'Users
WHERE PortalUserId = ' . $user_id;
$user_info = $this->Conn->GetRow($sql);
$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(kEvent $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(kEvent $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']);
+ $object->SetDBField('OrderIP', $this->Application->getClientIp());
}
$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);
// guess fields from "One Step Checkout" form
if ( $object->GetDBField('PaymentAccount') ) {
$order_helper = $this->Application->recallObject('OrderHelper');
/* @var $order_helper OrderHelper */
$object->SetDBField('PaymentCardType', $order_helper->getCreditCartType($object->GetDBField('PaymentAccount')));
}
else {
$object->SetDBField('PaymentCardType', '');
}
if ( !$object->GetDBField('PaymentNameOnCard') ) {
$object->SetDBField('PaymentNameOnCard', $object->GetDBField('BillingTo'));
}
if ( is_object($event->MasterEvent) && $event->MasterEvent->Name == 'OnUpdateAjax' && $this->Application->GetVar('create_account') && $object->Validate() ) {
$this->createAccountFromOrder($event);
}
}
/**
* Creates user account
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function createAccountFromOrder($event)
{
$order = $event->getObject();
/* @var $order OrdersItem */
$order_helper = $this->Application->recallObject('OrderHelper');
/* @var $order_helper OrderHelper */
$user_fields = $order_helper->getUserFields($order);
$user_fields['Password'] = $order->GetDBField('UserPassword_plain');
$user_fields['VerifyPassword'] = $order->GetDBField('VerifyUserPassword_plain');
if ( $order->GetDBField('PortalUserId') == USER_GUEST ) {
// will also auto-login user when created
$this->Application->SetVar('u_register', Array (USER_GUEST => $user_fields));
$this->Application->HandleEvent(new kEvent('u.register:OnCreate'));
}
else {
$user = $this->Application->recallObject('u.current');
/* @var $user UsersItem */
$user->SetFieldsFromHash($user_fields);
if ( !$user->Update() ) {
$order->SetError('BillingEmail', $user->GetErrorPseudo('Email'));
}
}
}
/**
* Apply any custom changes to list's sql query
*
* @param kEvent $event
* @return void
* @access protected
* @see kDBEventHandler::OnListBuild()
*/
protected function SetCustomQuery(kEvent $event)
{
parent::SetCustomQuery($event);
$object = $event->getObject();
/* @var $object kDBList */
$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) ) {
$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' : 'oi.BackOrderFlag';
// setting PackageNum to 0 for Non-tangible items, for tangibles first package num is always 1
$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) . ')';
$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.' 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 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['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
$this->Application->EmailEventUser('BACKORDER.ADD', $sub_order->GetDBField('PortalUserId'), $this->OrderEmailParams($sub_order));
$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.' AS BackOrderFlagCalc, oi.*
FROM '.TABLE_PREFIX.'OrderItems oi
LEFT JOIN '.TABLE_PREFIX.'Products p ON p.ProductId = oi.ProductId
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;
}
$item_data = $event->getEventParam('ItemData');
$item_data = $item_data ? unserialize($item_data) : Array ();
$options = getArrayValue($item_data, 'Options');
if ( !$this->CheckOptions($event, $options, $item_id, $qty, $product->GetDBField('OptionsSelectionMode')) ) {
return;
}
$manager = $this->Application->recallObject('OrderManager');
/* @var $manager OrderManager */
$manager->setOrder($order);
$manager->addProduct($product, $event->getEventParam('ItemData'), $qty, $package_num);
$this->Application->HandleEvent(new kEvent('ord:OnRecalculateItems'));
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function UpdateShippingTotal($event)
{
if ( $this->Application->GetVar('ebay_notification') == 1 ) {
// TODO: get rid of this "if"
return;
}
$object = $event->getObject();
/* @var $object OrdersItem */
$shipping_total = $insurance_fee = 0;
$shipping_info = $object->GetDBField('ShippingInfo') ? unserialize($object->GetDBField('ShippingInfo')) : false;
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 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 ;
}
$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 ( $order->GetDBField('CouponId') && $order->GetDBField('CouponDiscount') == 0 ) {
$this->RemoveCoupon($order);
$order->setCheckoutError(OrderCheckoutErrorType::COUPON, OrderCheckoutError::COUPON_REMOVED_AUTOMATICALLY);
}
if ( $result ) {
$this->UpdateShippingOption($event);
}
$this->UpdateShippingTotal($event);
$this->RecalculateProcessingFee($event);
$this->RecalculateTax($event);
$this->RecalculateGift($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', OrderCheckoutError::STATE_CHANGED);
}*/
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 = current($this->StoreSelectedIDs($event));
$id_field = $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, $id_field, $id) );
$prefix_special = $event->Prefix.'.'.$this->getSpecialByType($order_status);
$orders_list = $this->Application->recallObject($prefix_special, $event->Prefix.'_List', Array('per_page'=>-1) );
/* @var $orders_list kDBList */
$orders_list->Query();
foreach ($orders_list->Records as $row_num => $record) {
if ( $record[$id_field] == $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 kEvent $event
*/
function OnResetToPending($event)
{
$object = $event->getObject( Array('skip_autoload' => true) );
/* @var $object kDBItem */
$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);
}
/**
* [SCHEDULED TASK] 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
$this->Application->EmailEventUser('ORDER.RECURRING.PROCESSED', $order->GetDBField('PortalUserId'), $this->OrderEmailParams($order));
$this->Application->EmailEventAdmin('ORDER.RECURRING.PROCESSED');
}
else {
//send Recurring failed event
$order->SetDBField('Status', ORDER_STATUS_DENIED);
$order->Update();
$this->Application->EmailEventUser('ORDER.RECURRING.DENIED', $order->GetDBField('PortalUserId'), $this->OrderEmailParams($order));
$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.';
}
}
/**
* Occurs, when config was parsed, allows to change config data dynamically
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterConfigRead(kEvent $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
* @return void
* @access protected
*/
protected function OnBeforeExportBegin(kEvent $event)
{
parent::OnBeforeExportBegin($event);
$options = $event->getEventParam('options');
$items_list = $this->Application->recallObject($event->Prefix . '.' . $this->Application->RecallVar('export_oroginal_special'), $event->Prefix . '_List');
/* @var $items_list kDBList */
$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
* @access protected
*/
public function getCustomExportColumns(kEvent $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(kEvent $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);
}
}
/**
* Occurs 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
* @return void
* @access protected
*/
protected function OnBeforeCopyToLive(kEvent $event)
{
parent::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
* @access protected
*/
protected function checkItemStatus(kEvent $event)
{
if ( $this->Application->isAdminUser ) {
return true;
}
$object = $event->getObject();
/* @var $object kDBItem */
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');
}
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;
}
$this->Application->setContentType(kUtil::mimeContentType($full_path), false);
header('Content-Disposition: attachment; filename="' . $file . '"');
readfile($full_path);
}
/**
* Occurs before validation attempt
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemValidate(kEvent $event)
{
parent::OnBeforeItemValidate($event);
$create_account = $this->Application->GetVar('create_account');
$object = $event->getObject();
/* @var $object kDBItem */
$required_fields = Array ('UserPassword', 'UserPassword_plain', 'VerifyUserPassword', 'VerifyUserPassword_plain');
$object->setRequired($required_fields, $create_account);
$billing_email = $object->GetDBField('BillingEmail');
if ( $create_account && $object->GetDBField('PortalUserId') == USER_GUEST && $billing_email ) {
// check that e-mail available
$sql = 'SELECT PortalUserId
FROM ' . TABLE_PREFIX . 'Users
WHERE Email = ' . $this->Conn->qstr($billing_email);
$user_id = $this->Conn->GetOne($sql);
if ( $user_id ) {
$object->SetError('BillingEmail', 'unique');
}
}
}
/**
* Performs order update and returns results in format, needed by FormManager
*
* @param kEvent $event
*/
function OnUpdateAjax($event)
{
$ajax_form_helper = $this->Application->recallObject('AjaxFormHelper');
/* @var $ajax_form_helper AjaxFormHelper */
$ajax_form_helper->transitEvent($event, 'OnUpdate');
}
}
\ No newline at end of file
Index: branches/5.2.x/units/products/products_event_handler.php
===================================================================
--- branches/5.2.x/units/products/products_event_handler.php (revision 15567)
+++ branches/5.2.x/units/products/products_event_handler.php (revision 15568)
@@ -1,1595 +1,1595 @@
<?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 ProductsEventHandler extends kCatDBEventHandler {
/**
* Allows to override standard permission mapping
*
* @return void
* @access protected
* @see kEventHandler::$permMapping
*/
protected function mapPermissions()
{
parent::mapPermissions();
$permissions = Array(
// front
'OnCancelAction' => Array('self' => true),
'OnRateProduct' => Array('self' => true),
'OnClearRecent' => Array('self' => true),
'OnRecommendProduct' => Array('self' => true),
'OnAddToCompare' => Array('self' => true),
'OnRemoveFromCompare' => Array('self' => true),
'OnCancelCompare' => Array('self' => true),
// admin
'OnQtyAdd' => Array('self' => 'add|edit'),
'OnQtyRemove' => Array('self' => 'add|edit'),
'OnQtyOrder' => Array('self' => 'add|edit'),
'OnQtyReceiveOrder' => Array('self' => 'add|edit'),
'OnQtyCancelOrder' => Array('self' => 'add|edit'),
);
$this->permMapping = array_merge($this->permMapping, $permissions);
}
/**
* Define alternative event processing method names
*
* @return void
* @see kEventHandler::$eventMethods
* @access protected
*/
protected function mapEvents()
{
parent::mapEvents(); // ensure auto-adding of approve/decine and so on events
$product_events = Array (
'OnQtyAdd'=>'InventoryAction',
'OnQtyRemove'=>'InventoryAction',
'OnQtyOrder'=>'InventoryAction',
'OnQtyReceiveOrder'=>'InventoryAction',
'OnQtyCancelOrder'=>'InventoryAction',
);
$this->eventMethods = array_merge($this->eventMethods, $product_events);
}
/**
* Sets default processing data for subscriptions
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemCreate(kEvent $event)
{
parent::OnBeforeItemCreate($event);
$object = $event->getObject();
/* @var $object kDBItem */
$product_approve_events = Array (
2 => 'p:OnSubscriptionApprove',
4 => 'p:OnDownloadableApprove',
5 => 'p:OnPackageApprove'
);
$product_type = $object->GetDBField('Type');
$type_found = in_array($product_type, array_keys($product_approve_events));
if ( $type_found && !$object->GetDBField('ProcessingData') ) {
$processing_data = Array ('ApproveEvent' => $product_approve_events[$product_type]);
$object->SetDBField('ProcessingData', serialize($processing_data));
}
}
/**
* Process product count manipulations
*
* @param kEvent $event
* @access private
*/
function InventoryAction($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$field_values = $this->getSubmittedFields($event);
$object->SetFieldsFromHash($field_values, $this->getRequestProtectedFields($field_values));
if ($object->GetDBField('InventoryStatus') == 2) {
// inventory by options (use first selected combination in grid)
$combinations = $this->Application->GetVar('poc_grid');
list ($combination_id, ) = each($combinations);
}
else {
// inventory by product
$combination_id = 0;
}
// save id of selected option combination & preselect it in grid
$this->Application->SetVar('combination_id', $combination_id);
$this->ScheduleInventoryAction($event->Name, $object->GetId(), $object->GetDBField('Qty'), $combination_id);
$object->Validate();
if ( !$object->GetErrorPseudo('Qty') ){
// only update, when no error on that field
$this->modifyInventory($event->Name, $object, $object->GetDBField('Qty'), $combination_id);
}
$object->SetDBField('Qty', null);
$event->redirect = false;
}
/**
* Perform inventory action on supplied object
*
* @param string $action event name which is actually called by user
* @param ProductsItem $product
* @param int $qty
* @param int $combination_id
*/
function modifyInventory($action, &$product, $qty, $combination_id)
{
if ($product->GetDBField('InventoryStatus') == 2) {
// save inventory changes to option combination instead of product
$object = $this->Application->recallObject('poc.-item', null, Array('skip_autoload' => true));
$object->Load($combination_id);
}
elseif ($combination_id > 0) {
// combination id present, but not inventory by combinations => skip
return false;
}
elseif ($product->GetDBField('InventoryStatus') == 1) {
// save inventory changes to product
$object =& $product;
}
else {
// product has inventory actions, but don't use inventory => skip
return false;
}
if (!$object->isLoaded()) {
// product/combination in action doesn't exist in database by now
return false;
}
switch ($action) {
case 'OnQtyAdd':
$object->SetDBField('QtyInStock', $object->GetDBField('QtyInStock') + $qty);
break;
case 'OnQtyRemove':
if ($object->GetDBField('QtyInStock') < $qty) {
$qty = $object->GetDBField('QtyInStock');
}
$object->SetDBField('QtyInStock', $object->GetDBField('QtyInStock') - $qty);
break;
case 'OnQtyOrder':
$object->SetDBField('QtyOnOrder', $object->GetDBField('QtyOnOrder') + $qty);
break;
case 'OnQtyReceiveOrder':
$object->SetDBField('QtyOnOrder', $object->GetDBField('QtyOnOrder') - $qty);
$object->SetDBField('QtyInStock', $object->GetDBField('QtyInStock') + $qty);
break;
case 'OnQtyCancelOrder':
$object->SetDBField('QtyOnOrder', $object->GetDBField('QtyOnOrder') - $qty);
break;
}
return $object->Update();
}
function ScheduleInventoryAction($action, $prod_id, $qty, $combination_id = 0)
{
$inv_actions = $this->Application->RecallVar('inventory_actions');
if (!$inv_actions) {
$inv_actions = Array();
}
else {
$inv_actions = unserialize($inv_actions);
}
array_push($inv_actions, Array('action' => $action, 'product_id' => $prod_id, 'combination_id' => $combination_id, 'qty' => $qty));
$this->Application->StoreVar('inventory_actions', serialize($inv_actions));
}
function RealInventoryAction($action, $prod_id, $qty, $combination_id)
{
$product = $this->Application->recallObject('p.liveitem', null, Array('skip_autoload' => true));
$product->SwitchToLive();
$product->Load($prod_id);
$this->modifyInventory($action, $product, $qty, $combination_id);
}
function RunScheduledInventoryActions($event)
{
$inv_actions = $this->Application->GetVar('inventory_actions');
if (!$inv_actions) {
return;
}
$inv_actions = unserialize($inv_actions);
$products = array();
foreach($inv_actions as $an_action) {
$this->RealInventoryAction($an_action['action'], $an_action['product_id'], $an_action['qty'], $an_action['combination_id']);
array_push($products, $an_action['product_id'].'_'.$an_action['combination_id']);
}
$products = array_unique($products);
if ($products) {
$product_obj = $this->Application->recallObject('p.liveitem', null, Array('skip_autoload' => true));
$product_obj->SwitchToLive();
foreach ($products as $product_key) {
list($prod_id, $combination_id) = explode('_', $product_key);
$product_obj->Load($prod_id);
$this->FullfillBackOrders($product_obj, $combination_id);
}
}
}
/**
* In case if products arrived into inventory and they are required by old (non processed) orders, then use them (products) in that orders
*
* @param ProductsItem $product
* @param int $combination_id
*/
function FullfillBackOrders(&$product, $combination_id)
{
if ( !$this->Application->ConfigValue('Comm_Process_Backorders_Auto') ) return;
if ($combination_id && ($product->GetDBField('InventoryStatus') == 2)) {
// if combination id present and inventory by combinations
$poc_idfield = $this->Application->getUnitOption('poc', 'IDField');
$poc_tablename = $this->Application->getUnitOption('poc', 'TableName');
$sql = 'SELECT QtyInStock
FROM '.$poc_tablename.'
WHERE '.$poc_idfield.' = '.$combination_id;
$stock_qty = $this->Conn->GetOne($sql);
}
else {
// inventory by product
$stock_qty = $product->GetDBField('QtyInStock');
}
$qty = (int) $stock_qty - $product->GetDBField('QtyInStockMin');
$prod_id = $product->GetID();
if ($prod_id <= 0 || !$prod_id || $qty <= 0) return;
//selecting up to $qty backorders with $prod_id where full qty is not reserved
$query = 'SELECT '.TABLE_PREFIX.'Orders.OrderId
FROM '.TABLE_PREFIX.'OrderItems
LEFT JOIN '.TABLE_PREFIX.'Orders ON '.TABLE_PREFIX.'Orders.OrderId = '.TABLE_PREFIX.'OrderItems.OrderId
WHERE (ProductId = '.$prod_id.') AND (Quantity > QuantityReserved) AND (Status = '.ORDER_STATUS_BACKORDERS.')
GROUP BY '.TABLE_PREFIX.'Orders.OrderId
ORDER BY OrderDate ASC
LIMIT 0,'.$qty; //assuming 1 item per order - minimum possible
$orders = $this->Conn->GetCol($query);
if (!$orders) return;
$order = $this->Application->recallObject('ord.-inv', null, Array('skip_autoload' => true));
foreach ($orders as $ord_id) {
$order->Load($ord_id);
$this->Application->EmailEventAdmin('BACKORDER.FULLFILL');
//reserve what's possible in any case
$event = new kEvent('ord:OnReserveItems');
$this->Application->HandleEvent($event);
if ( $event->status == kEvent::erSUCCESS ) { //
//in case the order is ready to process - process it
$this->Application->HandleEvent($event, 'ord:OnOrderProcess');
}
}
}
/**
* Occurs before an item is deleted from live table when copying from temp
* (temp handler deleted all items from live and then copy over all items from temp)
* Id of item being deleted is passed as event' 'id' param
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeDeleteFromLive(kEvent $event)
{
parent::OnBeforeDeleteFromLive($event);
$product = $this->Application->recallObject($event->Prefix . '.itemlive', null, Array ('skip_autoload' => true));
/* @var $product kCatDBItem */
$product->SwitchToLive();
$id = $event->getEventParam('id');
if ( !$product->Load($id) ) {
// this will make sure New product will not be overwritten with empty data
return ;
}
$temp = $this->Application->recallObject($event->Prefix . '.itemtemp', null, Array ('skip_autoload' => true));
/* @var $temp kCatDBItem */
$temp->SwitchToTemp();
$temp->Load($id);
$temp->SetDBFieldsFromHash($product->GetFieldValues(), null, Array ('QtyInStock', 'QtyReserved', 'QtyBackOrdered', 'QtyOnOrder'));
$temp->Update();
}
/**
* Removes any information about current/selected ids
* from Application variables and Session
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function clearSelectedIDs(kEvent $event)
{
parent::clearSelectedIDs($event);
$this->Application->SetVar('inventory_actions', $this->Application->RecallVar('inventory_actions'));
$this->Application->RemoveVar('inventory_actions');
}
/**
* 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(kEvent $event)
{
parent::OnSave($event);
if ( $event->status == kEvent::erSUCCESS ) {
$this->RunScheduledInventoryActions($event);
}
}
/**
* 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(kEvent $event)
{
parent::onPreCreate($event);
$object = $event->getObject();
/* @var $object kDBItem */
$object->SetDBField('Type', $this->Application->GetVar($event->getPrefixSpecial(true) . '_new_type'));
}
/**
* Saves edited item in temp table and loads
* item with passed id in current template
* Used in Prev/Next buttons
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreSaveAndGo(kEvent $event)
{
$event->CallSubEvent('OnPreSave');
$this->LoadItem($event);
$object = $event->getObject();
/* @var $object kDBItem */
$from_type = $object->GetDBField('Type');
if ( $event->status == kEvent::erSUCCESS ) {
$this->Application->SetVar($event->getPrefixSpecial() . '_id', $this->Application->GetVar($event->getPrefixSpecial(true) . '_GoId'));
$this->LoadItem($event);
$to_type = $object->GetDBField('Type');
if ( $from_type != $to_type ) {
$from_tabs = $this->GetTabs($from_type);
$from_tab_i = array_search($this->Application->GetVar('t'), $from_tabs);
$to_tabs = $this->GetTabs($to_type);
$to_tab = $this->Application->GetVar('t');
$found = false;
while (!isset($to_tabs[$from_tab_i]) && $from_tab_i < count($to_tabs)) {
$from_tab_i++;
}
if ( !isset($to_tabs[$from_tab_i]) ) {
$from_tab_i = 0;
}
$to_tab = $to_tabs[$from_tab_i];
$event->redirect = $to_tab;
}
}
}
function GetTabs($type)
{
switch($type)
{
case 1:
return Array(
0 => 'in-commerce/products/products_edit',
1 => 'in-commerce/products/products_inventory',
2 => 'in-commerce/products/products_pricing',
3 => 'in-commerce/products/products_categories',
4 => 'in-commerce/products/products_images',
5 => 'in-commerce/products/products_reviews',
6 => 'in-commerce/products/products_custom',
);
case 2:
return Array(
0 => 'in-commerce/products/products_edit',
1 => 'in-commerce/products/products_access',
/*2 => 'in-commerce/products/products_access_pricing',*/
3 => 'in-commerce/products/products_categories',
4 => 'in-commerce/products/products_images',
5 => 'in-commerce/products/products_reviews',
6 => 'in-commerce/products/products_custom',
);
case 3:
return Array(
0 => 'in-commerce/products/products_edit',
2 => 'in-commerce/products/products_access_pricing',
3 => 'in-commerce/products/products_categories',
4 => 'in-commerce/products/products_images',
5 => 'in-commerce/products/products_reviews',
6 => 'in-commerce/products/products_custom',
);
case 4:
return Array(
0 => 'in-commerce/products/products_edit',
2 => 'in-commerce/products/products_files',
3 => 'in-commerce/products/products_categories',
4 => 'in-commerce/products/products_images',
5 => 'in-commerce/products/products_reviews',
6 => 'in-commerce/products/products_custom',
);
}
}
/**
* Return type clauses for list bulding on front
*
* @param kEvent $event
* @return Array
*/
function getTypeClauses($event)
{
$types = $event->getEventParam('types');
$types = $types ? explode(',', $types) : Array ();
$except_types = $event->getEventParam('except');
$except_types = $except_types ? explode(',', $except_types) : Array ();
$object = $event->getObject();
/* @var $object kDBList */
$type_clauses = parent::getTypeClauses($event);
$type_clauses['featured']['include'] = '%1$s.Featured = 1 AND ' . TABLE_PREFIX . 'CategoryItems.PrimaryCat = 1';
$type_clauses['featured']['except'] = '%1$s.Featured != 1 AND ' . TABLE_PREFIX . 'CategoryItems.PrimaryCat = 1';
$type_clauses['featured']['having_filter'] = false;
$type_clauses['onsale']['include'] = '%1$s.OnSale = 1 AND ' . TABLE_PREFIX . 'CategoryItems.PrimaryCat = 1';
$type_clauses['onsale']['except'] = '%1$s.OnSale != 1 AND ' . TABLE_PREFIX . 'CategoryItems.PrimaryCat = 1';
$type_clauses['onsale']['having_filter'] = false;
// products from selected manufacturer: begin
$manufacturer = $event->getEventParam('manufacturer');
if ( !$manufacturer ) {
$manufacturer = $this->Application->GetVar('manuf_id');
}
if ( $manufacturer ) {
$type_clauses['manufacturer']['include'] = '%1$s.ManufacturerId = ' . $manufacturer . ' AND PrimaryCat = 1';
$type_clauses['manufacturer']['except'] = '%1$s.ManufacturerId != ' . $manufacturer . ' AND PrimaryCat = 1';
$type_clauses['manufacturer']['having_filter'] = false;
}
// products from selected manufacturer: end
// recent products: begin
$recent = $this->Application->RecallVar('recent_products');
if ( $recent ) {
$recent = unserialize($recent);
$type_clauses['recent']['include'] = '%1$s.ProductId IN (' . implode(',', $recent) . ') AND PrimaryCat = 1';
$type_clauses['recent']['except'] = '%1$s.ProductId NOT IN (' . implode(',', $recent) . ') AND PrimaryCat = 1';
}
else {
$type_clauses['recent']['include'] = '0';
$type_clauses['recent']['except'] = '1';
}
$type_clauses['recent']['having_filter'] = false;
// recent products: end
// compare products: begin
if ( in_array('compare', $types) || in_array('compare', $except_types) ) {
$compare_products = $this->getCompareProducts();
if ( $compare_products ) {
$compare_products = $this->Conn->qstrArray($compare_products);
$type_clauses['compare']['include'] = '%1$s.ProductId IN (' . implode(',', $compare_products) . ') AND PrimaryCat = 1';
$type_clauses['compare']['except'] = '%1$s.ProductId NOT IN (' . implode(',', $compare_products) . ') AND PrimaryCat = 1';
}
else {
$type_clauses['compare']['include'] = '0';
$type_clauses['compare']['except'] = '1';
}
$type_clauses['compare']['having_filter'] = false;
if ( $event->getEventParam('per_page') === false ) {
$event->setEventParam('per_page', $this->Application->ConfigValue('MaxCompareProducts'));
}
}
// compare products: end
// products already in shopping cart: begin
if ( in_array('in_cart', $types) || in_array('in_cart', $except_types) ) {
$order_id = $this->Application->RecallVar('ord_id');
if ( $order_id ) {
$sql = 'SELECT ProductId
FROM ' . TABLE_PREFIX . 'OrderItems
WHERE OrderId = ' . $order_id;
$in_cart = $this->Conn->GetCol($sql);
if ( $in_cart ) {
$type_clauses['in_cart']['include'] = '%1$s.ProductId IN (' . implode(',', $in_cart) . ') AND PrimaryCat = 1';
$type_clauses['in_cart']['except'] = '%1$s.ProductId NOT IN (' . implode(',', $in_cart) . ') AND PrimaryCat = 1';
}
else {
$type_clauses['in_cart']['include'] = '0';
$type_clauses['in_cart']['except'] = '1';
}
}
else {
$type_clauses['in_cart']['include'] = '0';
$type_clauses['in_cart']['except'] = '1';
}
$type_clauses['in_cart']['having_filter'] = false;
}
// products already in shopping cart: end
// my downloadable products: begin
if ( in_array('my_downloads', $types) || in_array('my_downloads', $except_types) ) {
$user_id = $this->Application->RecallVar('user_id');
$sql = 'SELECT ProductId
FROM ' . TABLE_PREFIX . 'UserFileAccess
WHERE PortalUserId = ' . $user_id;
$my_downloads = $user_id > 0 ? $this->Conn->GetCol($sql) : false;
if ( $my_downloads ) {
$type_clauses['my_downloads']['include'] = '%1$s.ProductId IN (' . implode(',', $my_downloads) . ') AND PrimaryCat = 1';
$type_clauses['my_downloads']['except'] = '%1$s.ProductId NOT IN (' . implode(',', $my_downloads) . ') AND PrimaryCat = 1';
}
else {
$type_clauses['my_downloads']['include'] = '0';
$type_clauses['my_downloads']['except'] = '1';
}
$type_clauses['my_downloads']['having_filter'] = false;
}
// my downloadable products: end
// my favorite products: begin
if ( in_array('wish_list', $types) || in_array('wish_list', $except_types) ) {
$sql = 'SELECT ResourceId
FROM ' . $this->Application->getUnitOption('fav', 'TableName') . '
WHERE PortalUserId = ' . (int)$this->Application->RecallVar('user_id');
$wishlist_ids = $this->Conn->GetCol($sql);
if ( $wishlist_ids ) {
$type_clauses['wish_list']['include'] = '%1$s.ResourceId IN (' . implode(',', $wishlist_ids) . ') AND PrimaryCat = 1';
$type_clauses['wish_list']['except'] = '%1$s.ResourceId NOT IN (' . implode(',', $wishlist_ids) . ') AND PrimaryCat = 1';
}
else {
$type_clauses['wish_list']['include'] = '0';
$type_clauses['wish_list']['except'] = '1';
}
$type_clauses['wish_list']['having_filter'] = false;
}
// my favorite products: end
// products from package: begin
if ( in_array('content', $types) || in_array('content', $except_types) ) {
$object->removeFilter('category_filter');
$object->AddGroupByField('%1$s.ProductId');
$object_product = $this->Application->recallObject($event->Prefix);
/* @var $object_product ProductsItem */
$content_ids_array = $object_product->GetPackageContentIds();
if ( sizeof($content_ids_array) == 0 ) {
$content_ids_array = array ('-1');
}
if ( sizeof($content_ids_array) > 0 ) {
$type_clauses['content']['include'] = '%1$s.ProductId IN (' . implode(',', $content_ids_array) . ')';
}
else {
$type_clauses['content']['include'] = '0';
}
$type_clauses['related']['having_filter'] = false;
}
// products from package: end
$object->addFilter('not_virtual', '%1$s.Virtual = 0');
if ( !$this->Application->isAdminUser ) {
$object->addFilter('expire_filter', '%1$s.Expire IS NULL OR %1$s.Expire > ' . adodb_mktime());
}
return $type_clauses;
}
function OnClearRecent($event)
{
$this->Application->RemoveVar('recent_products');
}
/**
* Occurs, when user rates a product
*
* @param kEvent $event
*/
function OnRateProduct($event)
{
$event->SetRedirectParam('pass', 'all,p');
$event->redirect = $this->Application->GetVar('success_template');
$object = $event->getObject();
/* @var $object kDBItem */
$user_id = $this->Application->RecallVar('user_id');
$sql = ' SELECT * FROM ' . TABLE_PREFIX . 'SpamControl
WHERE ItemResourceId=' . $object->GetDBField('ResourceId') . '
- AND IPaddress="' . $_SERVER['REMOTE_ADDR'] . '"
+ AND IPaddress="' . $this->Application->getClientIp() . '"
AND PortalUserId=' . $user_id . '
AND DataType="Rating"';
$res = $this->Conn->GetRow($sql);
if ( $res && $res['Expire'] < adodb_mktime() ) {
$sql = ' DELETE FROM ' . TABLE_PREFIX . 'SpamControl
WHERE ItemResourceId=' . $object->GetDBField('ResourceId') . '
- AND IPaddress="' . $_SERVER['REMOTE_ADDR'] . '"
+ AND IPaddress="' . $this->Application->getClientIp() . '"
AND PortalUserId=' . $user_id . '
AND DataType="Rating"';
$this->Conn->Query($sql);
unset($res);
}
$new_rating = $this->Application->GetVar('rating');
if ( $new_rating !== false && !$res ) {
$rating = $object->GetDBField('CachedRating');
$votes = $object->GetDBField('CachedVotesQty');
$new_votes = $votes + 1;
$rating = (($rating * $votes) + $new_rating) / $new_votes;
$object->SetDBField('CachedRating', $rating);
$object->SetDBField('CachedVotesQty', $new_votes);
$object->Update();
$expire = adodb_mktime() + $this->Application->ConfigValue('product_ReviewDelay_Value') * $this->Application->ConfigValue('product_ReviewDelay_Interval');
$sql = ' INSERT INTO ' . TABLE_PREFIX . 'SpamControl
(ItemResourceId, IPaddress, PortalUserId, DataType, Expire)
VALUES (' . $object->GetDBField('ResourceId') . ',
- "' . $_SERVER['REMOTE_ADDR'] . '",
+ "' . $this->Application->getClientIp() . '",
' . $user_id . ',
"Rating",
' . $expire . ')';
$this->Conn->Query($sql);
}
else {
$event->status == kEvent::erFAIL;
$event->redirect = false;
$object->SetError('CachedRating', 'too_frequent', 'lu_ferror_rate_duplicate');
}
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnCancelAction($event)
{
$event->SetRedirectParam('pass', 'all,p');
$event->redirect = $this->Application->GetVar('cancel_template');
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnRecommendProduct($event)
{
// used for error reporting only -> rewrite code + theme (by Alex)
$object = $this->Application->recallObject('u', null, Array('skip_autoload' => true)); // TODO: change theme too
/* @var $object kDBItem */
$friend_email = $this->Application->GetVar('friend_email');
$friend_name = $this->Application->GetVar('friend_name');
$my_email = $this->Application->GetVar('your_email');
$my_name = $this->Application->GetVar('your_name');
$my_message = $this->Application->GetVar('your_message');
$send_params = array();
$send_params['to_email']=$friend_email;
$send_params['to_name']=$friend_name;
$send_params['from_email']=$my_email;
$send_params['from_name']=$my_name;
$send_params['message']=$my_message;
if ( preg_match('/' . REGEX_EMAIL_USER . '@' . REGEX_EMAIL_DOMAIN . '/', $friend_email) ) {
$user_id = $this->Application->RecallVar('user_id');
$email_sent = $this->Application->EmailEventUser('PRODUCT.SUGGEST', $user_id, $send_params);
$this->Application->EmailEventAdmin('PRODUCT.SUGGEST');
if ( $email_sent ) {
$event->setRedirectParams(Array ('opener' => 's', 'pass' => 'all'));
$event->redirect = $this->Application->GetVar('template_success');
}
else {
// $event->setRedirectParams(Array('opener' => 's', 'pass' => 'all'));
// $event->redirect = $this->Application->GetVar('template_fail');
$object->SetError('Email', 'send_error', 'lu_email_send_error');
$event->status = kEvent::erFAIL;
}
}
else {
$object->SetError('Email', 'invalid_email', 'lu_InvalidEmail');
$event->status = kEvent::erFAIL;
}
}
/**
* Creates/updates virtual product based on listing type data
*
* @param kEvent $event
*/
function OnSaveVirtualProduct($event)
{
$object = $event->getObject( Array('skip_autoload' => true) );
$listing_type = $this->Application->recallObject('lst', null, Array('skip_autoload' => true));
$listing_type->Load($event->MasterEvent->getEventParam('id'));
$product_id = $listing_type->GetDBField('VirtualProductId');
if ($product_id) {
$object->Load($product_id);
}
if (!$listing_type->GetDBField('EnableBuying')) {
if ($product_id) {
// delete virtual product here
$temp_handler = $this->Application->recallObject($event->getPrefixSpecial().'_TempHandler', 'kTempTablesHandler');
$temp_handler->DeleteItems($event->Prefix, $event->Special, Array($product_id));
$listing_type->SetDBField('VirtualProductId', 0);
$listing_type->Update();
}
return true;
}
$ml_formatter = $this->Application->recallObject('kMultiLanguage');
$object->SetDBField($ml_formatter->LangFieldName('Name'), $listing_type->GetDBField('ShopCartName') );
$object->SetDBField($ml_formatter->LangFieldName('Description'), $listing_type->GetDBField('Description'));
$object->SetDBField('SKU', 'ENHANCE_LINK_'.abs( crc32( $listing_type->GetDBField('Name') ) ) );
if ($product_id) {
$object->Update();
}
else {
$object->SetDBField('Type', 2);
$object->SetDBField('Status', 1);
$object->SetDBField('HotItem', 0);
$object->SetDBField('PopItem', 0);
$object->SetDBField('NewItem', 0);
$object->SetDBField('Virtual', 1);
// $processing_data = Array('ApproveEvent' => 'ls:EnhanceLinkAfterOrderApprove', 'ExpireEvent' => 'ls:ExpireLink');
$processing_data = Array( 'ApproveEvent' => 'ls:EnhanceLinkAfterOrderApprove',
'DenyEvent' => 'ls:EnhanceLinkAfterOrderDeny',
'CompleteOrderEvent' => 'ls:EnhancedLinkOnCompleteOrder',
'ExpireEvent' => 'ls:ExpireLink',
'HasNewProcessing' => 1);
$object->SetDBField('ProcessingData', serialize($processing_data));
$object->Create();
$listing_type->SetDBField('VirtualProductId', $object->GetID());
$listing_type->Update();
}
$additiona_fields = Array( 'AccessDuration' => $listing_type->GetDBField('Duration'),
'AccessUnit' => $listing_type->GetDBField('DurationType'),
);
$this->setPrimaryPrice($object->GetID(), (double)$listing_type->GetDBField('Price'), $additiona_fields);
}
/**
* [HOOK] Deletes virtual product when listing type is deleted
*
* @param kEvent $event
*/
function OnDeleteListingType($event)
{
$listing_type = $event->MasterEvent->getObject();
/* @var $listing_type kDBItem */
$product_id = $listing_type->GetDBField('VirtualProductId');
if ( $product_id ) {
$temp_handler = $this->Application->recallObject($event->getPrefixSpecial() . '_TempHandler', 'kTempTablesHandler');
$temp_handler->DeleteItems($event->Prefix, $event->Special, Array ($product_id));
}
}
/**
* Extends user membership in group when his order is approved
*
* @param kEvent $event
*/
function OnSubscriptionApprove($event)
{
$field_values = $event->getEventParam('field_values');
$item_data = unserialize($field_values['ItemData']);
if ( !getArrayValue($item_data,'PortalGroupId') ) {
// is subscription product, but no group defined in it's properties
trigger_error('Invalid product <b>'.$field_values['ProductName'].'</b> (id: '.$field_values['ProductId'].')', E_USER_WARNING);
return false;
}
$sql = 'SELECT PortalUserId
FROM ' . $this->Application->getUnitOption('ord', 'TableName') . '
WHERE ' . $this->Application->getUnitOption('ord', 'IDField') . ' = ' . $field_values['OrderId'];
$user_id = $this->Conn->GetOne($sql);
$group_id = $item_data['PortalGroupId'];
$duration = $item_data['Duration'];
$sql = 'SELECT *
FROM ' . TABLE_PREFIX . 'UserGroupRelations
WHERE PortalUserId = ' . $user_id;
$user_groups = $this->Conn->Query($sql, 'GroupId');
if ( !isset($user_groups[$group_id]) ) {
$expire = adodb_mktime() + $duration;
}
else {
$expire = $user_groups[$group_id]['MembershipExpires'];
$expire = $expire < adodb_mktime() ? adodb_mktime() + $duration : $expire + $duration;
}
/*// Customization healtheconomics.org
if ($item_data['DurationType'] == 2) {
$expire = $item_data['AccessExpiration'];
}
// Customization healtheconomics.org --*/
$fields_hash = Array (
'PortalUserId' => $user_id,
'GroupId' => $group_id,
'MembershipExpires' => $expire,
);
$this->Conn->doInsert($fields_hash, TABLE_PREFIX . 'UserGroupRelations', 'REPLACE');
$sub_order = $this->Application->recallObject('ord.-sub'.$event->getEventParam('next_sub_number'), 'ord');
$sub_order->SetDBField('IsRecurringBilling', getArrayValue($item_data, 'IsRecurringBilling') ? 1 : 0);
$sub_order->SetDBField('GroupId', $group_id);
$sub_order->SetDBField('NextCharge_date', $expire);
$sub_order->SetDBField('NextCharge_time', $expire);
}
function OnDownloadableApprove($event)
{
$field_values = $event->getEventParam('field_values');
$product_id = $field_values['ProductId'];
$sql = 'SELECT PortalUserId FROM '.$this->Application->getUnitOption('ord', 'TableName').'
WHERE OrderId = '.$field_values['OrderId'];
$user_id = $this->Conn->GetOne($sql);
$sql = 'INSERT INTO '.TABLE_PREFIX.'UserFileAccess VALUES("", '.$product_id.', '.$user_id.')';
$this->Conn->Query($sql);
}
protected function OnPackageApprove(kEvent $event)
{
$field_values = $event->getEventParam('field_values');
$item_data = unserialize($field_values['ItemData']);
$package_content_ids = $item_data['PackageContent'];
$object_item = $this->Application->recallObject('p.packageitem', null, array ('skip_autoload' => true));
/* @var $object_item ProductsItem */
foreach ($package_content_ids as $package_item_id) {
$object_field_values = array ();
// query processing data from product and run approve event
$sql = 'SELECT ProcessingData
FROM ' . TABLE_PREFIX . 'Products
WHERE ProductId = ' . $package_item_id;
$processing_data = $this->Conn->GetOne($sql);
if ( $processing_data ) {
$processing_data = unserialize($processing_data);
$approve_event = new kEvent($processing_data['ApproveEvent']);
//$order_item_fields = $this->Conn->GetRow('SELECT * FROM '.TABLE_PREFIX.'OrderItems WHERE OrderItemId = '.$grouping_data[1]);
$object_item->Load($package_item_id);
$object_field_values['OrderId'] = $field_values['OrderId'];
$object_field_values['ProductId'] = $package_item_id;
$object_field_values['ItemData'] = serialize($item_data['PackageItemsItemData'][$package_item_id]);
$approve_event->setEventParam('field_values', $object_field_values);
$this->Application->HandleEvent($approve_event);
}
}
}
/**
* Saves edited item into temp table
* If there is no id, new item is created in temp table
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnPreSave(kEvent $event)
{
$this->CheckRequiredOptions($event);
parent::OnPreSave($event);
}
/**
* Set new price to ProductsPricing
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemCreate(kEvent $event)
{
parent::OnAfterItemCreate($event);
$this->_updateProductPrice($event);
}
/**
* Set new price to ProductsPricing
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemUpdate(kEvent $event)
{
parent::OnAfterItemUpdate($event);
$this->_updateProductPrice($event);
}
/**
* Updates product's primary price based on Price virtual field value
*
* @param kEvent $event
*/
function _updateProductPrice($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$price = $object->GetDBField('Price');
// always create primary pricing, to show on Pricing tab (in admin) for tangible products
$force_create = ($object->GetDBField('Type') == PRODUCT_TYPE_TANGIBLE) && is_null($price);
if ($force_create || ($price != $object->GetOriginalField('Price'))) {
// new product OR price was changed in virtual field
$this->setPrimaryPrice($object->GetID(), (float)$price);
}
}
function CheckRequiredOptions($event)
{
$object = $event->getObject();
if ($object->GetDBField('ProductId') == '') return ; // if product does not have ID - it's not yet created
$opt_object = $this->Application->recallObject('po', null, Array('skip_autoload' => true) );
$has_required = $this->Conn->GetOne('SELECT COUNT(*) FROM '.$opt_object->TableName.' WHERE Required = 1 AND ProductId = '.$object->GetDBField('ProductId'));
//we need to imitate data sumbit, as parent' PreSave sets object values from $items_info
$items_info = $this->Application->GetVar( $event->getPrefixSpecial(true) );
$items_info[$object->GetDBField('ProductId')]['HasRequiredOptions'] = $has_required ? '1' : '0';
$this->Application->SetVar($event->getPrefixSpecial(true), $items_info);
$object->SetDBField('HasRequiredOptions', $has_required ? 1 : 0);
}
/**
* Sets required price in primary price backed, if it's missing, then create it
*
* @param int $product_id
* @param double $price
* @param Array $additional_fields
* @return bool
*/
function setPrimaryPrice($product_id, $price, $additional_fields = Array())
{
$pr_object = $this->Application->recallObject('pr.-item', null, Array('skip_autoload' => true) );
/* @var $pr_object kDBItem */
$pr_object->Load( Array('ProductId' => $product_id, 'IsPrimary' => 1) );
$sql = 'SELECT COUNT(*) FROM '.$pr_object->TableName.' WHERE ProductId = '.$product_id;
$has_pricings = $this->Conn->GetOne($sql);
if ($additional_fields) {
$pr_object->SetDBFieldsFromHash($additional_fields);
}
if( ($price === false) && $has_pricings ) return false;
if( $pr_object->isLoaded() )
{
$pr_object->SetField('Price', $price);
return $pr_object->Update();
}
else
{
$group_id = $this->Application->ConfigValue('User_LoggedInGroup');
$field_values = Array('ProductId' => $product_id, 'IsPrimary' => 1, 'MinQty' => 1, 'MaxQty' => -1, 'GroupId'=>$group_id);
$pr_object->SetDBFieldsFromHash($field_values);
$pr_object->SetField('Price', $price);
return $pr_object->Create();
}
}
/**
* Occurs after deleting item, id of deleted item
* is stored as 'id' param of event
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemDelete(kEvent $event)
{
parent::OnAfterItemDelete($event);
$product_id = $event->getEventParam('id');
if ( !$product_id ) {
return;
}
$sql = 'DELETE FROM ' . TABLE_PREFIX . 'UserFileAccess
WHERE ProductId = ' . $product_id;
$this->Conn->Query($sql);
}
/**
* Load price from temp table if product mode is temp table
*
* @param kEvent $event
*/
/**
* Load price from temp table if product mode is temp table
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterItemLoad(kEvent $event)
{
parent::OnAfterItemLoad($event);
$object = $event->getObject();
/* @var $object ProductsItem */
$a_pricing = $object->getPrimaryPricing();
if ( !$a_pricing ) {
// pricing doesn't exist for new products
$price = $cost = null;
}
else {
$price = (float)$a_pricing['Price'];
$cost = (float)$a_pricing['Cost'];
}
// set original fields to use them in OnAfterItemCreate/OnAfterItemUpdate later
$object->SetDBField('Price', $price);
$object->SetOriginalField('Price', $price);
$object->SetDBField('Cost', $cost);
$object->SetOriginalField('Cost', $cost);
}
/**
* Allows to add products to package besides all that parent method does
*
* @param kEvent $event
*/
function OnProcessSelected($event)
{
$dst_field = $this->Application->RecallVar('dst_field');
if ($dst_field == 'PackageContent') {
$this->OnAddToPackage($event);
}
elseif ($dst_field == 'AssignedCoupon') {
$coupon_id = $this->Application->GetVar('selected_ids');
$object = $event->getObject();
$object->SetDBField('AssignedCoupon', $coupon_id);
$this->RemoveRequiredFields($object);
$object->Update();
}
else {
parent::OnProcessSelected($event);
}
$this->finalizePopup($event);
}
/**
* Called when some products are selected in products selector for this prefix
*
* @param kEvent $event
*/
function OnAddToPackage($event)
{
$selected_ids = $this->Application->GetVar('selected_ids');
// update current package content with selected products
$object = $event->getObject();
/* @var $object ProductsItem */
$product_ids = $selected_ids['p'] ? explode(',', $selected_ids['p']) : Array();
if ($product_ids) {
$current_ids = $object->GetPackageContentIds();
$current_ids = array_unique(array_merge($current_ids, $product_ids));
// remove package product from selected list
$this_product = array_search($object->GetID(), $current_ids);
if ($this_product !== false) {
unset($current_ids[$this_product]);
}
$dst_field = $this->Application->RecallVar('dst_field');
$object->SetDBField($dst_field, '|'.implode('|', $current_ids).'|');
$object->Update();
$this->ProcessPackageItems($event);
}
$this->finalizePopup($event);
}
function ProcessPackageItems(kEvent $event)
{
//$this->Application->SetVar('p_mode', 't');
$object = $event->getObject();
/* @var $object ProductsItem */
$content_ids = $object->GetPackageContentIds();
if (sizeof($content_ids) > 0) {
$total_weight = $this->Conn->GetOne('SELECT SUM(Weight) FROM '.TABLE_PREFIX.'Products WHERE ProductId IN ('.implode(', ', $content_ids).') AND Type=1');
if (!$total_weight) $total_weight = 0;
$this->Conn->Query('UPDATE '.$object->TableName.' SET Weight='.$total_weight.' WHERE ProductId='.$object->GetID());
}
/*
$this->Application->SetVar('p_mode', false);
$list = $this->Application->recallObject('p.content', 'p_List', array('types'=>'content'));
$this->Application->SetVar('p_mode', 't');
$list->Query();
$total_weight_a = 0;
$total_weight_b = 0;
$list->GoFirst();
while (!$list->EOL())
{
if ($list->GetDBField('Type')==1){
$total_weight_a += $list->GetField('Weight_a');
$total_weight_b += $list->GetField('Weight_b');
}
$list->GoNext();
}
$object->SetField('Weight_a', $total_weight_a);
$object->SetField('Weight_b', $total_weight_b);
*/
//$object->Update();
}
/**
* Enter description here...
*
* @param kEvent $event
*/
function OnSaveItems($event)
{
//$event->CallSubEvent('OnUpdate');
$event->redirect = false;
//$event->setRedirectParams(Array ('opener' => 's', 'pass' => 'all,p'));
}
/**
* Removes product from package
*
* @param kEvent $event
*/
function OnRemovePackageItem($event) {
$this->Application->SetVar('p_mode', 't');
$object = $event->getObject();
$items_info = $this->Application->GetVar('p_content');
if($items_info)
{
$product_ids = array_keys($items_info);
$current_ids = $object->GetPackageContentIds();
$current_ids_flip = array_flip($current_ids);
foreach($product_ids as $key=>$val){
unset($current_ids_flip[$val]);
}
$current_ids = array_keys($current_ids_flip);
$current_ids_str = '|'.implode('|', array_unique($current_ids)).'|';
$object->SetDBField('PackageContent', $current_ids_str);
}
$object->Update();
$this->ProcessPackageItems($event);
}
/**
* Occurs before deleting item, id of item being
* deleted is stored as 'id' event param
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnBeforeItemDelete(kEvent $event)
{
parent::OnBeforeItemDelete($event);
$object = $event->getObject();
/* @var $object kDBItem */
$sql = 'SELECT COUNT(*)
FROM ' . TABLE_PREFIX . 'Products
WHERE PackageContent LIKE "%|' . $object->GetID() . '%"';
$product_includes_in = $this->Conn->GetOne($sql);
if ( $product_includes_in > 0 ) {
$event->status = kEvent::erFAIL;
}
}
/**
* Returns specific to each item type columns only
*
* @param kEvent $event
* @return Array
* @access protected
*/
public function getCustomExportColumns(kEvent $event)
{
$columns = parent::getCustomExportColumns($event);
$new_columns = Array (
'__VIRTUAL__Price' => 'Price',
'__VIRTUAL__Cost' => 'Cost',
);
return array_merge($columns, $new_columns);
}
/**
* Sets non standart virtual fields (e.g. to other tables)
*
* @param kEvent $event
*/
function setCustomExportColumns($event)
{
parent::setCustomExportColumns($event);
$object = $event->getObject();
/* @var $object kDBItem */
$this->setPrimaryPrice($object->GetID(), (double)$object->GetDBField('Price'), Array ('Cost' => (double)$object->GetDBField('Cost')));
}
function OnPreSaveAndOpenPopup($event)
{
$object = $event->getObject();
/* @var $object kDBItem */
$this->RemoveRequiredFields($object);
$event->CallSubEvent('OnPreSave');
$event->redirect = $this->Application->GetVar('t');
// pass ID too, in case if product is created by OnPreSave call to ensure proper editing
$event->SetRedirectParam('pass', 'all');
$event->SetRedirectParam($event->getPrefixSpecial(true) . '_id', $object->GetID());
}
/**
* Returns ID of current item to be edited
* by checking ID passed in get/post as prefix_id
* or by looking at first from selected ids, stored.
* Returned id is also stored in Session in case
* it was explicitly passed as get/post
*
* @param kEvent $event
* @return int
* @access public
*/
public function getPassedID(kEvent $event)
{
if ( $this->Application->isAdminUser ) {
$event->setEventParam('raise_warnings', 0);
}
$passed = parent::getPassedID($event);
if ( $passed ) {
return $passed;
}
if ( $this->Application->isAdminUser ) {
// we may get product id out of OrderItem, if it exists
$ord_item = $this->Application->recallObject('orditems', null, Array ('raise_warnings' => 0));
/* @var $ord_item OrdersItem */
if ( $ord_item->GetDBField('ProductId') ) {
$passed = $ord_item->GetDBField('ProductId');
}
}
return $passed;
}
/**
* Occurs, when config was parsed, allows to change config data dynamically
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAfterConfigRead(kEvent $event)
{
parent::OnAfterConfigRead($event);
if (!$this->Application->LoggedIn()) {
return ;
}
$user_id = $this->Application->RecallVar('user_id');
$sql = 'SELECT PrimaryGroupId
FROM ' . TABLE_PREFIX . 'Users
WHERE PortalUserId = ' . $user_id;
$primary_group_id = $this->Conn->GetOne($sql);
if (!$primary_group_id) {
return;
}
$sub_select = ' SELECT pp.Price
FROM ' . TABLE_PREFIX . 'ProductsPricing AS pp
WHERE pp.ProductId = %1$s.ProductId AND GroupId = ' . $primary_group_id . '
ORDER BY MinQty
LIMIT 0,1';
$calculated_fields = $this->Application->getUnitOption($event->Prefix, 'CalculatedFields');
$calculated_fields['']['Price'] = 'IFNULL((' . $sub_select . '), ' . $calculated_fields['']['Price'] . ')';
$this->Application->setUnitOption($event->Prefix, 'CalculatedFields', $calculated_fields);
}
/**
* Starts product editing, remove any pending inventory actions
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnEdit(kEvent $event)
{
$this->Application->RemoveVar('inventory_actions');
parent::OnEdit($event);
}
/**
* Adds "Shop Cart" tab on paid listing type editing tab
*
* @param kEvent $event
*/
function OnModifyPaidListingConfig($event)
{
$edit_tab_presets = $this->Application->getUnitOption($event->MasterEvent->Prefix, 'EditTabPresets');
$edit_tab_presets['Default']['shopping_cart'] = Array ('title' => 'la_tab_ShopCartEntry', 't' => 'in-commerce/paid_listings/paid_listing_type_shopcart', 'priority' => 2);
$this->Application->setUnitOption($event->MasterEvent->Prefix, 'EditTabPresets', $edit_tab_presets);
}
/**
* [HOOK] Allows to add cloned subitem to given prefix
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnCloneSubItem(kEvent $event)
{
parent::OnCloneSubItem($event);
if ( $event->MasterEvent->Prefix == 'rev' ) {
$clones = $this->Application->getUnitOption($event->MasterEvent->Prefix, 'Clones');
$subitem_prefix = $event->Prefix . '-' . $event->MasterEvent->Prefix;
$clones[$subitem_prefix]['ConfigMapping'] = Array (
'PerPage' => 'Comm_Perpage_Reviews',
'ReviewDelayInterval' => 'product_ReviewDelay_Value',
'ReviewDelayValue' => 'product_ReviewDelay_Interval',
);
$this->Application->setUnitOption($event->MasterEvent->Prefix, 'Clones', $clones);
}
}
/**
* Adds product to comparison list
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnAddToCompare(kEvent $event)
{
$products = $this->getCompareProducts();
$product_id = (int)$this->Application->GetVar($event->Prefix . '_id');
if ( $product_id ) {
$max_products = $this->Application->ConfigValue('MaxCompareProducts');
if ( count($products) < $max_products ) {
$products[] = $product_id;
$this->Application->Session->SetCookie('compare_products', implode('|', array_unique($products)));
$event->SetRedirectParam('result', 'added');
}
else {
$event->SetRedirectParam('result', 'error');
}
}
$event->SetRedirectParam('pass', 'm,p');
}
/**
* Adds product to comparison list
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnRemoveFromCompare(kEvent $event)
{
$products = $this->getCompareProducts();
$product_id = (int)$this->Application->GetVar($event->Prefix . '_id');
if ( $product_id && in_array($product_id, $products) ) {
$products = array_diff($products, Array ($product_id));
$this->Application->Session->SetCookie('compare_products', implode('|', array_unique($products)));
$event->SetRedirectParam('result', 'removed');
}
$event->SetRedirectParam('pass', 'm,p');
}
/**
* Cancels product compare
*
* @param kEvent $event
* @return void
* @access protected
*/
protected function OnCancelCompare(kEvent $event)
{
$this->Application->Session->SetCookie('compare_products', '', -1);
$event->SetRedirectParam('result', 'all_removed');
}
/**
* Returns products, that needs to be compared with each other
*
* @return Array
* @access protected
*/
protected function getCompareProducts()
{
$products = $this->Application->GetVarDirect('compare_products', 'Cookie');
$products = $products ? explode('|', $products) : Array ();
return $products;
}
}
\ No newline at end of file

Event Timeline