2

I am writing a plugin that connects WooCommerce to the Metrc API and pulls products in if they do not exist or updates products with the information from the API.

// Other Code Above 
// Decode the JSON response
    $data = json_decode($response_body, true);

    // Format the JSON response with indentation
    $formatted_data = json_encode($data, JSON_PRETTY_PRINT);

    // Log the formatted JSON response to a file in the root of the active plugin folder
    $log_file = plugin_dir_path(__FILE__) . 'metrc_response.log'; // Path to the log file
    $log_data = date('Y-m-d H:i:s') . " - " . $formatted_data . "\n"; // Log data to write

    // Append the log data to the log file
    file_put_contents($log_file, $log_data, FILE_APPEND);

    // Extract the relevant information from the API response
    $packages = $data['Data'];

    // Get all published products in WooCommerce
    $products = wc_get_products(array('status' => 'publish'));

    foreach ($products as $product) {
        $product_package = get_post_meta($product->get_id(), '_metrc_package', true);
        $matched = false;

        foreach ($packages as $package) {
            $label = $package['Label'];

            if ($product_package === $label) {
                $matched = true;
                break;
            }
        }
    }

    // Import new products based on the API response
    foreach ($packages as $package) {
        $label = $package['Label'];
        $quantity = $package['Quantity'];
        $uom = $package['UnitOfMeasureAbbreviation'];
        $prodcat = $package['Item']['ProductCategoryName'];
        $itemName = $package['Item']['Name'];

        // Check if product exists based on package meta value, else create a new product
        $product_id = get_product_id_by_meta('_metrc_package', $label);
        if (!$product_id) {
            $new_product = new WC_Product();

            // Set the product properties
            $new_product->set_name($package['Item']['Name']);
            $new_product->set_status('pending');
            $new_product->set_regular_price(0);
            
            // Enable stock management
            $new_product->set_manage_stock(true);
            $new_product->set_stock_status('instock');

            // Save the product
            $product_id = $new_product->save();

            // Update the package meta value for the new product
            update_post_meta($product_id, '_metrc_package', $label);
            update_post_meta($product_id, '_metrc_category', $prodcat);
            update_post_meta($product_id, '_metrc_item', $itemName);

            // Assign the initial default category to the new product
            wp_set_object_terms($product_id, $prodcat, 'product_cat', true);

            // Set the Product SKU 
            update_post_meta($product_id, '_sku', $label);

        } else {
            $new_product = wc_get_product($product_id);
        }

        // Update _metrc_qty and _metrc_uom with the package quantity and unit of measurement
        update_post_meta($product_id, '_metrc_qty', $quantity);
        update_post_meta($product_id, '_metrc_uom', $uom);
        update_post_meta($product_id, '_metrc_item', $itemName);
        Update_post_meta($product_id, '_metrc_category', $prodcat);
        
        // Set Product SKU 
        update_post_meta($product_id, '_sku', $label);
        }
      }

There is another function that is handling some basic math to convert weight based products into unit based products, where the formula is:

_qty = _metrc_qty / _package_size 

This takes the value in the quantity input box in the Metrc Data tab and divides it against the Package Size value entered in the inventory tab and outputs the result in the Quantity input field in the inventory tab. The Quantity input field in the Metrc Data Tab is populated by the handling of the response from the API by the function get_active() which is used to query the API via a curl request set via action scheduler to run every 15 minutes and gather all the "active" products in the third parties system and each product information such as description, qty etc... where it sends us back that data as a response.

From here a foreach loop is created which iterates through all the products in the WooCommerce database looking to see if the product already exists and if not creates a new product with a pending status and adds the information. If the product already exists, it updates the relevant product meta information and moves on to the next product.

However, what is occurring that the updated values resulting from the API call on existing products only appear to be visible on the edit product page unless you click the update button when editing a product manually. The all products listing page will display the old information such as in stock (20) rather than the updated value that was set by the API

See Screenshots for example:

Screenshot of product listing page

enter image description here

Edit Product Page on the Inventory Tab

enter image description here

Edit Product Page on my created Metrc Data Tab

enter image description here

Now if I manually update the product the stock quantity in WooCommerce updates to reflect the new value both on the front end and on the product listing page

Can someone point me in the right direction or let me know what i am missing and how I can get the new value to be displayed after each import without having to manually perform zero change updates on each product to make it populate the proper stock amounts.

I assumed this could be being caused by the default stock caching as it was displaying the transient values rather than the most up-to-date values from the import, So I included a function below to disable the stock caching, however this made no difference.

function disable_woocommerce_stock_caching() {
    return false;
}
add_filter('woocommerce_stock_html_cache_stock_html_enabled', 'disable_woocommerce_stock_caching');

This did not cause the front end or the product listing page in WooCommerce admin to display the proper values.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • There is an answer here below waiting for your feedback. Please, see [*"What should I do when someone answers my question? "*](https://stackoverflow.com/help/someone-answers). – LoicTheAztec Jul 31 '23 at 12:16

1 Answers1

0

Don't use WordPress get_post_meta() or update_post_meta() as there is now more and more WooCommerce custom tables and custom cached data in WooCommerce.

Important: You need to include in your code, the stock quantity calculation and set this stock quantity using something like (before saving):

$new_product->set_stock_quantity($new_stock_quantity);

Try the following instead:

// Import new products based on the API response
foreach ($packages as $package) {
    $label = $package['Label'];
    $quantity = $package['Quantity'];
    $uom = $package['UnitOfMeasureAbbreviation'];
    $prodcat = $package['Item']['ProductCategoryName'];
    $itemName = $package['Item']['Name'];

    // Check if product exists based on package meta value, else create a new product
    $product_id = get_product_id_by_meta('_metrc_package', $label);

    if (! $product_id) {
        // The product doesn't exists: We create it

        // Important!: Define here the desired product type
        $product_type = 'simple'; // Simple by default

        // Create an empty instance of the WC_Product object with the correct type
        $new_product = wc_get_product_object_type( $product_type );

        // Set some required product properties
        $new_product->set_name($itemName);
        $new_product->set_status('pending');
        $new_product->set_regular_price(0);

        // Set the Product SKU 
        $new_product->set_sku($label);
        
        // Enable stock management
        $new_product->set_manage_stock(true);
        $new_product->set_stock_status('instock');

        // Get the term ID for the default category
        $term = term_exists( $prodcat, 'product_cat' ); 

        // Set default category
        $new_product->set_category_ids( array($term['term_id']) );

        // Add custom meta data
        $new_product->add_meta_data('_metrc_package', $label);
        $new_product->add_meta_data('_metrc_category', $prodcat);
        $new_product->add_meta_data('_metrc_item', $itemName);
        $new_product->add_meta_data('_metrc_qty', $quantity);
        $new_product->add_meta_data('_metrc_uom', $uom);
        $new_product->add_meta_data('_metrc_item', $itemName);
        $new_product->add_meta_data('_metrc_category', $prodcat);
    } else {
        // The Product exists, so we get the product Object
        $new_product = wc_get_product($product_id);

        // Update the Product SKU 
        $new_product->set_sku($label);

        // Update custom meta data
        $new_product->update_meta_data('_metrc_qty', $quantity);
        $new_product->update_meta_data('_metrc_uom', $uom);
        $new_product->update_meta_data('_metrc_item', $itemName);
        $new_product->update_meta_data('_metrc_category', $prodcat);
    }
    // Calculate the stock quantity
    $stock_quantity = $quantity / $package_size; // <== (??? $package_size)

    // Set/Update the product stock quantity
    $new_product->set_stock_quantity($stock_quantity);

    // And now we can save (at the end)
    $product_id = $new_product->save();
}

It should work without needing to update the product manually, to get the refreshed data.

Related: Create programmatically a product using CRUD methods in Woocommerce 3

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399