Skip to content

Instantly share code, notes, and snippets.

@damiencarbery
Last active December 10, 2021 14:03
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save damiencarbery/5be43650ec558a9fa9fab48ddcda667e to your computer and use it in GitHub Desktop.
Save damiencarbery/5be43650ec558a9fa9fab48ddcda667e to your computer and use it in GitHub Desktop.
Edit stock levels in WooCommerce - Add editing capability to the List stock levels in WooCommerce plugin - https://www.damiencarbery.com/2019/10/edit-stock-levels-in-woocommerce
table.wp-list-table tr.updated { animation: highlight 3s ease 2; }
@keyframes highlight {
0% { background-color: inherit; }
50% { background-color: #d1fa88; }
100% { background-color: inherit; }
}
<?php
$stock_level_form = sprintf( '<form method="post" action="">'.
'<label for="new-stock-count"><input type="number" name="new_stock_count" value="%d" /></label>'.
'<input type="hidden" name="old_stock_count" value="%d" />'.
'<input type="hidden" name="product_id" value="%d" />'.
'<input type="submit" value="Update" data-product-id="%d" />%s</form>',
$stock_level, $stock_level, $product->get_id(), $product->get_id(), $nonce_field );
// $Id: woocommerce-stock-info.js 4872 2019-10-27 18:14:20Z damien $
jQuery(document).ready( function( $ ){
jQuery( 'input[type=submit]' ).on( 'click', function(e) { // Click on a Submit button.
e.preventDefault();
product_id = $(this).data( 'product-id' );
jQuery.ajax({
url : ajax_js_stock_level.ajax_url,
type : 'post',
data : {
action : 'dcwd_ajax_stock_level', // Note that this is part of the add_action() call.
wc_stock_info_name : ajax_js_stock_level.the_nonce,
product_id : product_id,
new_stock_count : $( '#product-' + product_id + ' input[name=new_stock_count]' ).val(),
old_stock_count : $( '#product-' + product_id + ' input[name=old_stock_count]' ).val(),
},
success : function( response ) {
alert( response.data.message );
$( '#product-' + response.data.product_id ).addClass( 'updated' );
},
error : function( response ) {
alert('Error updating stock level: ' + response.status + ' ' + response.statusText);
console.log( response );
}
});
});
});
<?php
/*
Plugin Name: WooCommerce Stock Info
Plugin URI: https://www.damiencarbery.com/2019/10/list-stock-levels-in-woocommerce/
Description: List the stock level for each product and variation. The level can be changed for items with "Manage Stock" enabled.
Author: Damien Carbery
Author URI: https://www.damiencarbery.com
Version: $Revision: 4877 $
Tested up to: 5.2.4
Requires PHP: 5.6
WC requires at least: 3.0.0
WC tested up to: 3.7.1
$Id: woocommerce-stock-info.php 4877 2019-11-01 13:11:37Z damien $
*/
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
// Load ajax script and add the nonce.
add_action( 'admin_enqueue_scripts', 'dcwd_ajax_enqueue_script' );
function dcwd_ajax_enqueue_script() {
$current_screen = get_current_screen();
if ( 'woocommerce_page_dcwd-woocommerce-stock-info' == $current_screen->id ) {
wp_enqueue_script( 'dcwd_stock_info', plugin_dir_url( __FILE__ ). 'woocommerce-stock-info.js', array('jquery') );
wp_localize_script( 'dcwd_stock_info', 'ajax_js_stock_level', array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'the_nonce' => wp_create_nonce('ajax_stock_info_name')
));
}
}
// Exclude the nopriv version as this code should only be run by logged in users.
// The add_submenu_page() call requires 'manage_options' and the function below
// checks that the user can 'manage_options' too.
add_action( 'wp_ajax_dcwd_ajax_stock_level', 'update_wc_stock_level' ); // For logged in users.
// Update stock level if a form or ajax request was submitted.
// wp_doing_ajax() is used to distinguish between an ajax and form submission.
function update_wc_stock_level() {
if ( wp_doing_ajax() ) {
// This function will die if submitted_nonce is not correct.
check_ajax_referer( 'ajax_stock_info_name', 'wc_stock_info_name' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error();
wp_die();
}
}
else {
if ( !isset( $_POST['wc_stock_info_name'] ) || !wp_verify_nonce( $_POST['wc_stock_info_name'], 'wc_stock_info_update' ) ) {
return null;
}
if ( ! current_user_can( 'manage_options' ) ) {
return null;
}
}
// Verify that 'product_id' is an int.
if ( isset( $_POST[ 'product_id' ] ) && (int) $_POST[ 'product_id' ] ) {
// wc_get_product() will ensure that 'product_id' corresponds to a valid product or variation.
$product_to_update = wc_get_product( (int) $_POST[ 'product_id' ] );
if ( ( 'simple' == $product_to_update->get_type() ) || ( 'variable' == $product_to_update->get_type() ) ) {
// Ensure 'new_stock_count' is a positive int.
if ( isset( $_POST[ 'new_stock_count' ] ) && (int) $_POST[ 'new_stock_count' ] && 0 < (int) $_POST[ 'new_stock_count' ] ) {
$product_to_update->set_stock_quantity( wc_stock_amount( (int) $_POST[ 'new_stock_count' ] ) );
$product_to_update->save();
$product_updated = $product_to_update->get_id();
if ( wp_doing_ajax() ) {
$response = array( 'message' => 'Stock level for ' . $product_to_update->get_name() . ' increased to ' . $_POST[ 'new_stock_count' ] . '.', 'product_id' => $product_to_update->get_id() );
wp_send_json_success( $response );
wp_die();
}
else {
return $product_updated;
}
}
}
}
if ( wp_doing_ajax() ) {
wp_send_json_error();
wp_die();
}
else {
return null;
}
}
add_action( 'admin_enqueue_scripts', 'wsi_include_wc_admin_css', 20 );
function wsi_include_wc_admin_css() {
wp_enqueue_style( 'woocommerce_admin_styles' );
}
add_action('admin_menu', 'wsi_add_menu_page', 100 );
function wsi_add_menu_page() {
add_submenu_page( 'woocommerce', 'WooCommerce Stock Info', 'Stock Info', 'manage_options', 'dcwd-woocommerce-stock-info', 'wsi_stock_info_page' );
}
function wsi_stock_info_page() {
?>
<div class="wrap">
<h1>Stock Information</h1>
<style>
table.wp-list-table mark.instock input, table.wp-list-table mark.onbackorder input, table.wp-list-table mark.outofstock input { font-weight: initial; }
table.wp-list-table input[type="number"] { width: 5em; }
/*table.wp-list-table tr { transition-property: background-color; transition-duration: 1s; }*/
table.wp-list-table tr.updated { animation: highlight 3s ease 2; }
@keyframes highlight {
0% { background-color: inherit; }
50% { background-color: #d1fa88; }
100% { background-color: inherit; }
}
</style>
<?php
$product_updated = update_wc_stock_level();
$args = array( 'limit' => -1, 'orderby' => 'name', 'order' => 'ASC', 'status' => 'publish' );
$all_products = wc_get_products( $args );
$nonce_field = wp_nonce_field( 'wc_stock_info_update', 'wc_stock_info_name', true, false );
echo '<table class="wp-list-table widefat fixed striped posts">';
echo '<thead><tr><td class="manage-column">Product name</td><td class="manage-column">Variation name</td><td class="manage-column">Stock level</td><td class="manage-column">In stock?</td></tr></thead>';
foreach ( $all_products as $product ) {
if ( 'variable' == $product->get_type() ) {
echo '<tr><td colspan="4"><strong>', $product->get_name(), ' (Variable product)</strong></td></tr>';
$variations = $product->get_available_variations();
foreach ( $variations as $variation_obj ) {
$variation = wc_get_product( $variation_obj['variation_id'] );
$stock_level = wsi_stock_level_message( $variation, $nonce_field );
echo wsi_stock_level_table_row( $variation->get_attribute_summary(), $variation->get_id(), $stock_level, $variation->get_stock_status(), $product_updated );
}
}
elseif ( 'simple' == $product->get_type() ) {
echo '<tr><td colspan="4"><strong>', $product->get_name(), ' (Simple product)</strong></td></tr>';
$stock_level = wsi_stock_level_message( $product, $nonce_field );
echo wsi_stock_level_table_row( $product->get_name(), $product->get_id(), $stock_level, $product->get_stock_status(), $product_updated );
}
else {
// Skip over products that are not variable or simple.
}
}
echo '</table>';
?>
</div>
<?php
}
// Generate the stock level message.
function wsi_stock_level_message( $product, $nonce_field ) {
$stock_level = '<span style="color: red">Stock not managed</span>'; // Warning that stock not managed.
if ( $product->get_manage_stock() ) {
$stock_level = wc_stock_amount( $product->get_stock_quantity() );
$stock_level_form = sprintf( '<form method="post" action="">'.
'<label for="new-stock-count"><input type="number" name="new_stock_count" value="%d" /></label>'.
'<input type="hidden" name="old_stock_count" value="%d" />'.
'<input type="hidden" name="product_id" value="%d" />'.
'<input type="submit" value="Update" data-product-id="%d" />%s</form>',
$stock_level, $stock_level, $product->get_id(), $product->get_id(), $nonce_field );
$low_stock_amount = wc_get_low_stock_amount( $product );
if ( $stock_level <= $low_stock_amount ) {
$stock_level = '<mark class="outofstock">' . $stock_level_form . ' (low stock)</mark>';
}
else {
$stock_level = '<mark class="instock">' . $stock_level_form . '</mark>';
}
}
return $stock_level;
}
// Generate the table row with product name, stock level and stock status.
function wsi_stock_level_table_row( $product_name, $product_id, $stock_level, $stock_status, $product_updated_id ) {
$stock_status_text = array( 'instock' => 'In stock', 'outofstock' => '<mark class="outofstock">Out of stock</mark>', 'onbackorder' => 'On backorder' );
return sprintf( '<tr id="product-%d" class="%s"><td></td><td>%s</td><td>%s</td><td>%s</td></tr>',
$product_id, ( $product_updated_id == $product_id ) ? 'updated' : '', $product_name, $stock_level, $stock_status_text[ $stock_status ] );
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment