/** * Reduces the points balance for the user identified by $user_id * * @since 1.0 * @param int $user_id the user identifier * @param int $points the points to reduce, ie 75 * @param string $event_type the type of event responsible * @param mixed $data optional arbitrary data to associate with the log for this action * @param int $order_id optional order identifier, if this action is associated with a particular order * @return boolean true if the points are successfully reduced from the user's balance */ public static function decrease_points($user_id, $points, $event_type, $data = null, $order_id = null) { global $wc_points_rewards, $wpdb; // ensure the user exists $user = get_userdata($user_id); if (false === $user) { return false; } $points = apply_filters('wc_points_rewards_decrease_points', $points, $user_id, $event_type, $data, $order_id); // get any existing points records $query = "SELECT * FROM {$wc_points_rewards->user_points_db_tablename} WHERE user_id = %d and points_balance != 0 ORDER BY date ASC"; $user_points = $wpdb->get_results($wpdb->prepare($query, $user_id)); // no non-zero records, so create a new one if (empty($user_points)) { $_data = array('user_id' => $user_id, 'points' => -$points, 'points_balance' => -$points, 'date' => current_time('mysql', 1)); $format = array('%d', '%d', '%d', '%s'); if ($order_id) { $_data['order_id'] = $order_id; $format[] = '%d'; } // create the negative-balance user points record $wpdb->insert($wc_points_rewards->user_points_db_tablename, $_data, $format); } elseif (count($user_points) > 0) { // existing non-zero points records $points_difference = -$points; // the goal is to get each existing record as close to zero as possible, oldest to newest foreach ($user_points as $index => &$_points) { if ($_points->points_balance > 0 && $points_difference < 0) { $_points->points_balance += $points_difference; if ($_points->points_balance >= 0 || count($user_points) - 1 == $index) { // used up all of points_difference, or reached the newest user points record which therefore receives the remaining balance $points_difference = 0; break; } else { // still have more points balance to distribute $points_difference = $_points->points_balance; $_points->points_balance = 0; } } elseif (count($user_points) - 1 == $index && 0 != $points_difference) { // if we made it here, assign all remaining points to the final record and we're done $_points->points_balance += $points_difference; $points_difference = 0; } } // update any affected rows for ($i = 0; $i <= $index; $i++) { $wpdb->update($wc_points_rewards->user_points_db_tablename, array('points_balance' => $user_points[$i]->points_balance), array('id' => $user_points[$i]->id), array('%d'), array('%d')); } } // update the current points balance user meta $points_balance = (int) get_user_meta($user_id, 'wc_points_balance'); update_user_meta($user_id, 'wc_points_balance', $points_balance - $points); // log the points change $args = array('user_id' => $user_id, 'points' => -$points, 'event_type' => $event_type); // optional associated order if ($order_id) { $args['order_id'] = $order_id; } // optional associated data if ($data) { $args['data'] = $data; } // log the event WC_Points_Rewards_Points_Log::add_log_entry($args); do_action('wc_points_rewards_after_reduce_points', $user_id, $points_balance); // always return true for now return true; }