This snippet will exclude the on sale products from the WooCommerce loop
function custom_exclude_on_sale_products_from_query( $q ){
if ( $q->is_search() ) return;
if ( ! $q->is_main_query() ) return;
if ( ! $q->is_post_type_archive() ) return;
if ( ! is_admin() ) {
$product_ids_on_sale = wc_get_product_ids_on_sale();
$q->set( 'post__not_in', (array) $product_ids_on_sale );
}
}
add_action( 'woocommerce_product_query', 'custom_exclude_on_sale_products_from_query' );