/**
* WooCommerce Stock Functions
*
* Functions used to manage product stock levels.
*
* @package WooCommerce\Functions
* @version 3.4.0
*/
defined( 'ABSPATH' ) || exit;
use Automattic\WooCommerce\Checkout\Helpers\ReserveStock;
use Automattic\WooCommerce\Enums\ProductType;
/**
* Update a product's stock amount.
*
* Uses queries rather than update_post_meta so we can do this in one query (to avoid stock issues).
*
* @since 3.0.0 this supports set, increase and decrease.
*
* @param int|WC_Product $product Product ID or product instance.
* @param int|null $stock_quantity Stock quantity.
* @param string $operation Type of operation, allows 'set', 'increase' and 'decrease'.
* @param bool $updating If true, the product object won't be saved here as it will be updated later.
* @return bool|int|null
*/
function wc_update_product_stock( $product, $stock_quantity = null, $operation = 'set', $updating = false ) {
if ( ! is_a( $product, 'WC_Product' ) ) {
$product = wc_get_product( $product );
}
if ( ! $product ) {
return false;
}
if ( ! is_null( $stock_quantity ) && $product->managing_stock() ) {
// Some products (variations) can have their stock managed by their parent. Get the correct object to be updated here.
$product_id_with_stock = $product->get_stock_managed_by_id();
$product_with_stock = $product_id_with_stock !== $product->get_id() ? wc_get_product( $product_id_with_stock ) : $product;
$data_store = WC_Data_Store::load( 'product' );
// Fire actions to let 3rd parties know the stock is about to be changed.
if ( $product_with_stock->is_type( ProductType::VARIATION ) ) {
// phpcs:disable WooCommerce.Commenting.CommentHooks.MissingSinceComment
/** This action is documented in includes/data-stores/class-wc-product-data-store-cpt.php */
do_action( 'woocommerce_variation_before_set_stock', $product_with_stock );
} else {
// phpcs:disable WooCommerce.Commenting.CommentHooks.MissingSinceComment
/** This action is documented in includes/data-stores/class-wc-product-data-store-cpt.php */
do_action( 'woocommerce_product_before_set_stock', $product_with_stock );
}
// Update the database.
$new_stock = $data_store->update_product_stock( $product_id_with_stock, $stock_quantity, $operation );
// Update the product object.
$data_store->read_stock_quantity( $product_with_stock, $new_stock );
// If this is not being called during an update routine, save the product so stock status etc is in sync, and caches are cleared.
if ( ! $updating ) {
$product_with_stock->save();
}
// Fire actions to let 3rd parties know the stock changed.
if ( $product_with_stock->is_type( ProductType::VARIATION ) ) {
// phpcs:disable WooCommerce.Commenting.CommentHooks.MissingSinceComment
/** This action is documented in includes/data-stores/class-wc-product-data-store-cpt.php */
do_action( 'woocommerce_variation_set_stock', $product_with_stock );
} else {
// phpcs:disable WooCommerce.Commenting.CommentHooks.MissingSinceComment
/** This action is documented in includes/data-stores/class-wc-product-data-store-cpt.php */
do_action( 'woocommerce_product_set_stock', $product_with_stock );
}
return $product_with_stock->get_stock_quantity();
}
return $product->get_stock_quantity();
}
/**
* Update a product's stock status.
*
* @param int $product_id Product ID.
* @param string $status Status.
*/
function wc_update_product_stock_status( $product_id, $status ) {
$product = wc_get_product( $product_id );
if ( $product ) {
$product->set_stock_status( $status );
$product->save();
}
}
/**
* When a payment is complete, we can reduce stock levels for items within an order.
*
* @since 3.0.0
* @param int $order_id Order ID.
*/
function wc_maybe_reduce_stock_levels( $order_id ) {
$order = wc_get_order( $order_id );
if ( ! $order ) {
return;
}
$stock_reduced = $order->get_data_store()->get_stock_reduced( $order_id );
$trigger_reduce = apply_filters( 'woocommerce_payment_complete_reduce_order_stock', ! $stock_reduced, $order_id );
// Only continue if we're reducing stock.
if ( ! $trigger_reduce ) {
return;
}
wc_reduce_stock_levels( $order );
// Ensure stock is marked as "reduced" in case payment complete or other stock actions are called.
$order->get_data_store()->set_stock_reduced( $order_id, true );
}
add_action( 'woocommerce_payment_complete', 'wc_maybe_reduce_stock_levels' );
add_action( 'woocommerce_order_status_completed', 'wc_maybe_reduce_stock_levels' );
add_action( 'woocommerce_order_status_processing', 'wc_maybe_reduce_stock_levels' );
add_action( 'woocommerce_order_status_on-hold', 'wc_maybe_reduce_stock_levels' );
/**
* When a payment is cancelled, restore stock.
*
* @since 3.0.0
* @param int $order_id Order ID.
*/
function wc_maybe_increase_stock_levels( $order_id ) {
$order = wc_get_order( $order_id );
if ( ! $order ) {
return;
}
$stock_reduced = $order->get_data_store()->get_stock_reduced( $order_id );
$trigger_increase = (bool) $stock_reduced;
// Only continue if we're increasing stock.
if ( ! $trigger_increase ) {
return;
}
wc_increase_stock_levels( $order );
// Ensure stock is not marked as "reduced" anymore.
$order->get_data_store()->set_stock_reduced( $order_id, false );
}
add_action( 'woocommerce_order_status_cancelled', 'wc_maybe_increase_stock_levels' );
add_action( 'woocommerce_order_status_pending', 'wc_maybe_increase_stock_levels' );
/**
* Reduce stock levels for items within an order, if stock has not already been reduced for the items.
*
* @since 3.0.0
* @param int|WC_Order $order_id Order ID or order instance.
*/
function wc_reduce_stock_levels( $order_id ) {
if ( is_a( $order_id, 'WC_Order' ) ) {
$order = $order_id;
$order_id = $order->get_id();
} else {
$order = wc_get_order( $order_id );
}
// We need an order, and a store with stock management to continue.
if ( ! $order || 'yes' !== get_option( 'woocommerce_manage_stock' ) || ! apply_filters( 'woocommerce_can_reduce_order_stock', true, $order ) ) {
return;
}
$changes = array();
// Loop over all items.
foreach ( $order->get_items() as $item ) {
if ( ! $item->is_type( 'line_item' ) ) {
continue;
}
// Only reduce stock once for each item.
$product = $item->get_product();
$item_stock_reduced = $item->get_meta( '_reduced_stock', true );
if ( $item_stock_reduced || ! $product || ! $product->managing_stock() ) {
continue;
}
/**
* Filter order item quantity.
*
* @param int|float $quantity Quantity.
* @param WC_Order $order Order data.
* @param WC_Order_Item_Product $item Order item data.
*/
$qty = apply_filters( 'woocommerce_order_item_quantity', $item->get_quantity(), $order, $item );
$item_name = $product->get_formatted_name();
$new_stock = wc_update_product_stock( $product, $qty, 'decrease' );
if ( is_wp_error( $new_stock ) ) {
/* translators: %s item name. */
$order->add_order_note( sprintf( __( 'Unable to reduce stock for item %s.', 'woocommerce' ), $item_name ) );
continue;
}
$item->add_meta_data( '_reduced_stock', $qty, true );
$item->save();
$change = array(
'product' => $product,
'from' => $new_stock + $qty,
'to' => $new_stock,
);
$changes[] = $change;
/**
* Fires when stock reduced to a specific line item
*
* @param WC_Order_Item_Product $item Order item data.
* @param array $change Change Details.
* @param WC_Order $order Order data.
* @since 7.6.0
*/
do_action( 'woocommerce_reduce_order_item_stock', $item, $change, $order );
}
wc_trigger_stock_change_notifications( $order, $changes );
do_action( 'woocommerce_reduce_order_stock', $order );
}
/**
* After stock change events, triggers emails and adds order notes.
*
* @since 3.5.0
* @param WC_Order $order order object.
* @param array $changes Array of changes.
*/
function wc_trigger_stock_change_notifications( $order, $changes ) {
if ( empty( $changes ) ) {
return;
}
$order_notes = array();
$no_stock_amount = absint( get_option( 'woocommerce_notify_no_stock_amount', 0 ) );
foreach ( $changes as $change ) {
$order_notes[] = $change['product']->get_formatted_name() . ' ' . $change['from'] . '→' . $change['to'];
$low_stock_amount = absint( wc_get_low_stock_amount( wc_get_product( $change['product']->get_id() ) ) );
if ( $change['to'] <= $no_stock_amount ) {
/**
* Action to signal that the value of 'stock_quantity' for a variation is about to change.
*
* @since 4.9
*
* @param int $product The variation whose stock is about to change.
*/
do_action( 'woocommerce_no_stock', wc_get_product( $change['product']->get_id() ) );
} elseif ( $change['to'] <= $low_stock_amount ) {
/**
* Action to signal that the value of 'stock_quantity' for a product is about to change.
*
* @since 4.9
*
* @param int $product The product whose stock is about to change.
*/
do_action( 'woocommerce_low_stock', wc_get_product( $change['product']->get_id() ) );
}
if ( $change['to'] < 0 ) {
/**
* Action fires when an item in an order is backordered.
*
* @since 3.0
*
* @param array $args {
* @type WC_Product $product The product that is on backorder.
* @type int $order_id The ID of the order.
* @type int|float $quantity The amount of product on backorder.
* }
*/
do_action(
'woocommerce_product_on_backorder',
array(
'product' => wc_get_product( $change['product']->get_id() ),
'order_id' => $order->get_id(),
'quantity' => abs( $change['from'] - $change['to'] ),
)
);
}
}
$order->add_order_note( __( 'Stock levels reduced:', 'woocommerce' ) . ' ' . implode( ', ', $order_notes ) );
}
/**
* Check if a product's stock quantity has reached certain thresholds and trigger appropriate actions.
*
* This functionality was moved out of `wc_trigger_stock_change_notifications` in order to decouple it from orders,
* since stock quantity can also be updated in other ways.
*
* @param WC_Product $product The product whose stock level has changed.
*
* @return void
*/
function wc_trigger_stock_change_actions( $product ) {
if ( true !== $product->get_manage_stock() ) {
return;
}
$no_stock_amount = absint( get_option( 'woocommerce_notify_no_stock_amount', 0 ) );
$low_stock_amount = absint( wc_get_low_stock_amount( $product ) );
$stock_quantity = $product->get_stock_quantity();
if ( $stock_quantity <= $no_stock_amount ) {
/**
* Action fires when a product's stock quantity reaches the "no stock" threshold.
*
* @since 3.0
*
* @param WC_Product $product The product whose stock quantity has changed.
*/
do_action( 'woocommerce_no_stock', $product );
} elseif ( $stock_quantity <= $low_stock_amount ) {
/**
* Action fires when a product's stock quantity reaches the "low stock" threshold.
*
* @since 3.0
*
* @param WC_Product $product The product whose stock quantity has changed.
*/
do_action( 'woocommerce_low_stock', $product );
}
}
/**
* Increase stock levels for items within an order.
*
* @since 3.0.0
* @param int|WC_Order $order_id Order ID or order instance.
*/
function wc_increase_stock_levels( $order_id ) {
if ( is_a( $order_id, 'WC_Order' ) ) {
$order = $order_id;
$order_id = $order->get_id();
} else {
$order = wc_get_order( $order_id );
}
// We need an order, and a store with stock management to continue.
if ( ! $order || 'yes' !== get_option( 'woocommerce_manage_stock' ) || ! apply_filters( 'woocommerce_can_restore_order_stock', true, $order ) ) {
return;
}
$changes = array();
// Loop over all items.
foreach ( $order->get_items() as $item ) {
if ( ! $item->is_type( 'line_item' ) ) {
continue;
}
// Only increase stock once for each item.
$product = $item->get_product();
$item_stock_reduced = $item->get_meta( '_reduced_stock', true );
if ( ! $item_stock_reduced || ! $product || ! $product->managing_stock() ) {
continue;
}
$item_name = $product->get_formatted_name();
$new_stock = wc_update_product_stock( $product, $item_stock_reduced, 'increase' );
$old_stock = $new_stock - $item_stock_reduced;
if ( is_wp_error( $new_stock ) ) {
/* translators: %s item name. */
$order->add_order_note( sprintf( __( 'Unable to restore stock for item %s.', 'woocommerce' ), $item_name ) );
continue;
}
$item->delete_meta_data( '_reduced_stock' );
$item->save();
$changes[] = $item_name . ' ' . $old_stock . '→' . $new_stock;
/**
* Fires when stock restored to a specific line item
*
* @since 9.1.0
* @param WC_Order_Item_Product $item Order item data.
* @param int $new_stock New stock.
* @param int $old_stock Old stock.
* @param WC_Order $order Order data.
*/
do_action( 'woocommerce_restore_order_item_stock', $item, $new_stock, $old_stock, $order );
}
if ( $changes ) {
$order->add_order_note( __( 'Stock levels increased:', 'woocommerce' ) . ' ' . implode( ', ', $changes ) );
}
do_action( 'woocommerce_restore_order_stock', $order );
}
/**
* See how much stock is being held in pending orders.
*
* @since 3.5.0
* @param WC_Product $product Product to check.
* @param integer $exclude_order_id Order ID to exclude.
* @return int
*/
function wc_get_held_stock_quantity( WC_Product $product, $exclude_order_id = 0 ) {
/**
* Filter: woocommerce_hold_stock_for_checkout
* Allows enable/disable hold stock functionality on checkout.
*
* @since 4.3.0
* @param bool $enabled Default to true if managing stock globally.
*/
if ( ! apply_filters( 'woocommerce_hold_stock_for_checkout', wc_string_to_bool( get_option( 'woocommerce_manage_stock', 'yes' ) ) ) ) {
return 0;
}
$reserve_stock = new ReserveStock();
return $reserve_stock->get_reserved_stock( $product, $exclude_order_id );
}
/**
* Hold stock for an order.
*
* @throws ReserveStockException If reserve stock fails.
*
* @since 4.1.0
* @param \WC_Order|int $order Order ID or instance.
*/
function wc_reserve_stock_for_order( $order ) {
/**
* Filter: woocommerce_hold_stock_for_checkout
* Allows enable/disable hold stock functionality on checkout.
*
* @since @since 4.1.0
* @param bool $enabled Default to true if managing stock globally.
*/
if ( ! apply_filters( 'woocommerce_hold_stock_for_checkout', wc_string_to_bool( get_option( 'woocommerce_manage_stock', 'yes' ) ) ) ) {
return;
}
$order = $order instanceof WC_Order ? $order : wc_get_order( $order );
if ( $order ) {
$reserve_stock = new ReserveStock();
$reserve_stock->reserve_stock_for_order( $order );
}
}
add_action( 'woocommerce_checkout_order_created', 'wc_reserve_stock_for_order' );
/**
* Release held stock for an order.
*
* @since 4.3.0
* @param \WC_Order|int $order Order ID or instance.
*/
function wc_release_stock_for_order( $order ) {
/**
* Filter: woocommerce_hold_stock_for_checkout
* Allows enable/disable hold stock functionality on checkout.
*
* @since 4.3.0
* @param bool $enabled Default to true if managing stock globally.
*/
if ( ! apply_filters( 'woocommerce_hold_stock_for_checkout', wc_string_to_bool( get_option( 'woocommerce_manage_stock', 'yes' ) ) ) ) {
return;
}
$order = $order instanceof WC_Order ? $order : wc_get_order( $order );
if ( $order ) {
$reserve_stock = new ReserveStock();
$reserve_stock->release_stock_for_order( $order );
}
}
add_action( 'woocommerce_checkout_order_exception', 'wc_release_stock_for_order' );
add_action( 'woocommerce_payment_complete', 'wc_release_stock_for_order', 11 );
add_action( 'woocommerce_order_status_cancelled', 'wc_release_stock_for_order', 11 );
add_action( 'woocommerce_order_status_completed', 'wc_release_stock_for_order', 11 );
add_action( 'woocommerce_order_status_processing', 'wc_release_stock_for_order', 11 );
add_action( 'woocommerce_order_status_on-hold', 'wc_release_stock_for_order', 11 );
/**
* Release coupons used for another order.
*
* @since 9.5.2
* @param \WC_Order|int $order Order ID or instance.
* @param bool $save Save the order after releasing coupons.
*/
function wc_release_coupons_for_order( $order, bool $save = true ) {
$order = $order instanceof WC_Order ? $order : wc_get_order( $order );
if ( $order ) {
$order->get_data_store()->release_held_coupons( $order, $save );
}
}
/**
* Return low stock amount to determine if notification needs to be sent
*
* Since 5.2.0, this function no longer redirects from variation to its parent product.
* Low stock amount can now be attached to the variation itself and if it isn't, only
* then we check the parent product, and if it's not there, then we take the default
* from the store-wide setting.
*
* @param WC_Product $product Product to get data from.
* @since 3.5.0
* @return int
*/
function wc_get_low_stock_amount( WC_Product $product ) {
$low_stock_amount = $product->get_low_stock_amount();
if ( '' === $low_stock_amount && $product->is_type( ProductType::VARIATION ) ) {
$product = wc_get_product( $product->get_parent_id() );
$low_stock_amount = $product->get_low_stock_amount();
}
if ( '' === $low_stock_amount ) {
$low_stock_amount = get_option( 'woocommerce_notify_low_stock_amount', 2 );
}
return (int) $low_stock_amount;
}
/*!
* Font Awesome Free 5.15.3 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
*/
@font-face {
font-family: 'Font Awesome 5 Brands';
font-style: normal;
font-weight: 400;
font-display: block;
src: url("../webfonts/fa-brands-400.eot");
src: url("../webfonts/fa-brands-400.eot?#iefix") format("embedded-opentype"), url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.woff") format("woff"), url("../webfonts/fa-brands-400.ttf") format("truetype"), url("../webfonts/fa-brands-400.svg#fontawesome") format("svg"); }
.fab {
font-family: 'Font Awesome 5 Brands';
font-weight: 400; }
/**
* WooCommerce Message Functions
*
* Functions for error/message handling and display.
*
* @package WooCommerce\Functions
* @version 2.1.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Get the count of notices added, either for all notices (default) or for one.
* particular notice type specified by $notice_type.
*
* @since 2.1
* @param string $notice_type Optional. The name of the notice type - either error, success or notice.
* @return int
*/
function wc_notice_count( $notice_type = '' ) {
if ( ! did_action( 'woocommerce_init' ) ) {
wc_doing_it_wrong( __FUNCTION__, __( 'This function should not be called before woocommerce_init.', 'woocommerce' ), '2.3' );
return;
}
$notice_count = 0;
$all_notices = WC()->session->get( 'wc_notices', array() );
if ( isset( $all_notices[ $notice_type ] ) && is_array( $all_notices[ $notice_type ] ) ) {
$notice_count = count( $all_notices[ $notice_type ] );
} elseif ( empty( $notice_type ) ) {
foreach ( $all_notices as $notices ) {
if ( is_countable( $notices ) ) {
$notice_count += count( $notices );
}
}
}
return $notice_count;
}
/**
* Check if a notice has already been added.
*
* @since 2.1
* @param string $message The text to display in the notice.
* @param string $notice_type Optional. The name of the notice type - either error, success or notice.
* @return bool
*/
function wc_has_notice( $message, $notice_type = 'success' ) {
if ( ! did_action( 'woocommerce_init' ) ) {
wc_doing_it_wrong( __FUNCTION__, __( 'This function should not be called before woocommerce_init.', 'woocommerce' ), '2.3' );
return false;
}
$notices = WC()->session->get( 'wc_notices', array() );
$notices = isset( $notices[ $notice_type ] ) ? $notices[ $notice_type ] : array();
return array_search( $message, wp_list_pluck( $notices, 'notice' ), true ) !== false;
}
/**
* Add and store a notice.
*
* @since 2.1
* @version 3.9.0
* @param string $message The text to display in the notice.
* @param string $notice_type Optional. The name of the notice type - either error, success or notice.
* @param array $data Optional notice data.
*/
function wc_add_notice( $message, $notice_type = 'success', $data = array() ) {
if ( ! did_action( 'woocommerce_init' ) ) {
wc_doing_it_wrong( __FUNCTION__, __( 'This function should not be called before woocommerce_init.', 'woocommerce' ), '2.3' );
return;
}
$notices = WC()->session->get( 'wc_notices', array() );
// Backward compatibility.
if ( 'success' === $notice_type ) {
$message = apply_filters( 'woocommerce_add_message', $message );
}
$message = apply_filters( 'woocommerce_add_' . $notice_type, $message );
if ( ! empty( $message ) ) {
$notices[ $notice_type ][] = array(
'notice' => $message,
'data' => $data,
);
}
WC()->session->set( 'wc_notices', $notices );
}
/**
* Set all notices at once.
*
* @since 2.6.0
* @param array[] $notices Array of notices.
*/
function wc_set_notices( $notices ) {
if ( ! did_action( 'woocommerce_init' ) ) {
wc_doing_it_wrong( __FUNCTION__, __( 'This function should not be called before woocommerce_init.', 'woocommerce' ), '2.6' );
return;
}
WC()->session->set( 'wc_notices', empty( $notices ) ? null : $notices );
}
/**
* Unset all notices.
*
* @since 2.1
*/
function wc_clear_notices() {
if ( ! did_action( 'woocommerce_init' ) ) {
wc_doing_it_wrong( __FUNCTION__, __( 'This function should not be called before woocommerce_init.', 'woocommerce' ), '2.3' );
return;
}
WC()->session->set( 'wc_notices', null );
}
/**
* Prints messages and errors which are stored in the session, then clears them.
*
* @since 2.1
* @param bool $return true to return rather than echo. @since 3.5.0.
* @return string|void
*/
function wc_print_notices( $return = false ) {
if ( ! did_action( 'woocommerce_init' ) ) {
wc_doing_it_wrong( __FUNCTION__, __( 'This function should not be called before woocommerce_init.', 'woocommerce' ), '2.3' );
return;
}
$session = WC()->session;
// If the session handler has not initialized, there will be no notices for us to read.
if ( null === $session ) {
return;
}
$all_notices = $session->get( 'wc_notices', array() );
$notice_types = apply_filters( 'woocommerce_notice_types', array( 'error', 'success', 'notice' ) );
// Buffer output.
ob_start();
foreach ( $notice_types as $notice_type ) {
if ( wc_notice_count( $notice_type ) > 0 ) {
$messages = array();
foreach ( $all_notices[ $notice_type ] as $notice ) {
$messages[] = isset( $notice['notice'] ) ? $notice['notice'] : $notice;
}
wc_get_template(
"notices/{$notice_type}.php",
array(
'messages' => array_filter( $messages ), // @deprecated 3.9.0
'notices' => array_filter( $all_notices[ $notice_type ] ),
)
);
}
}
wc_clear_notices();
$notices = wc_kses_notice( ob_get_clean() );
if ( $return ) {
return $notices;
}
echo $notices; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* Print a single notice immediately.
*
* @since 2.1
* @version 3.9.0
* @param string $message The text to display in the notice.
* @param string $notice_type Optional. The singular name of the notice type - either error, success or notice.
* @param array $data Optional notice data. @since 3.9.0.
* @param bool $return true to return rather than echo. @since 7.7.0.
*/
function wc_print_notice( $message, $notice_type = 'success', $data = array(), $return = false ) {
if ( 'success' === $notice_type ) {
$message = apply_filters( 'woocommerce_add_message', $message );
}
$message = apply_filters( 'woocommerce_add_' . $notice_type, $message );
// Buffer output.
ob_start();
wc_get_template(
"notices/{$notice_type}.php",
array(
'messages' => array( $message ), // @deprecated 3.9.0
'notices' => array(
array(
'notice' => $message,
'data' => $data,
),
),
)
);
$notice = wc_kses_notice( ob_get_clean() );
if ( $return ) {
return $notice;
}
echo $notice; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
* Returns all queued notices, optionally filtered by a notice type.
*
* @since 2.1
* @version 3.9.0
* @param string $notice_type Optional. The singular name of the notice type - either error, success or notice.
* @return array[]
*/
function wc_get_notices( $notice_type = '' ) {
if ( ! did_action( 'woocommerce_init' ) ) {
wc_doing_it_wrong( __FUNCTION__, __( 'This function should not be called before woocommerce_init.', 'woocommerce' ), '2.3' );
return;
}
$notices = array();
if ( ! WC()->session ) {
return $notices;
}
$all_notices = WC()->session->get( 'wc_notices', array() );
if ( empty( $notice_type ) ) {
$notices = $all_notices;
} elseif ( isset( $all_notices[ $notice_type ] ) ) {
$notices = $all_notices[ $notice_type ];
}
return $notices;
}
/**
* Add notices for WP Errors.
*
* @param WP_Error $errors Errors.
*/
function wc_add_wp_error_notices( $errors ) {
if ( is_wp_error( $errors ) && $errors->get_error_messages() ) {
foreach ( $errors->get_error_messages() as $error ) {
wc_add_notice( $error, 'error' );
}
}
}
/**
* Filters out the same tags as wp_kses_post, but allows tabindex for element.
*
* @since 3.5.0
* @param string $message Content to filter through kses.
* @return string
*/
function wc_kses_notice( $message ) {
$allowed_tags = array_replace_recursive(
wp_kses_allowed_html( 'post' ),
array(
'a' => array(
'tabindex' => true,
),
)
);
/**
* Kses notice allowed tags.
*
* @since 3.9.0
* @param array[]|string $allowed_tags An array of allowed HTML elements and attributes, or a context name such as 'post'.
*/
return wp_kses( $message, apply_filters( 'woocommerce_kses_notice_allowed_tags', $allowed_tags ) );
}
/**
* Get notice data attribute.
*
* @since 3.9.0
* @param array $notice Notice data.
* @return string
*/
function wc_get_notice_data_attr( $notice ) {
if ( empty( $notice['data'] ) ) {
return;
}
$attr = '';
foreach ( $notice['data'] as $key => $value ) {
$attr .= sprintf(
' data-%1$s="%2$s"',
sanitize_title( $key ),
esc_attr( $value )
);
}
return $attr;
}
/**
* Brands Helper Functions
*
* Important: For internal use only by the Automattic\WooCommerce\Internal\Brands package.
*
* @package WooCommerce
* @version 9.4.0
*/
declare( strict_types = 1);
/**
* Helper function :: wc_get_brand_thumbnail_url function.
*
* @param int $brand_id Brand ID.
* @param string $size Thumbnail image size.
* @return string
*/
function wc_get_brand_thumbnail_url( $brand_id, $size = 'full' ) {
$thumbnail_id = get_term_meta( $brand_id, 'thumbnail_id', true );
if ( $thumbnail_id ) {
$thumb_src = wp_get_attachment_image_src( $thumbnail_id, $size );
}
return ! empty( $thumb_src ) ? current( $thumb_src ) : '';
}
/**
* Helper function :: wc_get_brand_thumbnail_image function.
*
* @since 9.4.0
*
* @param object $brand Brand term.
* @param string $size Thumbnail image size.
* @return string
*/
function wc_get_brand_thumbnail_image( $brand, $size = '' ) {
$thumbnail_id = get_term_meta( $brand->term_id, 'thumbnail_id', true );
if ( '' === $size || 'brand-thumb' === $size ) {
/**
* Filter the brand's thumbnail size.
*
* @since 9.4.0
*
* @param string $size Brand's thumbnail size.
*/
$size = apply_filters( 'woocommerce_brand_thumbnail_size', 'shop_catalog' );
}
if ( $thumbnail_id ) {
$image_src = wp_get_attachment_image_src( $thumbnail_id, $size );
$image_src = $image_src[0];
$dimensions = wc_get_image_size( $size );
$image_srcset = function_exists( 'wp_get_attachment_image_srcset' ) ? wp_get_attachment_image_srcset( $thumbnail_id, $size ) : false;
$image_sizes = function_exists( 'wp_get_attachment_image_sizes' ) ? wp_get_attachment_image_sizes( $thumbnail_id, $size ) : false;
} else {
$image_src = wc_placeholder_img_src();
$dimensions = wc_get_image_size( $size );
$image_srcset = false;
$image_sizes = false;
}
// Add responsive image markup if available.
if ( $image_srcset && $image_sizes ) {
$image = ' ';
} else {
$image = ' ';
}
return $image;
}
/**
* Retrieves product's brands.
*
* @param int $post_id Post ID (default: 0).
* @param string $sep Seperator (default: ').
* @param string $before Before item (default: '').
* @param string $after After item (default: '').
* @return array List of terms
*/
function wc_get_brands( $post_id = 0, $sep = ', ', $before = '', $after = '' ) {
global $post;
if ( ! $post_id ) {
$post_id = $post->ID;
}
return get_the_term_list( $post_id, 'product_brand', $before, $sep, $after );
}
/**
* Polyfills for backwards compatibility with the WooCommerce Brands plugin.
*/
if ( ! function_exists( 'get_brand_thumbnail_url' ) ) {
/**
* Polyfill for get_brand_thumbnail_image.
*
* @param int $brand_id Brand ID.
* @param string $size Thumbnail image size.
* @return string
*/
function get_brand_thumbnail_url( $brand_id, $size = 'full' ) {
return wc_get_brand_thumbnail_url( $brand_id, $size );
}
}
if ( ! function_exists( 'get_brand_thumbnail_image' ) ) {
/**
* Polyfill for get_brand_thumbnail_image.
*
* @param object $brand Brand term.
* @param string $size Thumbnail image size.
* @return string
*/
function get_brand_thumbnail_image( $brand, $size = '' ) {
return wc_get_brand_thumbnail_image( $brand, $size );
}
}
if ( ! function_exists( 'get_brands' ) ) {
/**
* Polyfill for get_brands.
*
* @param int $post_id Post ID (default: 0).
* @param string $sep Seperator (default: ').
* @param string $before Before item (default: '').
* @param string $after After item (default: '').
* @return array List of terms
*/
function get_brands( $post_id = 0, $sep = ', ', $before = '', $after = '' ) {
return wc_get_brands( $post_id, $sep, $before, $after );
}
}
/**
* Core Functions
*
* Holds core functions for wc-admin.
*
* @package WooCommerce\Admin\Functions
*/
use Automattic\WooCommerce\Internal\Admin\Settings;
/**
* Format a number using the decimal and thousands separator settings in WooCommerce.
*
* @param mixed $number Number to be formatted.
* @return string
*/
function wc_admin_number_format( $number ) {
$currency_settings = Settings::get_currency_settings();
return number_format(
$number,
0,
$currency_settings['decimalSeparator'],
$currency_settings['thousandSeparator']
);
}
/**
* Retrieves a URL to relative path inside WooCommerce admin with
* the provided query parameters.
*
* @param string $path Relative path of the desired page.
* @param array $query Query parameters to append to the path.
*
* @return string Fully qualified URL pointing to the desired path.
*/
function wc_admin_url( $path = null, $query = array() ) {
if ( ! empty( $query ) ) {
$query_string = http_build_query( $query );
$path = $path ? '&path=' . $path . '&' . $query_string : '';
}
return admin_url( 'admin.php?page=wc-admin' . $path, dirname( __FILE__ ) );
}
/**
* Record an event using Tracks.
*
* @internal WooCommerce core only includes Tracks in admin, not the REST API, so we need to include it.
* @param string $event_name Event name for tracks.
* @param array $properties Properties to pass along with event.
*/
function wc_admin_record_tracks_event( $event_name, $properties = array() ) {
// WC post types must be registered first for WC_Tracks to work.
if ( ! post_type_exists( 'product' ) ) {
return;
}
if ( ! class_exists( 'WC_Tracks' ) ) {
if ( ! defined( 'WC_ABSPATH' ) || ! file_exists( WC_ABSPATH . 'includes/tracks/class-wc-tracks.php' ) ) {
return;
}
include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks.php';
include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks-event.php';
include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks-client.php';
include_once WC_ABSPATH . 'includes/tracks/class-wc-tracks-footer-pixel.php';
include_once WC_ABSPATH . 'includes/tracks/class-wc-site-tracking.php';
}
WC_Tracks::record_event( $event_name, $properties );
}
Order Flow в трейдинге SM Trader - Nước khoáng
Skip to content
Order Flow в трейдинге SM Trader Home - Forex News - Order Flow в трейдинге SM Trader
Ордер блок OB SM Trader
Необходимо, чтобы паттерн располагался на линии поддержки/сопротивления в зависимости от направления тренда (бычий/медвежий) и возможного изменения цены криптовалюты и других активов. Приоритет при медвежьем Order Flow нисходящее движение цены – то есть шорт позиции. Чтобы нам открыть сделку нужно дождаться, когда обновится ликвидность на покупку и цена протестирует зону интереса. На графике это зона, где происходит слом структуры, что может указывать на переход от восходящего тренда к нисходящему или наоборот. Этот паттерн может использоваться в торговых стратегиях для выявления потенциальных точек входа на рынок.
Вопросы, которые могут возникать при изучении ордер блоков
Направление рынка в трейдинге во многом зависит от действий крупных игроков, в частности банков и хедж-фондов. Именно их торговые операции могут создавать так называемые ордер-блоки — области на графике, где совершаются большие покупки или продажи. Эти зоны являются индикаторами потенциальных уровней поддержки или сопротивления, где вероятно продолжение или разворот тренда. Рассмотрим, что такое ордер-блок, как он формируется, какие его виды существуют и как эффективно использовать их в торговле.
Чтобы подтвердить медвежий брейкер, нужно дождаться изменения направления движения цены и импульсного пробития ордер блока. Для того чтобы быть успешным трейдером, мы должны учитывать, где находятся зоны ликвидности и где расположены блоки ордеров, служащие поддержкой или сопротивлением в тренде. Это позволяет размещать сделки в зонах, которые статистически обладают наибольшей ценовой реакцией. Ниже мы объясним, как торговать бычьим блоком ордеров (зоной спроса) по сравнению с медвежьим блоком ордеров (зоной предложения) с планом входа, установкой стоп-лосса и целями прибыли.
Вот те вещи, которые практически никогда не дадут вам «слить» свой депозит. Хотя, я думаю, эти индикаторы работают не во всех случаях(особенно в случаях кризисов и войн), но всё же рабочих случаев куда больше. CFD — это сложные инструменты, которые связаны с высоким риском быстрой потери средств из-за левереджа. 82.78% аккаунтов розничных инвесторов теряют деньги при торговле CFD с этим брокером. Убедитесь, что вы понимаете, как работают CFD, и можете позволить себе высокий риск потери средств.
Какие бывают ордер блоки?
В данной статье рассмотрим такой инструмент, как Брейкер блок, неотъемлемая часть в концепции Смарт-Мани.
Треугольник – если блок отработал в верном направлении, крестик – блок не отработал и цена продолжила движение.
То есть, крупные игроки рынка открывают противоположные друг другу ордеры (на продажу и на покупку).
На графике ордер-блок выглядит как диапазон, в котором цена разворачивается под давлением институциональных инвесторов, затем возвращается в зону и выходит из нее, меняя свое направление.
Идеальным ордер блок будет тогда, когда цена при его тестировании не опустится ниже 50% диапазона блока, т.к. Чем ниже опустится цена, тем выше вероятность того, что данный блок не устоит и будет пробит. Она заключается в крупных объемах, находящихся в твоем распоряжении. Ведь если ты войдешь ордер блок это в позицию лонг по маркету, твой крупный объем заявки на покупку станет стимулом, толкающим цену вверх – это не выгодно. Поэтому тебе нужен нисходящий тренд (ликвидность от продавцов), который ты “встретишь” лимитными ордерами (Блоками ордеров) на покупку. Это увеличивает шансы на открытие большой позиции лонг вблизи минимума цены.
Блоки ордеров – это мощные инструменты для определения ключевых ценовых зон, в которые крупные трейдеры вошли на рынок. Независимо от того, предпочитаете ли вы агрессивные или консервативные входы, важно оставаться последовательным в своем подходе и защищать свой капитал. Медвежьи ордер блокиформируются, когда на определенном ценовом уровне крупные ордеры на продажу пересиливают натиск покупателей. Выявление медвежьих ордер блоков поможет вам предсказывать медвежьи тренды и определить возможности для открытия коротких позиций.
В отличии от вариантов блоков выше, данный блок может образоваться как в начале тренда, так и в его середине или конце. Однако помните, что гепы в конце тренда обычно происходят на истощение и символизируют о завершении тренда. На максимуме она может быть не только бычьей, но и медвежьей, а на минимуме она может быть не только медвежьей, но и бычьей. Ищите самую высокую свечу, с самым высоким открытием или закрытием и с самым высоким фитилём (тоже самое и в обратную сторону). ✘ Поскольку для идентификации и торговли Ордер блоков используются свечные формации, в анализ могут вноситься нюансы, связанные с таймфреймами и положением цен OHLC. ✘ Для эффективной торговли Блоков ордеров крайне желателен опыт и навыки оценки рыночного контекста с целью понимания намерения представителей Smart Money.
Это происходит даже без явного намерения воздействовать на котировки. Важно отметить, что ценовое давление создается не только реальными транзакциями крупных участников, но и их отложенными ордерами в биржевом стакане заявок. Ордер блоки могут дать представление о том, где крупные игроки ранее входили в рынок или выходили из него.
Однако учитывайте, что валидный блок тогда, когда он протестировал предыдущий ордер блок. Еще со времён австралопитеков известно, что при движении цены по тренду открывать сделки рекомендуется в направлении этого тренда. ✔ Медвежий ордер блок строится от восходящей свечи и предполагает разворот вниз в случае последующего теста области.
И торгуем в его сторону (оба ордер блока бычьи, илимедвежьи – в одном направлении). Ордер https://forexby.com/ блоки в трейдинге помогают трейдерам видеть скрытые уровни поддержки и сопротивления, а также предугадывать возможные движения цены. Использование ордеров блоков позволяет трейдерам принимать более тактичные решения и повышает шансы на успешную торговлю. Смарт Мани трейдеры, которые учитывают потоки ордеров, могут определить, в каком направлении движется рынок, и вступать в сделки в направлении актуального потока приказов.