Server IP : 103.119.228.120 / Your IP : 3.133.117.113 Web Server : Apache System : Linux v8.techscape8.com 3.10.0-1160.119.1.el7.tuxcare.els2.x86_64 #1 SMP Mon Jul 15 12:09:18 UTC 2024 x86_64 User : nobody ( 99) PHP Version : 5.6.40 Disable Function : shell_exec,symlink,system,exec,proc_get_status,proc_nice,proc_terminate,define_syslog_variables,syslog,openlog,closelog,escapeshellcmd,passthru,ocinum cols,ini_alter,leak,listen,chgrp,apache_note,apache_setenv,debugger_on,debugger_off,ftp_exec,dl,dll,myshellexec,proc_open,socket_bind,proc_close,escapeshellarg,parse_ini_filepopen,fpassthru,exec,passthru,escapeshellarg,escapeshellcmd,proc_close,proc_open,ini_alter,popen,show_source,proc_nice,proc_terminate,proc_get_status,proc_close,pfsockopen,leak,apache_child_terminate,posix_kill,posix_mkfifo,posix_setpgid,posix_setsid,posix_setuid,dl,symlink,shell_exec,system,dl,passthru,escapeshellarg,escapeshellcmd,myshellexec,c99_buff_prepare,c99_sess_put,fpassthru,getdisfunc,fx29exec,fx29exec2,is_windows,disp_freespace,fx29sh_getupdate,fx29_buff_prepare,fx29_sess_put,fx29shexit,fx29fsearch,fx29ftpbrutecheck,fx29sh_tools,fx29sh_about,milw0rm,imagez,sh_name,myshellexec,checkproxyhost,dosyayicek,c99_buff_prepare,c99_sess_put,c99getsource,c99sh_getupdate,c99fsearch,c99shexit,view_perms,posix_getpwuid,posix_getgrgid,posix_kill,parse_perms,parsesort,view_perms_color,set_encoder_input,ls_setcheckboxall,ls_reverse_all,rsg_read,rsg_glob,selfURL,dispsecinfo,unix2DosTime,addFile,system,get_users,view_size,DirFiles,DirFilesWide,DirPrintHTMLHeaders,GetFilesTotal,GetTitles,GetTimeTotal,GetMatchesCount,GetFileMatchesCount,GetResultFiles,fs_copy_dir,fs_copy_obj,fs_move_dir,fs_move_obj,fs_rmdir,SearchText,getmicrotime MySQL : ON | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /var/softaculous/sitepad/editor/site-data/plugins/kkart-pro/src/Checkout/Helpers/ |
Upload File : |
<?php /** * Handle product stock reservation during checkout. */ namespace Automattic\Kkart\Checkout\Helpers; defined( 'ABSPATH' ) || exit; /** * Stock Reservation class. */ final class ReserveStock { /** * Is stock reservation enabled? * * @var boolean */ private $enabled = true; /** * Constructor */ public function __construct() { // Table needed for this feature are added in 4.3. $this->enabled = get_option( 'kkart_schema_version', 0 ) >= 430; } /** * Is stock reservation enabled? * * @return boolean */ protected function is_enabled() { return $this->enabled; } /** * Query for any existing holds on stock for this item. * * @param \KKART_Product $product Product to get reserved stock for. * @param integer $exclude_order_id Optional order to exclude from the results. * * @return integer Amount of stock already reserved. */ public function get_reserved_stock( $product, $exclude_order_id = 0 ) { global $wpdb; if ( ! $this->is_enabled() ) { return 0; } // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared return (int) $wpdb->get_var( $this->get_query_for_reserved_stock( $product->get_stock_managed_by_id(), $exclude_order_id ) ); } /** * Put a temporary hold on stock for an order if enough is available. * * @throws ReserveStockException If stock cannot be reserved. * * @param \KKART_Order $order Order object. * @param int $minutes How long to reserve stock in minutes. Defaults to kkart_hold_stock_minutes. */ public function reserve_stock_for_order( $order, $minutes = 0 ) { $minutes = $minutes ? $minutes : (int) get_option( 'kkart_hold_stock_minutes', 60 ); if ( ! $minutes || ! $this->is_enabled() ) { return; } try { $items = array_filter( $order->get_items(), function( $item ) { return $item->is_type( 'line_item' ) && $item->get_product() instanceof \KKART_Product && $item->get_quantity() > 0; } ); $rows = array(); foreach ( $items as $item ) { $product = $item->get_product(); if ( ! $product->is_in_stock() ) { throw new ReserveStockException( 'kkart_product_out_of_stock', sprintf( /* translators: %s: product name */ __( '"%s" is out of stock and cannot be purchased.', 'kkart' ), $product->get_name() ), 403 ); } // If stock management is off, no need to reserve any stock here. if ( ! $product->managing_stock() || $product->backorders_allowed() ) { continue; } $managed_by_id = $product->get_stock_managed_by_id(); /** * Filter order item quantity. * * @param int|float $quantity Quantity. * @param KKART_Order $order Order data. * @param KKART_Order_Item_Product $item Order item data. */ $item_quantity = apply_filters( 'kkart_order_item_quantity', $item->get_quantity(), $order, $item ); $rows[ $managed_by_id ] = isset( $rows[ $managed_by_id ] ) ? $rows[ $managed_by_id ] + $item_quantity : $item_quantity; } if ( ! empty( $rows ) ) { foreach ( $rows as $product_id => $quantity ) { $this->reserve_stock_for_product( $product_id, $quantity, $order, $minutes ); } } } catch ( ReserveStockException $e ) { $this->release_stock_for_order( $order ); throw $e; } } /** * Release a temporary hold on stock for an order. * * @param \KKART_Order $order Order object. */ public function release_stock_for_order( $order ) { global $wpdb; if ( ! $this->is_enabled() ) { return; } $wpdb->delete( $wpdb->kkart_reserved_stock, array( 'order_id' => $order->get_id(), ) ); } /** * Reserve stock for a product by inserting rows into the DB. * * @throws ReserveStockException If a row cannot be inserted. * * @param int $product_id Product ID which is having stock reserved. * @param int $stock_quantity Stock amount to reserve. * @param \KKART_Order $order Order object which contains the product. * @param int $minutes How long to reserve stock in minutes. */ private function reserve_stock_for_product( $product_id, $stock_quantity, $order, $minutes ) { global $wpdb; $product_data_store = \KKART_Data_Store::load( 'product' ); $query_for_stock = $product_data_store->get_query_for_stock( $product_id ); $query_for_reserved_stock = $this->get_query_for_reserved_stock( $product_id, $order->get_id() ); // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared $result = $wpdb->query( $wpdb->prepare( " INSERT INTO {$wpdb->kkart_reserved_stock} ( `order_id`, `product_id`, `stock_quantity`, `timestamp`, `expires` ) SELECT %d, %d, %d, NOW(), ( NOW() + INTERVAL %d MINUTE ) FROM DUAL WHERE ( $query_for_stock FOR UPDATE ) - ( $query_for_reserved_stock FOR UPDATE ) >= %d ON DUPLICATE KEY UPDATE `expires` = VALUES( `expires` ), `stock_quantity` = VALUES( `stock_quantity` ) ", $order->get_id(), $product_id, $stock_quantity, $minutes, $stock_quantity ) ); // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared if ( ! $result ) { $product = kkart_get_product( $product_id ); throw new ReserveStockException( 'kkart_product_not_enough_stock', sprintf( /* translators: %s: product name */ __( 'Not enough units of %s are available in stock to fulfil this order.', 'kkart' ), $product ? $product->get_name() : '#' . $product_id ), 403 ); } } /** * Returns query statement for getting reserved stock of a product. * * @param int $product_id Product ID. * @param integer $exclude_order_id Optional order to exclude from the results. * @return string|void Query statement. */ private function get_query_for_reserved_stock( $product_id, $exclude_order_id = 0 ) { global $wpdb; $query = $wpdb->prepare( " SELECT COALESCE( SUM( stock_table.`stock_quantity` ), 0 ) FROM $wpdb->kkart_reserved_stock stock_table LEFT JOIN $wpdb->posts posts ON stock_table.`order_id` = posts.ID WHERE posts.post_status IN ( 'kkart-checkout-draft', 'kkart-pending' ) AND stock_table.`expires` > NOW() AND stock_table.`product_id` = %d AND stock_table.`order_id` != %d ", $product_id, $exclude_order_id ); /** * Filter: kkart_query_for_reserved_stock * Allows to filter the query for getting reserved stock of a product. * * @since 4.5.0 * @param string $query The query for getting reserved stock of a product. * @param int $product_id Product ID. * @param int $exclude_order_id Order to exclude from the results. */ return apply_filters( 'kkart_query_for_reserved_stock', $query, $product_id, $exclude_order_id ); } }