Keith Pickering
Keith Pickering

Reputation: 786

Woocommerce: Store incremental order number, per variation

We're setting up a store in which each product has three variations (small, medium, and large). There is a limited stock for each variation of each product - 100 small, 35 medium, 15 large. Because the products are limited edition, we want to be able to store the customer's order number - i.e. original quantity - new quantity. So for instance, if someone is the first to purchase the Small variant of Product A, their number will be 1. The next person who purchases the Small variant of Product A will be number 2, and so on, all the way up to 100 after which the Small variant will no longer be for sale.

Here's what I have so far, hooked into woocommerce_checkout_update_order_meta:

// Generate order number
    $order = wc_get_order( $order_id );
    // Getting the items in the order
    $order_items = $order->get_items();
    // Iterating through each item in the order
    foreach ($order_items as $item_id => $item_data) {
        $product = wc_get_product($item_data['product_id']);
        $total = get_post_meta($product->id, 'total_sales', true);
        $quantity = $product->get_stock_quantity();
        $max = $total + $quantity;
        $number = $max - $quantity;
        $name = $item_data['name'];

        update_post_meta( $order_id, $name.' - Print Number', $number);
    }

However, this doesn't work properly, because the total_sales field does not keep track of variations; instead it refers to the total sales of ANY variation of a given product.

So what I need to do is come up with a way to keep track of the original stock of each variation, then subtract the current variation quantity from the original stock whenever an order is completed. Does anyone have any advice on getting this implemented? I'd love to have this calculated automatically so we don't have to take extra time to do it manually for each order.

Upvotes: 0

Views: 724

Answers (2)

Keith Pickering
Keith Pickering

Reputation: 786

VanboDevelops' answer was extremely helpful, so I went ahead and marked it as the accepted answer. However, how I ended up doing it was a little different. I wanted to be able to edit the limited-edition numbers easily, not only for testing, but also in case an order is canceled and we need to roll the print number back. So for me, the solution was to create a custom field for all variations that can be easily edited alongside the price, dimensions, etc. Here's what I added to functions.php:

add_action( 'woocommerce_product_after_variable_attributes', 'variation_settings_fields', 10, 3 );
function variation_settings_fields( $loop, $variation_data, $variation ) {
    woocommerce_wp_text_input( 
        array( 
            'id'          => '_print_number[' . $variation->ID . ']', 
            'label'       => __( 'Print number (most recently sold)', 'woocommerce' ), 
            'desc_tip'    => 'true',
            'description' => __( 'The most recently-sold Print Number for this size and print. WARNING: This value plus 1 is used to show the user which print number they will be purchasing. Only update this value manually in specific situations, such as if an order is canceled and the print number needs to be offset.', 'woocommerce' ),
            'value'       => get_post_meta( $variation->ID, '_print_number', true ),
            'custom_attributes' => array(
                            'step'  => '1',
                            'min'   => '1'
                        ) 
        )
    );
}
add_action( 'woocommerce_save_product_variation', 'save_variation_settings_fields', 10, 2 );
function save_variation_settings_fields( $post_id ) {
    $number_field = $_POST['_print_number'][ $post_id ];
    if( !empty( $number_field ) || $number_field == 0 ) {
        update_post_meta( $post_id, '_print_number', esc_attr( $number_field ) );
    }
}
add_filter( 'woocommerce_available_variation', 'load_variation_settings_fields' );
function load_variation_settings_fields( $variations ) {
    $variations['print_number'] = get_post_meta( $variations[ 'variation_id' ], '_print_number', true );

    return $variations;
}
add_action( 'woocommerce_checkout_update_order_meta', 'prefix_add_order_meta', 10, 2 );
function prefix_add_order_meta( $order_id, $data ) {
    // Generate order number
    $order = wc_get_order( $order_id );

    // Getting the items in the order
    $order_items = $order->get_items();

    foreach ( $order_items as $item_id => $item_data ) {
        $product = $item_data->get_product();
        $product_id = $product->get_id();

        // Update print number
        $print_number = (int) get_post_meta( $product_id, '_print_number', true );
        $print_number += wc_clean( $item_data['qty'] );
        update_post_meta( $product_id, '_print_number', wc_clean( $print_number ) );

        // Add to order meta
        $order->add_meta_data( $product_id . '_print_number', wc_clean( $print_number ), true );
        $order->save();
    }
}

This solution also considers whether more than one of the selected variation is included in the order - so if someone picks 2 Small prints of the same photo, for instance, the print number for that variation will go up by 2 accordingly. In such instances, the order meta info will only show the latest print number, but by looking at the quantity and the final print number, we can infer which numbers to ship out. A more robust solution would ideally list ALL print numbers in the order metadata, but this will work for our needs I think.

Upvotes: 0

VanboDevelops
VanboDevelops

Reputation: 156

What you can do is add two custom fields for each product variation: 1. Original product quantity: The quantity you will be substracting from 2. Total sales: The current sales quantity

These will allow you to keep track of each variation quantity and sales.

Add the number to the order or to the order item meta: 1. You can add the quantity to the order as a whole by prefixing the meta field with the product ID, for example. Use woocommerce_checkout_update_order_meta:

add_action( 'woocommerce_checkout_update_order_meta', 'prefix_add_order_meta', 10, 2 );
function prefix_add_order_meta( $order_id, $data ) {
    // Generate order number
    $order = wc_get_order( $order_id );

    // Getting the items in the order
    $order_items = $order->get_items();

    /**
     * @var WC_Order_Item_Product $item_data
     */
    foreach ( $order_items as $item_id => $item_data ) {
        $product     = $item_data->get_product();
        $total_sales = (int) get_post_meta( $product->get_id(), 'custom_total_sales', true );

        $item_number = $total_sales + 1;

        // Add to order meta
        $order->add_meta_data( $product->get_id() . '_print_number', wc_clean( $item_number ), true );
        $order->save();

        // Add to order item meta. You can set this here or use the 'woocommerce_checkout_create_order_line_item'
        $item_data->add_meta_data( 'print_number', wc_clean( $item_number ), true );
        $item_data->save();

        // Save total to product
        $product->add_meta_data( 'custom_total_sales', wc_clean( $item_number ), true );
        $product->save();
    }
}
  1. You can use the 'woocommerce_checkout_create_order_line_item' to add the meta to the order item. No need for prefixing as the item itself is the product

Here is an example:

    add_action( 'woocommerce_checkout_create_order_line_item', 'prefix_add_order_item_meta', 10, 4 );
function prefix_add_order_item_meta( $item, $cart_item_key, $values, $order ) {
    $product     = $item->get_product();
    $total_sales = (int) get_post_meta( $product->get_id(), 'custom_total_sales', true );

    $item_number = $total_sales + 1;

    // Add to order item meta. No need to save here
    $item->add_meta_data( 'print_number', wc_clean( $item_number ), true );
}

Upvotes: 2

Related Questions