/** * Deducts point from a user wallet balance. * * It will first validate if the user has sufficient balance for the deduct. * * @see UserBase::_validate_add_deduct_point() * @see UserBase::get_wallet_table_name() * * @throws error exception if there is an error: *
* A181002 - Insufficient balance for this transaction. * A181003 - Error deduct user point. ** * @param string $pointType the type of wallet to deduct from the user. * @param double $amount the amount of wallet to deduct from the user. * @param string $remarks the remarks for the transaction. * @param string $descrCode the transaction details, in json formatted, which can later be reconstructed back * to display the transaction details to user. * @param string $transType the type of transaction, usually numeric code. * @param bool $allowNegative whether to allow negative balance if the amount to deduct is more than the available balance. * @param bool $save_record whether to record the transaction in the transaction table, see UserBase::get_wallet_table_name(), defaults to true. * @param array $params extra parameters which consists of the following: *
* Array( * 'data' => array, //array of field and value pair, which will be stored to the wallet record table. * The field to store cannot be one of the field in the wallet record table including * 'id', 'wtype', 'atype', 'auid', 'uid', 'type', 'amount', 'balance', 'trans_type', 'descr', 'descr_code', 'cdate' * 'decimal' - the number of decimal to use to round the input $amount, this is due to PHP internal calculation * will sometimes make the number actually bigger than it appears. * ) ** * @return int the new transaction ID created from this operation. */ public function deduct_point($pointType, $amount, $remarks, $descrCode = '', $transType = '', $allowNegative = false, $save_record = true, $params = array()){ $method_code = 'A181'; $actionCode = $this->_class_code . $method_code; $transCode = $this->_class_trans_code.'dpoint-' . $pointType; $this->_validate_add_deduct_point('deduct', $pointType, $amount, $remarks, $descrCode, $transType, $allowNegative, $save_record, $params); $last_id = ''; $decimal = strlen($params['decimal']) && is_numeric($params['decimal']) && $params['decimal'] > 0? $params['decimal'] : 2; $decimal = round(max($decimal, 0)); if(!$allowNegative && round($amount, $decimal) > round($this->_user[$pointType], $decimal)){ $this->_set_error_code($method_code . '002'); $log = T::xt("Insufficient balance for this transaction. Available: {avai} Requested: {amount}", array( '{avai}' => number_format($this->_user[$pointType], 2), '{amount}' => number_format($amount, 2), ) ); $this->_set_log($log); throw new Xception ($log, $this->err_code); } else{ $trans_table_name = $this->get_wallet_table_name($pointType); if($allowNegative){ $sql = "UPDATE {$this->_linked_user_table} SET `{$pointType}`=`{$pointType}`-$amount WHERE uid='{$this->_user_id}' LIMIT 1"; } else{ $sql = "UPDATE {$this->_linked_user_table} SET `{$pointType}`=`{$pointType}`-$amount WHERE uid='{$this->_user_id}' AND {$pointType}>={$amount} LIMIT 1"; } try{ $wrs = $this->_db->query($sql); $this->_reload_account(); //no record affected, means user has insufficient wallet balance if($wrs->rowCount() <= 0){ $this->_set_error_code($method_code . '004'); $log = T::xt("Insufficient balance for this transaction. Available: {avai} Requested: {amount}", array( '{avai}' => number_format($this->_user[$pointType], 2), '{amount}' => number_format($amount, 2), ) ); $this->_set_log($log); $throw = true; //throw later } } catch(exception $e){ $this->_set_error_code($method_code . '003'); $log = "Error deduct user point:\nSQL: $sql\n\nError: " . $e->getMessage(); $this->_set_log($log); Log::logCriticalErrors($actionCode, $transCode, $this->logs, array('object' => $this->_class_code, 'oid' => $this->_user_id)); throw new Xception ($this->logs, $this->err_code); } if($throw){ throw new Xception ($log, $this->err_code); } if($save_record){ $balance = $this->_user[$pointType]; $initiator = Util::getInitiator(); $wdata = array( 'wtype' => $pointType, 'atype' => $initiator['icode'], 'auid' => $initiator['iid'], 'uid' => $this->_user_id, 'type' => 'd', 'amount' => $amount, 'balance' => $balance, 'descr' => $remarks, 'descr_code' => $descrCode, 'trans_type' => $transType, 'cdate' => Util::system_datetime(), ); if($params['data']){ foreach($params['data'] as $f => $v){ $wdata[$f] = $v; } } $insert_q = DBHelper::prepare_insert($wdata); $sql = "INSERT INTO {$trans_table_name} {$insert_q}"; try{ $this->_db->query($sql); $last_id = $this->_db->lastInsertId(); } catch(exception $e){ $this->_set_error_code($method_code . '003'); $log = "Error deduct user point:\nSQL: $sql\n\nError: " . $e->getMessage(); $this->_set_log($log); Log::logCriticalErrors($actionCode, $transCode, $this->logs, array('object' => $this->_class_code, 'oid' => $this->_user_id)); throw new Xception ($this->logs, $this->err_code); } } } //record the activity $actlogs = new ActLogs(); $identifiers = $this->get_identifiers(); $identifiers[] = $method_code; $actlogs->set_identifiers($identifiers); $actlogs->record_log($actionCode, $transCode, array('point_type' => $pointType, 'amount' => $amount, 'remarks' => $remarks, 'descr_code' => $descrCode, 'trans_type' => $transType, 'allow_negative' => $allowNegative, 'params' => $params)); return $last_id; } /** * Adds points to a user wallet balance. * * @see UserBase::_validate_add_deduct_point() * @see UserBase::get_wallet_table_name() * * @throws error exception if there is an error: *
* A182002 - Error add user point. ** * @param string $pointType the type of wallet to add to the user. * @param double $amount the amount of wallet to add to the user. * @param string $remarks the remarks for the transaction. * @param string $descrCode the transaction details, in json format, which can later be reconstructed back * to display the transaction details to user. * @param string $transType the type of transaction, usually numeric code. * @param bool $save_record whether to record the transaction in the transaction table, see UserBase::get_wallet_table_name(), defaults to true. * @param array $params extra parameters which consists of the following: *
* Array( * 'data' => array, //array of field and value pair, which will be stored to the wallet record table. * The field to store cannot be one of the field in the wallet record table including * 'id', 'wtype', 'atype', 'auid', 'uid', 'type', 'amount', 'balance', 'trans_type', 'descr', 'descr_code', 'cdate' * ) ** * @return int the new transaction ID created from this operation. */ public function add_point($pointType, $amount, $remarks, $descrCode = '', $transType = '', $save_record = true, $params = array()){ $method_code = 'A182'; $actionCode = $this->_class_code . $method_code; $transCode = $this->_class_trans_code.'cpoint-' . $pointType; $this->_validate_add_deduct_point('add', $pointType, $amount, $remarks, $descrCode, $transType, $allowNegative = false, $save_record, $params); $trans_table_name = $this->get_wallet_table_name($pointType); $this->_reload_account(); $sqls[] = "UPDATE {$this->_linked_user_table} SET `{$pointType}`=`{$pointType}`+{$amount} WHERE uid='{$this->_user_id}' LIMIT 1"; if($save_record){ $balance = $this->_user[$pointType] + $amount; $initiator = Util::getInitiator(); $wdata = array( 'wtype' => $pointType, 'atype' => $initiator['icode'], 'auid' => $initiator['iid'], 'uid' => $this->_user_id, 'type' => 'c', 'amount' => $amount, 'balance' => $balance, 'descr' => $remarks, 'descr_code' => $descrCode, 'trans_type' => $transType, 'cdate' => Util::system_datetime(), ); if($params['data']){ foreach($params['data'] as $f => $v){ $wdata[$f] = $v; } } $insert_q = DBHelper::prepare_insert($wdata); $sqls['history'] = "INSERT INTO {$trans_table_name} {$insert_q}"; $last_ids = array(); } try{ $exSqls = array(); foreach($sqls as $type => $sql){ $exSqls[] = $sql; $this->_db->query($sql); $last_ids[$type] = $this->_db->lastInsertId(); } $this->_reload_account(); } catch(exception $e){ $this->_set_error_code($method_code . '002'); $nonSqls = array_diff($sqls, $exSqls); $log = "Error add user point:\nSQL:\n".implode("\n", $exSqls)."\n\nError: ".$e->getMessage()."\n".($nonSqls? "\nNon-executed SQL:\n".implode("\n", $nonSqls)."\n" : ''); $this->_set_log($log); Log::logCriticalErrors($actionCode, $transCode, $this->logs, array('object' => $this->_class_code, 'oid' => $this->_user_id, 'data' => func_get_args())); throw new Xception ($this->logs, $this->err_code); } //record the activity $actlogs = new ActLogs(); $identifiers = $this->get_identifiers(); $identifiers[] = $method_code; $actlogs->set_identifiers($identifiers); $actlogs->record_log($actionCode, $transCode, array('point_type' => $pointType, 'amount' => $amount, 'remarks' => $remarks, 'descr_code' => $descrCode, 'trans_type' => $transType, 'params' => $params)); return $last_ids['history']; } /** * Validates whether the given point type is a valid point type for a particular user. * * @throws error exception if there is an error: *
* A281001 - No such point exists. ** * @param string $point_type the type of wallet to verify. */ protected function _validate_user_point_type($point_type){ $method_code = 'A281'; $this->_validateUserStd(); if(!isset($this->_user[$point_type])){ $this->_set_error_code($method_code . '001'); $log = T::xt('No such point ({point}) exist!', array('{point}' => $point_type)); $this->_set_log($log); throw new Xception ($log, $this->err_code); } } /** * Validates the parameters for add / deduct user point. * * @throws error exception if there is an error: *
* A282001 - Invalid $params, it must be an array. * A282002 - Invalid $params["data"], some of the data parameters are invalid. ** * @param string $add_deduct the type of transaction, can be "add" (add point) or "deduct" (deduct point). * @param string $point_type the type of wallet to add/deduct to the user. * @param double $amount the amount of wallet to add/deduct to the user. * @param string $remarks the remarks for the transaction. * @param string $descr_code the transaction details, in json formatted, which can later be reconstructed back * to display the transaction details to user. * @param string $trans_type the type of transaction, usually numeric code. * @param bool $allow_negative whether to allow negative balance in deduct operation. * @param bool $save_record whether to record the transaction in the transaction table. * @param array $params extra parameters */ protected function _validate_add_deduct_point($add_deduct, $point_type, $amount, $remarks, $descr_code, $trans_type, $allow_negative, $save_record, $params){ $method_code = 'A282'; $this->_validateUserStd(); $this->_validate_user_point_type($point_type); $method = $add_deduct == 'add'? 'add_point' : 'deduct_point'; if(!is_array($params)){ $this->_set_error_code($method_code . '001'); $log = T::xt('Invalid $params for {method}(), it must be an array!', array('{method}' => $method)); $this->_set_log($log); throw new Xception ($log, $this->err_code); } if($params['data']){ $invalid_fields = ''; foreach($params['data'] as $field => $value){ if(in_array($field, array('id', 'wtype', 'atype', 'auid', 'uid', 'type', 'amount', 'balance', 'trans_type', 'descr', 'descr_code', 'cdate'))){ $invalid_fields .= $field . ', '; } } if($invalid_fields){ $this->_set_error_code($method_code . '002'); $log = T::xt('Invalid $params["data"] for {method}(), some of the data parameters are invalid: {data}!', array('{method}' => $method, '{data}' => rtrim($invalid_fields, ', '))); $this->_set_log($log); throw new Xception ($log, $this->err_code); } } }