| @@ -1,445 +1,667 @@ | ||
| 1 | -<?php | |
| 2 | -/** | |
| 3 | - * Defines functions related to order | |
| 4 | - * | |
| 5 | - * @author ThimPress | |
| 6 | - * @package LearnPress/Functions | |
| 7 | - * @version 1.0 | |
| 8 | - */ | |
| 9 | - | |
| 10 | -defined( 'ABSPATH' ) || exit; | |
| 11 | - | |
| 12 | -/** | |
| 13 | - * Generate unique key for an order. | |
| 14 | - * | |
| 15 | - * @return mixed | |
| 16 | - */ | |
| 17 | -function learn_press_generate_order_key() { | |
| 18 | - return apply_filters( 'learn-press/order-key', strtoupper( uniqid( 'ORDER' ) ) ); | |
| 19 | -} | |
| 20 | - | |
| 21 | -/** | |
| 22 | - * Update Order status | |
| 23 | - * | |
| 24 | - * @param int | |
| 25 | - * @param string | |
| 26 | - * | |
| 27 | - * @return bool | |
| 28 | - */ | |
| 29 | -function learn_press_update_order_status( $order_id, $status = '' ) { | |
| 30 | - $order = new LP_Order( $order_id ); | |
| 31 | - if ( $order ) { | |
| 32 | - return $order->update_status( $status ); | |
| 33 | - } | |
| 34 | - | |
| 35 | - return false; | |
| 36 | -} | |
| 37 | - | |
| 38 | -/** | |
| 39 | - * Add order item meta data. | |
| 40 | - * | |
| 41 | - * @param int $item_id | |
| 42 | - * @param string $meta_key | |
| 43 | - * @param mixed $meta_value | |
| 44 | - * @param string $prev_value | |
| 45 | - * | |
| 46 | - * @return false|int | |
| 47 | - */ | |
| 48 | -function learn_press_add_order_item_meta( $item_id, $meta_key, $meta_value, $prev_value = '' ) { | |
| 49 | - return add_metadata( 'learnpress_order_item', $item_id, $meta_key, $meta_value, $prev_value ); | |
| 50 | -} | |
| 51 | - | |
| 52 | -/** | |
| 53 | - * Update order item meta data. | |
| 54 | - * | |
| 55 | - * @param int $item_id | |
| 56 | - * @param string $meta_key | |
| 57 | - * @param mixed $meta_value | |
| 58 | - * @param string $prev_value | |
| 59 | - * | |
| 60 | - * @return bool|int | |
| 61 | - */ | |
| 62 | -function learn_press_update_order_item_meta( $item_id, $meta_key, $meta_value, $prev_value = '' ) { | |
| 63 | - return update_metadata( 'learnpress_order_item', $item_id, $meta_key, $meta_value, $prev_value ); | |
| 64 | -} | |
| 65 | - | |
| 66 | -/** | |
| 67 | - * Delete order item meta data. | |
| 68 | - * | |
| 69 | - * @param int $item_id | |
| 70 | - * @param string $meta_key | |
| 71 | - * @param mixed $meta_value | |
| 72 | - * @param bool $delete_all | |
| 73 | - * | |
| 74 | - * @return bool | |
| 75 | - */ | |
| 76 | -function learn_press_delete_order_item_meta( $item_id, $meta_key, $meta_value, $delete_all = false ) { | |
| 77 | - return delete_metadata( 'learnpress_order_item', $item_id, $meta_key, $meta_value, $delete_all ); | |
| 78 | -} | |
| 79 | - | |
| 80 | -/** | |
| 81 | - * Get order item meta data. | |
| 82 | - * | |
| 83 | - * @param int $item_id | |
| 84 | - * @param string $meta_key | |
| 85 | - * @param bool $single | |
| 86 | - * | |
| 87 | - * @return mixed | |
| 88 | - */ | |
| 89 | -function learn_press_get_order_item_meta( $item_id, $meta_key, $single = true ) { | |
| 90 | - return get_metadata( 'learnpress_order_item', $item_id, $meta_key, $single ); | |
| 91 | -} | |
| 92 | - | |
| 93 | -/** | |
| 94 | - * Get order | |
| 95 | - * | |
| 96 | - * @param mixed $the_order | |
| 97 | - * | |
| 98 | - * @return LP_Order|bool object instance | |
| 99 | - */ | |
| 100 | -function learn_press_get_order( $the_order = false ) { | |
| 101 | - global $post; | |
| 102 | - $the_id = 0; | |
| 103 | - if ( false === $the_order && is_a( $post, 'WP_Post' ) && LP_ORDER_CPT === get_post_type( $post ) ) { | |
| 104 | - $the_id = $post->ID; | |
| 105 | - } elseif ( is_numeric( $the_order ) ) { | |
| 106 | - $the_id = $the_order; | |
| 107 | - } elseif ( $the_order instanceof LP_Order ) { | |
| 108 | - $the_id = $the_order->get_id(); | |
| 109 | - } elseif ( ! empty( $the_order->ID ) ) { | |
| 110 | - $the_id = $the_order->ID; | |
| 111 | - } | |
| 112 | - | |
| 113 | - if ( LP_ORDER_CPT != get_post_type( $the_id ) ) { | |
| 114 | - return false; | |
| 115 | - } | |
| 116 | - | |
| 117 | - return new LP_Order( $the_id ); | |
| 118 | -} | |
| 119 | - | |
| 120 | -/** | |
| 121 | - * Count orders by it's status | |
| 122 | - * | |
| 123 | - * @param array $args | |
| 124 | - * @Todo tungnx review to rewrite query | |
| 125 | - * @return array | |
| 126 | - */ | |
| 127 | -function learn_press_count_orders( $args = array() ) { | |
| 128 | - if ( is_string( $args ) ) { | |
| 129 | - $args = array( 'status' => $args ); | |
| 130 | - } else { | |
| 131 | - $args = wp_parse_args( | |
| 132 | - $args, | |
| 133 | - array( | |
| 134 | - 'status' => '', | |
| 135 | - ) | |
| 136 | - ); | |
| 137 | - } | |
| 138 | - global $wpdb; | |
| 139 | - $statuses = $args['status']; | |
| 140 | - | |
| 141 | - if ( ! $statuses ) { | |
| 142 | - $statuses = array_keys( LP_Order::get_order_statuses() ); | |
| 143 | - } | |
| 144 | - | |
| 145 | - settype( $statuses, 'array' ); | |
| 146 | - $size_of_status = sizeof( $statuses ); | |
| 147 | - | |
| 148 | - foreach ( $statuses as $k => $status ) { | |
| 149 | - $statuses[ $k ] = ! preg_match( '~^lp-~', $status ) ? 'lp-' . $status : $status; | |
| 150 | - } | |
| 151 | - | |
| 152 | - $format = array_fill( 0, $size_of_status, '%s' ); | |
| 153 | - $counts = array_fill_keys( $statuses, 0 ); | |
| 154 | - $statuses[] = LP_ORDER_CPT; | |
| 155 | - $query = $wpdb->prepare( | |
| 156 | - " | |
| 157 | - SELECT COUNT(ID) AS count, post_status AS status | |
| 158 | - FROM {$wpdb->posts} o | |
| 159 | - WHERE post_status IN(" . join( ',', $format ) . ') | |
| 160 | - AND post_type = %s | |
| 161 | - GROUP BY o.post_status | |
| 162 | - ', | |
| 163 | - $statuses | |
| 164 | - ); | |
| 165 | - | |
| 166 | - $results = $wpdb->get_results( $query ); | |
| 167 | - if ( $results ) { | |
| 168 | - foreach ( $results as $result ) { | |
| 169 | - if ( array_key_exists( $result->status, $counts ) ) { | |
| 170 | - $counts[ $result->status ] = absint( $result->count ); | |
| 171 | - } | |
| 172 | - } | |
| 173 | - } | |
| 174 | - | |
| 175 | - return $size_of_status > 1 ? $counts : reset( $counts ); | |
| 176 | -} | |
| 177 | - | |
| 178 | -/** | |
| 179 | - * Format price with currency and other settings. | |
| 180 | - * | |
| 181 | - * @param float $price | |
| 182 | - * @param string $currency | |
| 183 | - * | |
| 184 | - * @return string | |
| 185 | - */ | |
| 186 | -function learn_press_format_price( $price = 0, $currency = '' ): string { | |
| 187 | - if ( ! is_numeric( $price ) ) { | |
| 188 | - $price = 0; | |
| 189 | - } | |
| 190 | - | |
| 191 | - $before = $after = ''; | |
| 192 | - | |
| 193 | - $currency = esc_html( | |
| 194 | - is_string( $currency ) && '' !== $currency | |
| 195 | - ? $currency | |
| 196 | - : learn_press_get_currency_symbol() | |
| 197 | - ); | |
| 198 | - $thousands_separator = esc_html( LP_Settings::get_option( 'thousands_separator', ',' ) ); | |
| 199 | - $number_of_decimals = esc_html( LP_Settings::get_option( 'number_of_decimals', 2 ) ); | |
| 200 | - $decimals_separator = esc_html( LP_Settings::get_option( 'decimals_separator', '.' ) ); | |
| 201 | - | |
| 202 | - switch ( LP_Settings::get_option( 'currency_pos' ) ) { | |
| 203 | - default: | |
| 204 | - $before = $currency; | |
| 205 | - break; | |
| 206 | - case 'left_with_space': | |
| 207 | - $before = $currency . ' '; | |
| 208 | - break; | |
| 209 | - case 'right': | |
| 210 | - $after = $currency; | |
| 211 | - break; | |
| 212 | - case 'right_with_space': | |
| 213 | - $after = ' ' . $currency; | |
| 214 | - } | |
| 215 | - | |
| 216 | - return $before . number_format( $price, $number_of_decimals, $decimals_separator, $thousands_separator ) . $after; | |
| 217 | -} | |
| 218 | - | |
| 219 | -/** | |
| 220 | - * Update | |
| 221 | - * | |
| 222 | - * @param $order_id | |
| 223 | - * | |
| 224 | - * @return array|bool | |
| 225 | - */ | |
| 226 | -function learn_press_update_order_items( $order_id ) { | |
| 227 | - $order = learn_press_get_order( $order_id ); | |
| 228 | - if ( ! $order ) { | |
| 229 | - return false; | |
| 230 | - } | |
| 231 | - | |
| 232 | - $subtotal = 0; | |
| 233 | - $total = 0; | |
| 234 | - $items = $order->get_items(); | |
| 235 | - | |
| 236 | - if ( $items ) { | |
| 237 | - foreach ( $items as $item ) { | |
| 238 | - $subtotal += $item['subtotal']; | |
| 239 | - $total += $item['total']; | |
| 240 | - } | |
| 241 | - } | |
| 242 | - | |
| 243 | - update_post_meta( $order_id, '_order_currency', learn_press_get_currency() ); | |
| 244 | - update_post_meta( $order_id, '_prices_include_tax', 'no' ); | |
| 245 | - update_post_meta( $order_id, '_order_subtotal', $subtotal ); | |
| 246 | - update_post_meta( $order_id, '_order_total', $total ); | |
| 247 | - update_post_meta( $order_id, '_order_key', learn_press_generate_order_key() ); | |
| 248 | - update_post_meta( $order_id, '_payment_method', '' ); | |
| 249 | - update_post_meta( $order_id, '_payment_method_title', '' ); | |
| 250 | - update_post_meta( $order_id, '_order_version', '1.0' ); | |
| 251 | - | |
| 252 | - return array( | |
| 253 | - 'subtotal' => $subtotal, | |
| 254 | - 'total' => $total, | |
| 255 | - 'currency' => learn_press_get_currency(), | |
| 256 | - ); | |
| 257 | -} | |
| 258 | - | |
| 259 | -/** | |
| 260 | - * Format order's ID in ten numbers. Eg: 0000000XXX. | |
| 261 | - * | |
| 262 | - * @param int $order_number | |
| 263 | - * | |
| 264 | - * @since 2.0.0 | |
| 265 | - * | |
| 266 | - * @return string | |
| 267 | - */ | |
| 268 | -function learn_press_transaction_order_number( $order_number ) { | |
| 269 | - $formatted_number = apply_filters( 'learn_press_get_order_number', '#' . sprintf( "%'.010d", $order_number ), $order_number ); | |
| 270 | - | |
| 271 | - return apply_filters( 'learn-press/order-number-formatted', $formatted_number, $order_number ); | |
| 272 | -} | |
| 273 | - | |
| 274 | -/** | |
| 275 | - * Get list of registered order's statues for registering with wp post's status. | |
| 276 | - * | |
| 277 | - * @since 2.0.0 | |
| 278 | - * | |
| 279 | - * @return array | |
| 280 | - */ | |
| 281 | -function learn_press_get_register_order_statuses() { | |
| 282 | - $order_statues = array(); | |
| 283 | - | |
| 284 | - $order_statues['lp-completed'] = array( | |
| 285 | - 'label' => _x( 'Completed', 'Order status', 'learnpress' ), | |
| 286 | - 'public' => false, | |
| 287 | - 'exclude_from_search' => false, | |
| 288 | - 'show_in_admin_all_list' => true, | |
| 289 | - 'show_in_admin_status_list' => true, | |
| 290 | - 'label_count' => _n_noop( 'Completed <span class="count">(%s)</span>', 'Completed <span class="count">(%s)</span>', 'learnpress' ), | |
| 291 | - ); | |
| 292 | - $order_statues['lp-pending'] = array( | |
| 293 | - 'label' => _x( 'Pending', 'Order status', 'learnpress' ), | |
| 294 | - 'public' => false, | |
| 295 | - 'exclude_from_search' => false, | |
| 296 | - 'show_in_admin_all_list' => true, | |
| 297 | - 'show_in_admin_status_list' => true, | |
| 298 | - 'label_count' => _n_noop( 'Pending Payment <span class="count">(%s)</span>', 'Pending Payment <span class="count">(%s)</span>', 'learnpress' ), | |
| 299 | - ); | |
| 300 | - $order_statues['lp-processing'] = array( | |
| 301 | - 'label' => _x( 'Processing', 'Order status', 'learnpress' ), | |
| 302 | - 'public' => false, | |
| 303 | - 'exclude_from_search' => false, | |
| 304 | - 'show_in_admin_all_list' => true, | |
| 305 | - 'show_in_admin_status_list' => true, | |
| 306 | - 'label_count' => _n_noop( 'Processing <span class="count">(%s)</span>', 'Processing <span class="count">(%s)</span>', 'learnpress' ), | |
| 307 | - ); | |
| 308 | - $order_statues['lp-cancelled'] = array( | |
| 309 | - 'label' => _x( 'Cancelled', 'Order status', 'learnpress' ), | |
| 310 | - 'public' => false, | |
| 311 | - 'exclude_from_search' => false, | |
| 312 | - 'show_in_admin_all_list' => true, | |
| 313 | - 'show_in_admin_status_list' => true, | |
| 314 | - 'label_count' => _n_noop( 'Cancelled <span class="count">(%s)</span>', 'Cancelled <span class="count">(%s)</span>', 'learnpress' ), | |
| 315 | - ); | |
| 316 | - $order_statues['lp-failed'] = array( | |
| 317 | - 'label' => _x( 'Failed', 'Order status', 'learnpress' ), | |
| 318 | - 'public' => false, | |
| 319 | - 'exclude_from_search' => false, | |
| 320 | - 'show_in_admin_all_list' => true, | |
| 321 | - 'show_in_admin_status_list' => true, | |
| 322 | - 'label_count' => _n_noop( 'Failed <span class="count">(%s)</span>', 'Failed <span class="count">(%s)</span>', 'learnpress' ), | |
| 323 | - ); | |
| 324 | - $order_statues['trash'] = array( | |
| 325 | - 'label' => _x( 'Trash', 'Order status', 'learnpress' ), | |
| 326 | - 'public' => false, | |
| 327 | - 'exclude_from_search' => false, | |
| 328 | - 'show_in_admin_all_list' => true, | |
| 329 | - 'show_in_admin_status_list' => true, | |
| 330 | - 'label_count' => _n_noop( 'Trash <span class="count">(%s)</span>', 'Trash <span class="count">(%s)</span>', 'learnpress' ), | |
| 331 | - ); | |
| 332 | - | |
| 333 | - return $order_statues; | |
| 334 | -} | |
| 335 | - | |
| 336 | -function _learn_press_get_order_status_description( $status ) { | |
| 337 | - $descriptions = array( | |
| 338 | - 'pending' => __( 'Order received in case a user purchases a course but doesn\'t finalize the order.', 'learnpress' ), | |
| 339 | - 'processing' => __( 'Payment received and the order is awaiting fulfillment.', 'learnpress' ), | |
| 340 | - 'completed' => __( 'The order is fulfilled and completed.', 'learnpress' ), | |
| 341 | - 'cancelled' => __( 'The order is cancelled by an admin or the customer.', 'learnpress' ), | |
| 342 | - ); | |
| 343 | - | |
| 344 | - return apply_filters( 'learn_press_order_status_description', ! empty( $descriptions[ $status ] ) ? $descriptions[ $status ] : '' ); | |
| 345 | -} | |
| 346 | - | |
| 347 | -/** | |
| 348 | - * Get status of an order by the ID. | |
| 349 | - * | |
| 350 | - * @param int $order_id | |
| 351 | - * | |
| 352 | - * @return bool|string | |
| 353 | - */ | |
| 354 | -function learn_press_get_order_status( $order_id ) { | |
| 355 | - $order = learn_press_get_order( $order_id ); | |
| 356 | - | |
| 357 | - if ( $order ) { | |
| 358 | - return $order->get_status(); | |
| 359 | - } | |
| 360 | - | |
| 361 | - return false; | |
| 362 | -} | |
| 363 | - | |
| 364 | -if ( ! function_exists( 'learn_press_cancel_order_process' ) ) { | |
| 365 | - /** | |
| 366 | - * Process action allows user to cancel an order is pending | |
| 367 | - * in their profile. | |
| 368 | - */ | |
| 369 | - function learn_press_cancel_order_process() { | |
| 370 | - if ( empty( $_REQUEST['cancel-order'] ) || empty( $_REQUEST['lp-nonce'] ) || | |
| 371 | - ! wp_verify_nonce( $_REQUEST['lp-nonce'], 'cancel-order' ) || is_admin() ) { | |
| 372 | - return; | |
| 373 | - } | |
| 374 | - | |
| 375 | - $user_id = get_current_user_id(); | |
| 376 | - $profile = LP_Profile::instance( $user_id ); | |
| 377 | - $url = $profile->get_tab_link( | |
| 378 | - LP_Settings::instance()->get( 'profile_endpoints.orders', 'orders' ) | |
| 379 | - ); | |
| 380 | - | |
| 381 | - try { | |
| 382 | - $message = [ | |
| 383 | - 'status' => 'error', | |
| 384 | - 'content' => '', | |
| 385 | - ]; | |
| 386 | - | |
| 387 | - $order_id = absint( $_REQUEST['cancel-order'] ); | |
| 388 | - $order = learn_press_get_order( $order_id ); | |
| 389 | - | |
| 390 | - if ( ! $order ) { | |
| 391 | - throw new Exception( sprintf( __( 'Order number <strong>%s</strong> not found', 'learnpress' ), $order_id ) ); | |
| 392 | - } | |
| 393 | - | |
| 394 | - $user_ids = (array) $order->get_user_id(); | |
| 395 | - if ( ! in_array( $user_id, $user_ids ) ) { | |
| 396 | - throw new Exception( __( 'You do not have permission to cancel this order.', 'learnpress' ) ); | |
| 397 | - } | |
| 398 | - | |
| 399 | - if ( $order->has_status( LP_ORDER_PENDING ) ) { | |
| 400 | - $order->update_status( LP_ORDER_CANCELLED ); | |
| 401 | - $order->add_note( __( 'The order is cancelled by the customer', 'learnpress' ) ); | |
| 402 | - | |
| 403 | - $message['status'] = 'success'; | |
| 404 | - $message['content'] = sprintf( __( 'Order number <strong>%s</strong> has been cancelled', 'learnpress' ), $order->get_order_number() ); | |
| 405 | - } else { | |
| 406 | - throw new Exception( | |
| 407 | - __( 'The order number <strong>%s</strong> can not be cancelled.', 'learnpress' ), | |
| 408 | - $order->get_order_number() | |
| 409 | - ); | |
| 410 | - } | |
| 411 | - } catch ( Throwable $e ) { | |
| 412 | - $message['content'] = $e->getMessage(); | |
| 413 | - } | |
| 414 | - | |
| 415 | - learn_press_set_message( $message ); | |
| 416 | - wp_safe_redirect( $url ); | |
| 417 | - exit(); | |
| 418 | - } | |
| 419 | -} | |
| 420 | -add_action( 'init', 'learn_press_cancel_order_process' ); | |
| 421 | - | |
| 422 | - | |
| 423 | -/** | |
| 424 | - * get total price order complete | |
| 425 | - * | |
| 426 | - */ | |
| 427 | - | |
| 428 | -function learn_press_get_total_price_order_complete() { | |
| 429 | - global $wpdb; | |
| 430 | - | |
| 431 | - $query = $wpdb->prepare( | |
| 432 | - "SELECT SUM(meta_value) as order_total From `{$wpdb->prefix}postmeta` as mt | |
| 433 | - INNER JOIN `{$wpdb->prefix}posts` as p ON p.id = mt.post_id | |
| 434 | - WHERE p.post_type = %s AND mt.meta_key = %s | |
| 435 | - AND p.post_status = %s | |
| 436 | - ", | |
| 437 | - LP_ORDER_CPT, | |
| 438 | - '_order_total', | |
| 439 | - 'lp-completed' | |
| 440 | - ); | |
| 441 | - | |
| 442 | - $total = $wpdb->get_results( $query )[0]->order_total; | |
| 443 | - | |
| 444 | - return learn_press_format_price( $total, true ); | |
| 445 | -} | |
| 1 | +<?php | |
| 2 | +/** | |
| 3 | + * Defines functions related to order | |
| 4 | + * | |
| 5 | + * @author ThimPress | |
| 6 | + * @package LearnPress/Functions | |
| 7 | + * @version 1.0 | |
| 8 | + */ | |
| 9 | +use LearnPress\Models\UserItems\UserCourseModel; | |
| 10 | +use LearnPress\Models\UserModel; | |
| 11 | + | |
| 12 | +defined( 'ABSPATH' ) || exit; | |
| 13 | + | |
| 14 | +/** | |
| 15 | + * Generate unique key for an order. | |
| 16 | + * | |
| 17 | + * @return mixed | |
| 18 | + */ | |
| 19 | +function learn_press_generate_order_key() { | |
| 20 | + return apply_filters( 'learn-press/order-key', strtoupper( uniqid( 'ORDER' ) ) ); | |
| 21 | +} | |
| 22 | + | |
| 23 | +/** | |
| 24 | + * Update Order status | |
| 25 | + * | |
| 26 | + * @param int | |
| 27 | + * @param string | |
| 28 | + * | |
| 29 | + * @return bool | |
| 30 | + */ | |
| 31 | +function learn_press_update_order_status( $order_id, $status = '' ) { | |
| 32 | + $order = new LP_Order( $order_id ); | |
| 33 | + if ( $order ) { | |
| 34 | + return $order->update_status( $status ); | |
| 35 | + } | |
| 36 | + | |
| 37 | + return false; | |
| 38 | +} | |
| 39 | + | |
| 40 | +/** | |
| 41 | + * Add order item meta data. | |
| 42 | + * | |
| 43 | + * @param int $item_id | |
| 44 | + * @param string $meta_key | |
| 45 | + * @param mixed $meta_value | |
| 46 | + * @param string $prev_value | |
| 47 | + * | |
| 48 | + * @return false|int | |
| 49 | + */ | |
| 50 | +function learn_press_add_order_item_meta( $item_id, $meta_key, $meta_value, $prev_value = '' ) { | |
| 51 | + return add_metadata( 'learnpress_order_item', $item_id, $meta_key, $meta_value, $prev_value ); | |
| 52 | +} | |
| 53 | + | |
| 54 | +/** | |
| 55 | + * Update order item meta data. | |
| 56 | + * | |
| 57 | + * @param int $item_id | |
| 58 | + * @param string $meta_key | |
| 59 | + * @param mixed $meta_value | |
| 60 | + * @param string $prev_value | |
| 61 | + * | |
| 62 | + * @return bool|int | |
| 63 | + */ | |
| 64 | +function learn_press_update_order_item_meta( $item_id, $meta_key, $meta_value, $prev_value = '' ) { | |
| 65 | + return update_metadata( 'learnpress_order_item', $item_id, $meta_key, $meta_value, $prev_value ); | |
| 66 | +} | |
| 67 | + | |
| 68 | +/** | |
| 69 | + * Delete order item meta data. | |
| 70 | + * | |
| 71 | + * @param int $item_id | |
| 72 | + * @param string $meta_key | |
| 73 | + * @param mixed $meta_value | |
| 74 | + * @param bool $delete_all | |
| 75 | + * | |
| 76 | + * @return bool | |
| 77 | + */ | |
| 78 | +function learn_press_delete_order_item_meta( $item_id, $meta_key, $meta_value, $delete_all = false ) { | |
| 79 | + return delete_metadata( 'learnpress_order_item', $item_id, $meta_key, $meta_value, $delete_all ); | |
| 80 | +} | |
| 81 | + | |
| 82 | +/** | |
| 83 | + * Get order item meta data. | |
| 84 | + * | |
| 85 | + * @param int $item_id | |
| 86 | + * @param string $meta_key | |
| 87 | + * @param bool $single | |
| 88 | + * | |
| 89 | + * @return mixed | |
| 90 | + */ | |
| 91 | +function learn_press_get_order_item_meta( $item_id, $meta_key, $single = true ) { | |
| 92 | + return get_metadata( 'learnpress_order_item', $item_id, $meta_key, $single ); | |
| 93 | +} | |
| 94 | + | |
| 95 | +/** | |
| 96 | + * Get order | |
| 97 | + * | |
| 98 | + * @param mixed $the_order | |
| 99 | + * | |
| 100 | + * @return LP_Order|bool object instance | |
| 101 | + */ | |
| 102 | +function learn_press_get_order( $the_order = false ) { | |
| 103 | + global $post; | |
| 104 | + $the_id = 0; | |
| 105 | + if ( false === $the_order && is_a( $post, 'WP_Post' ) && LP_ORDER_CPT === get_post_type( $post ) ) { | |
| 106 | + $the_id = $post->ID; | |
| 107 | + } elseif ( is_numeric( $the_order ) ) { | |
| 108 | + $the_id = $the_order; | |
| 109 | + } elseif ( $the_order instanceof LP_Order ) { | |
| 110 | + $the_id = $the_order->get_id(); | |
| 111 | + } elseif ( ! empty( $the_order->ID ) ) { | |
| 112 | + $the_id = $the_order->ID; | |
| 113 | + } | |
| 114 | + | |
| 115 | + if ( LP_ORDER_CPT != get_post_type( $the_id ) ) { | |
| 116 | + return false; | |
| 117 | + } | |
| 118 | + | |
| 119 | + return new LP_Order( $the_id ); | |
| 120 | +} | |
| 121 | + | |
| 122 | +/** | |
| 123 | + * Count orders by it's status | |
| 124 | + * | |
| 125 | + * @param array $args | |
| 126 | + * @Todo tungnx review to rewrite query | |
| 127 | + * @return array | |
| 128 | + */ | |
| 129 | +function learn_press_count_orders( $args = array() ) { | |
| 130 | + if ( is_string( $args ) ) { | |
| 131 | + $args = array( 'status' => $args ); | |
| 132 | + } else { | |
| 133 | + $args = wp_parse_args( | |
| 134 | + $args, | |
| 135 | + array( | |
| 136 | + 'status' => '', | |
| 137 | + ) | |
| 138 | + ); | |
| 139 | + } | |
| 140 | + global $wpdb; | |
| 141 | + $statuses = $args['status']; | |
| 142 | + | |
| 143 | + if ( ! $statuses ) { | |
| 144 | + $statuses = array_keys( LP_Order::get_order_statuses() ); | |
| 145 | + } | |
| 146 | + | |
| 147 | + settype( $statuses, 'array' ); | |
| 148 | + $size_of_status = sizeof( $statuses ); | |
| 149 | + | |
| 150 | + foreach ( $statuses as $k => $status ) { | |
| 151 | + $statuses[ $k ] = ! preg_match( '~^lp-~', $status ) ? 'lp-' . $status : $status; | |
| 152 | + } | |
| 153 | + | |
| 154 | + $format = array_fill( 0, $size_of_status, '%s' ); | |
| 155 | + $counts = array_fill_keys( $statuses, 0 ); | |
| 156 | + $statuses[] = LP_ORDER_CPT; | |
| 157 | + $query = $wpdb->prepare( | |
| 158 | + " | |
| 159 | + SELECT COUNT(ID) AS count, post_status AS status | |
| 160 | + FROM {$wpdb->posts} o | |
| 161 | + WHERE post_status IN(" . join( ',', $format ) . ') | |
| 162 | + AND post_type = %s | |
| 163 | + GROUP BY o.post_status | |
| 164 | + ', | |
| 165 | + $statuses | |
| 166 | + ); | |
| 167 | + | |
| 168 | + $results = $wpdb->get_results( $query ); | |
| 169 | + if ( $results ) { | |
| 170 | + foreach ( $results as $result ) { | |
| 171 | + if ( array_key_exists( $result->status, $counts ) ) { | |
| 172 | + $counts[ $result->status ] = absint( $result->count ); | |
| 173 | + } | |
| 174 | + } | |
| 175 | + } | |
| 176 | + | |
| 177 | + return $size_of_status > 1 ? $counts : reset( $counts ); | |
| 178 | +} | |
| 179 | + | |
| 180 | +/** | |
| 181 | + * Format price with currency and other settings. | |
| 182 | + * | |
| 183 | + * @param float $price | |
| 184 | + * @param string $currency | |
| 185 | + * | |
| 186 | + * @return string | |
| 187 | + */ | |
| 188 | +function learn_press_format_price( $price = 0, $currency = '' ): string { | |
| 189 | + if ( ! is_numeric( $price ) ) { | |
| 190 | + $price = 0; | |
| 191 | + } | |
| 192 | + | |
| 193 | + $before = $after = ''; | |
| 194 | + | |
| 195 | + $currency = esc_html( | |
| 196 | + is_string( $currency ) && '' !== $currency | |
| 197 | + ? $currency | |
| 198 | + : learn_press_get_currency_symbol() | |
| 199 | + ); | |
| 200 | + $thousands_separator = esc_html( LP_Settings::get_option( 'thousands_separator', ',' ) ); | |
| 201 | + $number_of_decimals = esc_html( LP_Settings::get_option( 'number_of_decimals', 2 ) ); | |
| 202 | + $decimals_separator = esc_html( LP_Settings::get_option( 'decimals_separator', '.' ) ); | |
| 203 | + | |
| 204 | + switch ( LP_Settings::get_option( 'currency_pos' ) ) { | |
| 205 | + default: | |
| 206 | + $before = $currency; | |
| 207 | + break; | |
| 208 | + case 'left_with_space': | |
| 209 | + $before = $currency . ' '; | |
| 210 | + break; | |
| 211 | + case 'right': | |
| 212 | + $after = $currency; | |
| 213 | + break; | |
| 214 | + case 'right_with_space': | |
| 215 | + $after = ' ' . $currency; | |
| 216 | + } | |
| 217 | + | |
| 218 | + return $before . number_format( $price, $number_of_decimals, $decimals_separator, $thousands_separator ) . $after; | |
| 219 | +} | |
| 220 | + | |
| 221 | +/** | |
| 222 | + * Update | |
| 223 | + * | |
| 224 | + * @param $order_id | |
| 225 | + * | |
| 226 | + * @return array|bool | |
| 227 | + */ | |
| 228 | +function learn_press_update_order_items( $order_id ) { | |
| 229 | + $order = learn_press_get_order( $order_id ); | |
| 230 | + if ( ! $order ) { | |
| 231 | + return false; | |
| 232 | + } | |
| 233 | + | |
| 234 | + $subtotal = 0; | |
| 235 | + $total = 0; | |
| 236 | + $items = $order->get_items(); | |
| 237 | + | |
| 238 | + if ( $items ) { | |
| 239 | + foreach ( $items as $item ) { | |
| 240 | + $subtotal += $item['subtotal']; | |
| 241 | + $total += $item['total']; | |
| 242 | + } | |
| 243 | + } | |
| 244 | + | |
| 245 | + update_post_meta( $order_id, '_order_currency', learn_press_get_currency() ); | |
| 246 | + update_post_meta( $order_id, '_prices_include_tax', 'no' ); | |
| 247 | + update_post_meta( $order_id, '_order_subtotal', $subtotal ); | |
| 248 | + update_post_meta( $order_id, '_order_total', $total ); | |
| 249 | + update_post_meta( $order_id, '_order_key', learn_press_generate_order_key() ); | |
| 250 | + update_post_meta( $order_id, '_payment_method', '' ); | |
| 251 | + update_post_meta( $order_id, '_payment_method_title', '' ); | |
| 252 | + update_post_meta( $order_id, '_order_version', '1.0' ); | |
| 253 | + | |
| 254 | + return array( | |
| 255 | + 'subtotal' => $subtotal, | |
| 256 | + 'total' => $total, | |
| 257 | + 'currency' => learn_press_get_currency(), | |
| 258 | + ); | |
| 259 | +} | |
| 260 | + | |
| 261 | +/** | |
| 262 | + * Format order's ID in ten numbers. Eg: 0000000XXX. | |
| 263 | + * | |
| 264 | + * @param int $order_number | |
| 265 | + * | |
| 266 | + * @since 2.0.0 | |
| 267 | + * | |
| 268 | + * @return string | |
| 269 | + */ | |
| 270 | +function learn_press_transaction_order_number( $order_number ) { | |
| 271 | + $formatted_number = apply_filters( 'learn_press_get_order_number', '#' . sprintf( "%'.010d", $order_number ), $order_number ); | |
| 272 | + | |
| 273 | + return apply_filters( 'learn-press/order-number-formatted', $formatted_number, $order_number ); | |
| 274 | +} | |
| 275 | + | |
| 276 | +/** | |
| 277 | + * Get list of registered order's statues for registering with wp post's status. | |
| 278 | + * | |
| 279 | + * @since 2.0.0 | |
| 280 | + * | |
| 281 | + * @return array | |
| 282 | + */ | |
| 283 | +function learn_press_get_register_order_statuses() { | |
| 284 | + $order_statues = array(); | |
| 285 | + | |
| 286 | + $order_statues['lp-completed'] = array( | |
| 287 | + 'label' => _x( 'Completed', 'Order status', 'learnpress' ), | |
| 288 | + 'public' => false, | |
| 289 | + 'exclude_from_search' => false, | |
| 290 | + 'show_in_admin_all_list' => true, | |
| 291 | + 'show_in_admin_status_list' => true, | |
| 292 | + 'label_count' => _n_noop( 'Completed <span class="count">(%s)</span>', 'Completed <span class="count">(%s)</span>', 'learnpress' ), | |
| 293 | + ); | |
| 294 | + $order_statues['lp-pending'] = array( | |
| 295 | + 'label' => _x( 'Pending', 'Order status', 'learnpress' ), | |
| 296 | + 'public' => false, | |
| 297 | + 'exclude_from_search' => false, | |
| 298 | + 'show_in_admin_all_list' => true, | |
| 299 | + 'show_in_admin_status_list' => true, | |
| 300 | + 'label_count' => _n_noop( 'Pending Payment <span class="count">(%s)</span>', 'Pending Payment <span class="count">(%s)</span>', 'learnpress' ), | |
| 301 | + ); | |
| 302 | + $order_statues['lp-processing'] = array( | |
| 303 | + 'label' => _x( 'Processing', 'Order status', 'learnpress' ), | |
| 304 | + 'public' => false, | |
| 305 | + 'exclude_from_search' => false, | |
| 306 | + 'show_in_admin_all_list' => true, | |
| 307 | + 'show_in_admin_status_list' => true, | |
| 308 | + 'label_count' => _n_noop( 'Processing <span class="count">(%s)</span>', 'Processing <span class="count">(%s)</span>', 'learnpress' ), | |
| 309 | + ); | |
| 310 | + $order_statues['lp-cancelled'] = array( | |
| 311 | + 'label' => _x( 'Cancelled', 'Order status', 'learnpress' ), | |
| 312 | + 'public' => false, | |
| 313 | + 'exclude_from_search' => false, | |
| 314 | + 'show_in_admin_all_list' => true, | |
| 315 | + 'show_in_admin_status_list' => true, | |
| 316 | + 'label_count' => _n_noop( 'Cancelled <span class="count">(%s)</span>', 'Cancelled <span class="count">(%s)</span>', 'learnpress' ), | |
| 317 | + ); | |
| 318 | + $order_statues['lp-failed'] = array( | |
| 319 | + 'label' => _x( 'Failed', 'Order status', 'learnpress' ), | |
| 320 | + 'public' => false, | |
| 321 | + 'exclude_from_search' => false, | |
| 322 | + 'show_in_admin_all_list' => true, | |
| 323 | + 'show_in_admin_status_list' => true, | |
| 324 | + 'label_count' => _n_noop( 'Failed <span class="count">(%s)</span>', 'Failed <span class="count">(%s)</span>', 'learnpress' ), | |
| 325 | + ); | |
| 326 | + $order_statues['lp-refunded'] = array( | |
| 327 | + 'label' => _x( 'Refunded', 'Order status', 'learnpress' ), | |
| 328 | + 'public' => false, | |
| 329 | + 'exclude_from_search' => false, | |
| 330 | + 'show_in_admin_all_list' => true, | |
| 331 | + 'show_in_admin_status_list' => true, | |
| 332 | + 'label_count' => _n_noop( 'Refunded <span class="count">(%s)</span>', 'Refunded <span class="count">(%s)</span>', 'learnpress' ), | |
| 333 | + ); | |
| 334 | + $order_statues['trash'] = array( | |
| 335 | + 'label' => _x( 'Trash', 'Order status', 'learnpress' ), | |
| 336 | + 'public' => false, | |
| 337 | + 'exclude_from_search' => false, | |
| 338 | + 'show_in_admin_all_list' => true, | |
| 339 | + 'show_in_admin_status_list' => true, | |
| 340 | + 'label_count' => _n_noop( 'Trash <span class="count">(%s)</span>', 'Trash <span class="count">(%s)</span>', 'learnpress' ), | |
| 341 | + ); | |
| 342 | + | |
| 343 | + return $order_statues; | |
| 344 | +} | |
| 345 | + | |
| 346 | +function _learn_press_get_order_status_description( $status ) { | |
| 347 | + | |
| 348 | + $status = str_replace( 'lp-', '', (string) $status ); | |
| 349 | + $descriptions = array( | |
| 350 | + 'pending' => __( 'Order received in case a user purchases a course but doesn\'t finalize the order.', 'learnpress' ), | |
| 351 | + 'processing' => __( 'Payment received and the order is awaiting fulfillment.', 'learnpress' ), | |
| 352 | + 'completed' => __( 'The order is fulfilled and completed.', 'learnpress' ), | |
| 353 | + 'cancelled' => __( 'The order is cancelled by an admin or the customer.', 'learnpress' ), | |
| 354 | + 'refunded' => __( 'Order was refunded to the customer.', 'learnpress' ), | |
| 355 | + ); | |
| 356 | + | |
| 357 | + return apply_filters( 'learn_press_order_status_description', ! empty( $descriptions[ $status ] ) ? $descriptions[ $status ] : '' ); | |
| 358 | +} | |
| 359 | +/** | |
| 360 | + * Get status of an order by the ID. | |
| 361 | + * | |
| 362 | + * @param int $order_id | |
| 363 | + * | |
| 364 | + * @return bool|string | |
| 365 | + */ | |
| 366 | +function learn_press_get_order_status( $order_id ) { | |
| 367 | + | |
| 368 | + $order = learn_press_get_order( $order_id ); | |
| 369 | + | |
| 370 | + if ( $order ) { | |
| 371 | + return $order->get_status(); | |
| 372 | + } | |
| 373 | + | |
| 374 | + return false; | |
| 375 | +} | |
| 376 | + | |
| 377 | +if ( ! function_exists( 'learn_press_get_refund_setting' ) ) { | |
| 378 | + /** | |
| 379 | + * Get refund setting value with safe defaults. | |
| 380 | + * | |
| 381 | + * @param string $key | |
| 382 | + * @param mixed $default | |
| 383 | + * | |
| 384 | + * @since 4.3.4 | |
| 385 | + * @version 1.0.0 | |
| 386 | + * @return mixed | |
| 387 | + */ | |
| 388 | + function learn_press_get_refund_setting( string $key, $default = null ) { | |
| 389 | + $defaults = array( | |
| 390 | + 'enable_refund_requests' => 'no', | |
| 391 | + 'auto_refund' => 'no', | |
| 392 | + 'refund_time_limit' => 30, | |
| 393 | + 'require_refund_reason' => 'no', | |
| 394 | + 'allow_resend_after_rejected' => 'no', | |
| 395 | + 'refund_max_completion' => 0, | |
| 396 | + ); | |
| 397 | + | |
| 398 | + if ( null === $default && array_key_exists( $key, $defaults ) ) { | |
| 399 | + $default = $defaults[ $key ]; | |
| 400 | + } | |
| 401 | + | |
| 402 | + return LP_Settings::get_option( $key, $default ); | |
| 403 | + } | |
| 404 | +} | |
| 405 | + | |
| 406 | +if ( ! function_exists( 'learn_press_get_order_refund_supported_gateways' ) ) { | |
| 407 | + /** | |
| 408 | + * Get list gateways that support refund. | |
| 409 | + * | |
| 410 | + * @since 4.3.4 | |
| 411 | + * @version 1.1.0 | |
| 412 | + * @return array | |
| 413 | + */ | |
| 414 | + function learn_press_get_order_refund_supported_gateways(): array { | |
| 415 | + if ( ! class_exists( 'LP_Gateways' ) ) { | |
| 416 | + return array(); | |
| 417 | + } | |
| 418 | + | |
| 419 | + $gateways_instance = LP_Gateways::instance(); | |
| 420 | + if ( ! $gateways_instance || ! method_exists( $gateways_instance, 'get_gateways' ) ) { | |
| 421 | + return array(); | |
| 422 | + } | |
| 423 | + | |
| 424 | + $supported_gateways = array(); | |
| 425 | + $all_gateways = (array) $gateways_instance->get_gateways(); | |
| 426 | + foreach ( $all_gateways as $gateway_id => $gateway ) { | |
| 427 | + $gateway_id = sanitize_key( (string) $gateway_id ); | |
| 428 | + if ( empty( $gateway_id ) ) { | |
| 429 | + continue; | |
| 430 | + } | |
| 431 | + | |
| 432 | + if ( ! is_object( $gateway ) || ! is_callable( array( $gateway, 'refund' ) ) ) { | |
| 433 | + continue; | |
| 434 | + } | |
| 435 | + | |
| 436 | + $supported_gateways[] = $gateway_id; | |
| 437 | + } | |
| 438 | + | |
| 439 | + return array_values( array_unique( $supported_gateways ) ); | |
| 440 | + } | |
| 441 | +} | |
| 442 | + | |
| 443 | +if ( ! function_exists( 'learn_press_cancel_order_process' ) ) { | |
| 444 | + /** | |
| 445 | + * Process action allows user to cancel an order is pending | |
| 446 | + * in their profile. | |
| 447 | + */ | |
| 448 | + function learn_press_cancel_order_process() { | |
| 449 | + | |
| 450 | + if ( empty( $_REQUEST['cancel-order'] ) || empty( $_REQUEST['lp-nonce'] ) || | |
| 451 | + ! wp_verify_nonce( $_REQUEST['lp-nonce'], 'cancel-order' ) || is_admin() ) { | |
| 452 | + return; | |
| 453 | + } | |
| 454 | + | |
| 455 | + $user_id = get_current_user_id(); | |
| 456 | + $profile = LP_Profile::instance( $user_id ); | |
| 457 | + $url = $profile->get_tab_link( | |
| 458 | + LP_Settings::instance()->get( 'profile_endpoints.orders', 'orders' ) | |
| 459 | + ); | |
| 460 | + | |
| 461 | + try { | |
| 462 | + $message = array( | |
| 463 | + 'status' => 'error', | |
| 464 | + 'content' => '', | |
| 465 | + ); | |
| 466 | + | |
| 467 | + $order_id = absint( $_REQUEST['cancel-order'] ); | |
| 468 | + $order = learn_press_get_order( $order_id ); | |
| 469 | + | |
| 470 | + if ( ! $order ) { | |
| 471 | + throw new Exception( sprintf( __( 'Order number <strong>%s</strong> not found', 'learnpress' ), $order_id ) ); | |
| 472 | + } | |
| 473 | + | |
| 474 | + $user_ids = (array) $order->get_user_id(); | |
| 475 | + if ( ! in_array( $user_id, $user_ids ) ) { | |
| 476 | + throw new Exception( __( 'You do not have permission to cancel this order.', 'learnpress' ) ); | |
| 477 | + } | |
| 478 | + | |
| 479 | + if ( $order->has_status( LP_ORDER_PENDING ) ) { | |
| 480 | + $order->update_status( LP_ORDER_CANCELLED ); | |
| 481 | + $order->add_note( __( 'The order is cancelled by the customer', 'learnpress' ) ); | |
| 482 | + | |
| 483 | + $message['status'] = 'success'; | |
| 484 | + $message['content'] = sprintf( __( 'Order number <strong>%s</strong> has been cancelled', 'learnpress' ), $order->get_order_number() ); | |
| 485 | + } else { | |
| 486 | + throw new Exception( | |
| 487 | + sprintf( | |
| 488 | + __( 'The order number <strong>%s</strong> can not be cancelled.', 'learnpress' ), | |
| 489 | + $order->get_order_number() | |
| 490 | + ) | |
| 491 | + ); | |
| 492 | + } | |
| 493 | + } catch ( Throwable $e ) { | |
| 494 | + $message['content'] = $e->getMessage(); | |
| 495 | + } | |
| 496 | + | |
| 497 | + learn_press_set_message( $message ); | |
| 498 | + wp_safe_redirect( $url ); | |
| 499 | + exit(); | |
| 500 | + } | |
| 501 | +} | |
| 502 | +add_action( 'init', 'learn_press_cancel_order_process' ); | |
| 503 | +if ( ! function_exists( 'learn_press_get_order_refund_event_data' ) ) { | |
| 504 | + /** | |
| 505 | + * Build normalized refund event payload. | |
| 506 | + * | |
| 507 | + * @since 4.3.5 | |
| 508 | + * @version 1.0.1 | |
| 509 | + * | |
| 510 | + * @param LP_Order $order | |
| 511 | + * @param array $overrides | |
| 512 | + * | |
| 513 | + * @return array | |
| 514 | + */ | |
| 515 | + function learn_press_get_order_refund_event_data( LP_Order $order, array $overrides = array() ): array { | |
| 516 | + $order_id = $order->get_id(); | |
| 517 | + $requested_at = get_post_meta( $order_id, '_lp_refund_requested_at', true ); | |
| 518 | + if ( ! empty( $requested_at ) ) { | |
| 519 | + $requested_at = new LP_Datetime( $requested_at ); | |
| 520 | + $requested_at = sprintf( | |
| 521 | + esc_html__( '%1$s %2$s', 'learnpress' ), | |
| 522 | + $requested_at->format( LP_Datetime::I18N_FORMAT_HAS_TIME ), | |
| 523 | + LP_Datetime::get_timezone_string() | |
| 524 | + ); | |
| 525 | + } | |
| 526 | + | |
| 527 | + $user_id = (int) $order->get_user_id(); | |
| 528 | + $userOrderModel = UserModel::find( $user_id, true ); | |
| 529 | + if ( $userOrderModel instanceof UserModel ) { | |
| 530 | + $requested_by = $userOrderModel->get_display_name(); | |
| 531 | + } else { | |
| 532 | + $requested_by = $order->get_checkout_email(); | |
| 533 | + } | |
| 534 | + | |
| 535 | + $data = array( | |
| 536 | + 'order_id' => $order_id, | |
| 537 | + 'order_number' => $order->get_order_number(), | |
| 538 | + 'order_key' => $order->get_order_key(), | |
| 539 | + 'order_status' => $order->get_status(), | |
| 540 | + 'request_status' => $order->get_refund_request(), | |
| 541 | + 'requested_by' => $requested_by, | |
| 542 | + 'requested_at' => $requested_at, | |
| 543 | + 'reviewed_by' => absint( get_post_meta( $order_id, '_lp_refund_reviewed_by', true ) ), | |
| 544 | + 'reviewed_at' => (string) get_post_meta( $order_id, '_lp_refund_reviewed_at', true ), | |
| 545 | + 'reason' => (string) get_post_meta( $order_id, LP_Order::META_KEY_REFUND_REQUEST_REASON, true ), | |
| 546 | + 'requester_email' => '', | |
| 547 | + 'admin_order_edit_url' => add_query_arg( | |
| 548 | + array( | |
| 549 | + 'post' => $order_id, | |
| 550 | + 'action' => 'edit', | |
| 551 | + ), | |
| 552 | + admin_url( 'post.php' ) | |
| 553 | + ), | |
| 554 | + ); | |
| 555 | + | |
| 556 | + $requested_user_id = absint( $data['requested_by'] ); | |
| 557 | + if ( ! empty( $requested_user_id ) ) { | |
| 558 | + $requested_user = get_user_by( 'id', $requested_user_id ); | |
| 559 | + if ( $requested_user instanceof WP_User ) { | |
| 560 | + $data['requester_email'] = $requested_user->user_email; | |
| 561 | + } | |
| 562 | + } | |
| 563 | + | |
| 564 | + if ( ! empty( $overrides ) ) { | |
| 565 | + $data = array_merge( $data, $overrides ); | |
| 566 | + } | |
| 567 | + | |
| 568 | + return $data; | |
| 569 | + } | |
| 570 | +} | |
| 571 | +/** | |
| 572 | + * Render pending refund request panel on order detail. | |
| 573 | + * | |
| 574 | + * @since 4.3.4 | |
| 575 | + * @version 1.0.0 | |
| 576 | + * @param LP_Order $order | |
| 577 | + */ | |
| 578 | +function learn_press_admin_order_refund_request_panel( $order ) { | |
| 579 | + | |
| 580 | + if ( ! $order instanceof LP_Order ) { | |
| 581 | + return; | |
| 582 | + } | |
| 583 | + | |
| 584 | + $order_id = $order->get_id(); | |
| 585 | + if ( ! current_user_can( 'edit_post', $order_id ) ) { | |
| 586 | + return; | |
| 587 | + } | |
| 588 | + | |
| 589 | + $refund_event_data = learn_press_get_order_refund_event_data( $order ); | |
| 590 | + $refund_request_status = sanitize_key( (string) ( $refund_event_data['request_status'] ?? '' ) ); | |
| 591 | + $requested_by = $refund_event_data['requested_by'] ?? ''; | |
| 592 | + $requested_at = $refund_event_data['requested_at'] ?? ''; | |
| 593 | + $refund_reason = $refund_event_data['reason'] ?? ''; | |
| 594 | + $requester_email = ''; | |
| 595 | + | |
| 596 | + $status_labels = array( | |
| 597 | + 'pending' => __( 'Pending review', 'learnpress' ), | |
| 598 | + 'approved' => __( 'Approved', 'learnpress' ), | |
| 599 | + 'auto-approved' => __( 'Auto approved', 'learnpress' ), | |
| 600 | + 'rejected' => __( 'Rejected', 'learnpress' ), | |
| 601 | + ); | |
| 602 | + $status_label = $status_labels[ $refund_request_status ] ?? ''; | |
| 603 | + if ( empty( $status_label ) && ! empty( $refund_request_status ) ) { | |
| 604 | + $status_label = ucwords( str_replace( '-', ' ', $refund_request_status ) ); | |
| 605 | + } | |
| 606 | + | |
| 607 | + $render_statuses = array( 'pending', 'approved', 'auto-approved' ); | |
| 608 | + if ( ! in_array( $refund_request_status, $render_statuses, true ) ) { | |
| 609 | + return; | |
| 610 | + } | |
| 611 | + | |
| 612 | + $is_pending = ( 'pending' === $refund_request_status ); | |
| 613 | + $currency_symbol = learn_press_get_currency_symbol( $order->get_currency() ); | |
| 614 | + | |
| 615 | + $order_total = round( max( 0, floatval( $order->get_total() ) ), 2 ); | |
| 616 | + $order_total_formatted = learn_press_format_price( $order_total, $currency_symbol ); | |
| 617 | + | |
| 618 | + // Refund amount is only meaningful once the refund has been executed | |
| 619 | + // (approved/auto-approved), when _lp_refund_amount has been written. | |
| 620 | + $refund_amount_formatted = ''; | |
| 621 | + if ( ! $is_pending ) { | |
| 622 | + $refund_amount = get_post_meta( $order_id, LP_Order::META_KEY_REFUNDED_AMOUNT, true ); | |
| 623 | + if ( '' === $refund_amount || null === $refund_amount ) { | |
| 624 | + $refund_amount = $order_total; // Fallback for full refunds without a stored amount. | |
| 625 | + } | |
| 626 | + $refund_amount = round( max( 0, floatval( $refund_amount ) ), 2 ); | |
| 627 | + $refund_amount_formatted = learn_press_format_price( $refund_amount, $currency_symbol ); | |
| 628 | + } | |
| 629 | + | |
| 630 | + learn_press_admin_view( | |
| 631 | + 'meta-boxes/order/refund-request-panel', | |
| 632 | + compact( | |
| 633 | + 'order_id', | |
| 634 | + 'is_pending', | |
| 635 | + 'order_total', | |
| 636 | + 'order_total_formatted', | |
| 637 | + 'refund_amount_formatted', | |
| 638 | + 'requested_by', | |
| 639 | + 'requested_at', | |
| 640 | + 'requester_email', | |
| 641 | + 'refund_reason', | |
| 642 | + 'status_label' | |
| 643 | + ) | |
| 644 | + ); | |
| 645 | +} | |
| 646 | + | |
| 647 | +/** | |
| 648 | + * get total price order complete | |
| 649 | + */ | |
| 650 | +function learn_press_get_total_price_order_complete() { | |
| 651 | + global $wpdb; | |
| 652 | + | |
| 653 | + $query = $wpdb->prepare( | |
| 654 | + "SELECT SUM(meta_value) as order_total From `{$wpdb->prefix}postmeta` as mt | |
| 655 | + INNER JOIN `{$wpdb->prefix}posts` as p ON p.id = mt.post_id | |
| 656 | + WHERE p.post_type = %s AND mt.meta_key = %s | |
| 657 | + AND p.post_status = %s | |
| 658 | + ", | |
| 659 | + LP_ORDER_CPT, | |
| 660 | + '_order_total', | |
| 661 | + 'lp-completed' | |
| 662 | + ); | |
| 663 | + | |
| 664 | + $total = $wpdb->get_results( $query )[0]->order_total; | |
| 665 | + | |
| 666 | + return learn_press_format_price( $total, true ); | |
| 667 | +} | |