0

I face a problem in woocommerce shipping class. To explain it better I describe it with an example:

  • Product A has Air Shipping class
  • Product B has Road Shipping class
  • product C has Possible shipping in road and air

I use the following code to split the order based on shipping class:

add_filter( 'woocommerce_cart_shipping_packages', 'wf_split_cart_by_shipping_class_group' );
function wf_split_cart_by_shipping_class_group($packages){
    //Reset packages
    $packages               = array();
    
    //Init splitted package
    $splitted_packages      =   array();
    
    // Group of shipping class ids
    $class_groups =  array(
        'group1'    => array('plane'),
        'group2'    =>  array('truck'),
        // 'group3' =>  array(11,15,17),        
    );  
    
    foreach ( WC()->cart->get_cart() as $item_key => $item ) {
        if ( $item['data']->needs_shipping() ) {
            
            $belongs_to_class_group =   'none';
            
            $item_ship_class_id =   $item['data']->get_shipping_class();
            
            if($item_ship_class_id){
                
                foreach($class_groups as $class_group_key   =>  $class_group){
                    if(in_array($item_ship_class_id, $class_group)){                
                        $belongs_to_class_group = $class_group_key;
                        continue;
                    }
                }
                
            }           
            
            $splitted_packages[$belongs_to_class_group][$item_key]  =   $item;
        }
    }
    
    // Add grouped items as packages 
    if(is_array($splitted_packages)){
        
        foreach($splitted_packages as $splitted_package_items){
            $packages[] = array(
                'contents'        => $splitted_package_items,
                'contents_cost'   => array_sum( wp_list_pluck( $splitted_package_items, 'line_total' ) ),
                'applied_coupons' => WC()->cart->get_applied_coupons(),
                'user'            => array(
                     'ID' => get_current_user_id(),
                ),
                'destination'    => array(
                    'country'    => WC()->customer->get_shipping_country(),
                    'state'      => WC()->customer->get_shipping_state(),
                    'postcode'   => WC()->customer->get_shipping_postcode(),
                    'city'       => WC()->customer->get_shipping_city(),
                    'address'    => WC()->customer->get_shipping_address(),
                    'address_2'  => WC()->customer->get_shipping_address_2()
                )
            );
        }
    }
    return $packages;
}

In this code plan class and truck class separated. But in the case of product number 3, user should select between air and road, since it is optional. When user selects road in the total price the shipping cost apply twice, while I expect to add the weight to the first line and just calculate the shipping class once.

Update with a real example:

I am using Free version of WooCommerce Advanced Shipping By sormano.

In another word, obviously, I want to do as following:
Some products like pork and meat they need to be kept frozen, so we must send them by airplane, In some cases, the weight of the product is too heavy, let's say 20 KG, so this is not logical to send this type of product by airplane, since it is too expensive, to make the shipping fee fair enough we send them by truck. In another case, there are some products shipping them whether by airplane or truck doesn't matter, hence the customer decides the shipping method. This the scenario: a customer orders pork which must be shipped by airplane also a 10 KG package of olive which must be shipped by truck, and the third product in his basket is a product that the shipping method is not important. For the third product, the customer chooses the truck shipping method. In a normal situation, each product's shipping method is calculated separately, but I want to add the weight of the third product to the first product (olive package). what should I do to solve this problem?
Any help to handle this problem will be appreciated.

Sallar Rabiei
  • 630
  • 1
  • 12
  • 33
  • 1
    `//'group3' => array(11,15,17)` means `array('plain','truck', 'plain or truck') ` ? – Mario Abbruscato Jan 23 '21 at 17:10
  • 1
    @LoicTheAztec ,Thanks, your help means a lot to me. I add a real example to my question. – Sallar Rabiei Jan 23 '21 at 19:57
  • I use `advance shipping` for showing prices based on different weights. and then I use this code. to split them in different shipping, while I remove the mention code, and have 3 product with 3 different shipping method, I see the error that mentions there is no shipping class for this basket. @LoicTheAztec – Sallar Rabiei Jan 26 '21 at 21:52
  • 1
    How can people could guess that before? So you mean that you are using the Free version of [WooCommerce Advanced Shipping](https://wordpress.org/plugins/woocommerce-easy-table-rate-shipping/) *By JEM Plugins*… Note that *"The question should be updated to include desired behavior, a specific problem or error, **the shortest code necessary to reproduce the problem** and all related settings in a reproducible example""* if you want to have any useful working answer. – LoicTheAztec Jan 27 '21 at 06:30

2 Answers2

0

At this point you should be able to overcome the obstacle. Please note that you have not given any information on the data structure of $ packages so it was a great stretch of the imagination. You could change the script a bit to fit the data structure of $packages from woocommerce.

You need this two function to support the process

function arrarr_unique($a){
    $l=[];$r=[];
    foreach($a as $e){
       $l[]=json_encode($e);
    }
    $lu = array_unique($l);
    foreach($lu as $e){
        $r[]=json_decode($e);
    }
    return $r;
}

function filter_match_shipping($items, $array_group,$fieldname='shipping_group'){

    $res=[];
    foreach($items as $item){
        if ($item[$fieldname]==$array_group){
            $res[]=$item;
        }
    }
    return $res;
}

And here wf_split_cart_by_shipping_class_group function

#Please consider that i do not have your data structure so it can differ a little to works properly

#However, it would be nice to know what the data structure of $packages is to Help you.


add_filter( 'woocommerce_cart_shipping_packages', 'wf_split_cart_by_shipping_class_group');

function wf_split_cart_by_shipping_class_group($packages){

    
    $cts = WC()->cart->get_cart();

    # Try also:
    # $cts = $packages;
    
    # shipping_group is the field name in your woocommerce? if not , change it with the name of your table cart item field name
    $groups = arrarr_unique(wp_list_pluck($cts,'shipping_group'));

    //Settings 
    $splitted_packages=[];
    foreach($groups as $g){
        $k=json_encode($g);
        $splitted_packages[$k]=[];
    }
    //Fill 
    foreach($groups as $g){
        $k=json_encode($g);
        $splitted_packages[$k][] = filter_match_shipping( $cts,$g,'shipping_group');
    }

    // Add grouped items as packages 
    if(is_array($splitted_packages)){
        foreach($splitted_packages as $k => $splitted_package_items){

            $packages[] = array(
                'contents'        => $splitted_package_items,
                'contents_cost'   => array_sum( wp_list_pluck( $splitted_package_items, 'line_total' ) ),
                #'applied_coupons' => WC()->cart->get_applied_coupons(),
                'user'            => array(
                     'ID' => get_current_user_id(),
                ),
                'destination'    => array(
                    'country'    => WC()->customer->get_shipping_country(),
                    'state'      => WC()->customer->get_shipping_state(),
                    'postcode'   => WC()->customer->get_shipping_postcode(),
                    'city'       => WC()->customer->get_shipping_city(),
                    'address'    => WC()->customer->get_shipping_address(),
                    'address_2'  => WC()->customer->get_shipping_address_2()
                )
            );

        }
    }

    return $packages;

}

Mario Abbruscato
  • 819
  • 1
  • 7
  • 9
-1

for my convenience I have created the data structure of your Woocommerce in the most similar way possible.

You can test it with php client php x.php

<?php
function get_current_user_id(){
    return 1;
}
function wp_list_pluck($items,$column){
    $ares=[];
    foreach($items as $item){
        foreach($item as $k=>$v){
            if( $k==$column){
                $ares[]=$v;
            }
        }
    }
    return $ares;
}
$wc=[
    "customer"=>[
        "get_shipping_country"=>"Italy",
        "get_shipping_state"=>"Not yet shipped",
        "get_shipping_postcode"=>"32029",
        "get_shipping_city"=>"Beautiful Field",
        "get_shipping_address"=>"Road of Trees",
        "get_shipping_address_2"=>""
    ],
    "cart"=> [
        "get_contents" => [
                ["prod_id"=>1, "shipping_group"=>["plane"],'line_total'=>11.00 ,'needs_shipping'=>true],
                ["prod_id"=>2, "shipping_group"=>["truck"],'line_total'=>14.00 ,'needs_shipping'=>true],
                ["prod_id"=>3, "shipping_group"=>[ 11, 15, 17],'line_total'=>16.00,'needs_shipping'=>true],
                ["prod_id"=>4, "shipping_group"=>["plane"],'line_total'=>22.00 ,'needs_shipping'=>true],
                ["prod_id"=>5, "shipping_group"=>["truck"],'line_total'=>24.00 ,'needs_shipping'=>true],
                ["prod_id"=>6, "shipping_group"=>[ 11, 15, 17],'line_total'=>26.00,'needs_shipping'=>true],
                ["prod_id"=>7, "shipping_group"=>["plane"],'line_total'=>32.00 ,'needs_shipping'=>true],
                ["prod_id"=>8, "shipping_group"=>["truck"],'line_total'=>34.00 ,'needs_shipping'=>true],
                ["prod_id"=>9, "shipping_group"=>[ 11, 15, 17],'line_total'=>36.00,'needs_shipping'=>true]
            ]
    ]
];

$wc["cart"]["get_cart"]=[
                'contents' => $wc["cart"]["get_contents"],
                'contents_cost'   => array_sum( wp_list_pluck( $wc["cart"]["get_contents"], 'line_total' ) ),
                'user'            => array('ID' => get_current_user_id()),
                'destination'    => [
                    'country'    => $wc["customer"]["get_shipping_country"],
                    'state'      => $wc["customer"]["get_shipping_state"],
                    'postcode'   => $wc["customer"]["get_shipping_postcode"],
                    'city'       => $wc["customer"]["get_shipping_city"],
                    'address'    => $wc["customer"]["get_shipping_address"],
                    'address_2'  => $wc["customer"]["get_shipping_address_2"]
                ]   
            ];

function arrarr_unique($a){
    $l=[];$r=[];
    foreach($a as $e){
       $l[]=json_encode($e);
    }
    $lu = array_unique($l);
    foreach($lu as $e){
        $r[]=json_decode($e);
    }
    return $r;
}

function filter_match_shipping($items, $array_group){
    $res=[];
    foreach($items as $item){
        if ($item["shipping_group"]==$array_group){
            $res[]=$item;
        }
    }
    return $res;
}
function add_filter(){
    //do nothing
}


add_filter( 'woocommerce_cart_shipping_packages', 'wtf' );

function wtf($wc,$packages=[]){
    
    $cts = $wc["cart"]["get_contents"];
    $groups = arrarr_unique(wp_list_pluck($cts,'shipping_group'));

    //Settings 
    $splitted_packages=[];
    foreach($groups as $g){
        $k=json_encode($g);        
        $splitted_packages[$k]=[];
    }
    //Fill 
    foreach($groups as $g){
        $k=json_encode($g);        
        $splitted_packages[$k][] = filter_match_shipping( $cts,$g );
    }

    // Add grouped items as packages 
    if(is_array($splitted_packages)){
        $c=0;        
        foreach($splitted_packages as $k => $splitted_package_items){
                $packages[] = array(
                    'contents'        => $splitted_package_items,
                    'contents_cost'   => array_sum( wp_list_pluck( $splitted_package_items, 'line_total' ) ),
                    'user'            => array(
                         'ID' => get_current_user_id(),
                    ),
                    'destination'    => array(
                        'country'    => $wc["customer"]["get_shipping_country"],
                        'state'      => $wc["customer"]["get_shipping_state"],
                        'postcode'   => $wc["customer"]["get_shipping_postcode"],
                        'city'       => $wc["customer"]["get_shipping_city"],
                        'address'    => $wc["customer"]["get_shipping_address"],
                        'address_2'  => $wc["customer"]["get_shipping_address_2"]
                    )
                );
        }
    }

    return $packages;

}

$pks = wtf($wc);
echo print_r($pks,1);

?>

Here the result:

Array
(
    [0] => Array
        (
            [contents] => Array
                (
                    [0] => Array
                        (
                            [0] => Array
                                (
                                    [prod_id] => 1
                                    [shipping_group] => Array
                                        (
                                            [0] => plane
                                        )

                                    [line_total] => 11
                                    [needs_shipping] => 1
                                )

                            [1] => Array
                                (
                                    [prod_id] => 4
                                    [shipping_group] => Array
                                        (
                                            [0] => plane
                                        )

                                    [line_total] => 22
                                    [needs_shipping] => 1
                                )

                            [2] => Array
                                (
                                    [prod_id] => 7
                                    [shipping_group] => Array
                                        (
                                            [0] => plane
                                        )

                                    [line_total] => 32
                                    [needs_shipping] => 1
                                )

                        )

                )

            [contents_cost] => 0
            [user] => Array
                (
                    [ID] => 1
                )

            [destination] => Array
                (
                    [country] => Italy
                    [state] => Not yet shipped
                    [postcode] => 32029
                    [city] => Beautiful Field
                    [address] => Road of Trees
                    [address_2] => 
                )

        )

    [1] => Array
        (
            [contents] => Array
                (
                    [0] => Array
                        (
                            [0] => Array
                                (
                                    [prod_id] => 2
                                    [shipping_group] => Array
                                        (
                                            [0] => truck
                                        )

                                    [line_total] => 14
                                    [needs_shipping] => 1
                                )

                            [1] => Array
                                (
                                    [prod_id] => 5
                                    [shipping_group] => Array
                                        (
                                            [0] => truck
                                        )

                                    [line_total] => 24
                                    [needs_shipping] => 1
                                )

                            [2] => Array
                                (
                                    [prod_id] => 8
                                    [shipping_group] => Array
                                        (
                                            [0] => truck
                                        )

                                    [line_total] => 34
                                    [needs_shipping] => 1
                                )

                        )

                )

            [contents_cost] => 0
            [user] => Array
                (
                    [ID] => 1
                )

            [destination] => Array
                (
                    [country] => Italy
                    [state] => Not yet shipped
                    [postcode] => 32029
                    [city] => Beautiful Field
                    [address] => Road of Trees
                    [address_2] => 
                )

        )

    [2] => Array
        (
            [contents] => Array
                (
                    [0] => Array
                        (
                            [0] => Array
                                (
                                    [prod_id] => 3
                                    [shipping_group] => Array
                                        (
                                            [0] => 11
                                            [1] => 15
                                            [2] => 17
                                        )

                                    [line_total] => 16
                                    [needs_shipping] => 1
                                )

                            [1] => Array
                                (
                                    [prod_id] => 6
                                    [shipping_group] => Array
                                        (
                                            [0] => 11
                                            [1] => 15
                                            [2] => 17
                                        )

                                    [line_total] => 26
                                    [needs_shipping] => 1
                                )

                            [2] => Array
                                (
                                    [prod_id] => 9
                                    [shipping_group] => Array
                                        (
                                            [0] => 11
                                            [1] => 15
                                            [2] => 17
                                        )

                                    [line_total] => 36
                                    [needs_shipping] => 1
                                )

                        )

                )

            [contents_cost] => 0
            [user] => Array
                (
                    [ID] => 1
                )

            [destination] => Array
                (
                    [country] => Italy
                    [state] => Not yet shipped
                    [postcode] => 32029
                    [city] => Beautiful Field
                    [address] => Road of Trees
                    [address_2] => 
                )

        )

)

To clarify, I divide the script into its basic parts below

Data structure: I used Array of array but the sintax of your WC Class and methods call can be simply identify.

$wc=[
    "customer"=>[
        "get_shipping_country"=>"Italy",
        "get_shipping_state"=>"Not yet shipped",
        "get_shipping_postcode"=>"32029",
        "get_shipping_city"=>"Beautiful Field",
        "get_shipping_address"=>"Road of Trees",
        "get_shipping_address_2"=>""
    ],
    "cart"=> [
        "get_contents" => [
                ["prod_id"=>1, "shipping_group"=>["plane"],'line_total'=>11.00 ,'needs_shipping'=>true],
                ["prod_id"=>2, "shipping_group"=>["truck"],'line_total'=>14.00 ,'needs_shipping'=>true],
                ["prod_id"=>3, "shipping_group"=>[ 11, 15, 17],'line_total'=>16.00,'needs_shipping'=>true],
                ["prod_id"=>4, "shipping_group"=>["plane"],'line_total'=>22.00 ,'needs_shipping'=>true],
                ["prod_id"=>5, "shipping_group"=>["truck"],'line_total'=>24.00 ,'needs_shipping'=>true],
                ["prod_id"=>6, "shipping_group"=>[ 11, 15, 17],'line_total'=>26.00,'needs_shipping'=>true],
                ["prod_id"=>7, "shipping_group"=>["plane"],'line_total'=>32.00 ,'needs_shipping'=>true],
                ["prod_id"=>8, "shipping_group"=>["truck"],'line_total'=>34.00 ,'needs_shipping'=>true],
                ["prod_id"=>9, "shipping_group"=>[ 11, 15, 17],'line_total'=>36.00,'needs_shipping'=>true]
            ]
    ]
];

$wc["cart"]["get_cart"]=[
                'contents' => $wc["cart"]["get_contents"],
                'contents_cost'   => array_sum( wp_list_pluck( $wc["cart"]["get_contents"], 'line_total' ) ),
                'user'            => array('ID' => get_current_user_id()),
                'destination'    => [
                    'country'    => $wc["customer"]["get_shipping_country"],
                    'state'      => $wc["customer"]["get_shipping_state"],
                    'postcode'   => $wc["customer"]["get_shipping_postcode"],
                    'city'       => $wc["customer"]["get_shipping_city"],
                    'address'    => $wc["customer"]["get_shipping_address"],
                    'address_2'  => $wc["customer"]["get_shipping_address_2"]
                ]   
            ];

The i create Fake Woocommerce function in order for the script to work

function get_current_user_id(){
    return 1;
}
function wp_list_pluck($items,$column){
    $ares=[];
    foreach($items as $item){
        foreach($item as $k=>$v){
            if( $k==$column){
                $ares[]=$v;
            }
        }
    }
    return $ares;
}
function add_filter(){
    //do nothing
}

Then some function to support the process


function arrarr_unique($a){
    $l=[];$r=[];
    foreach($a as $e){
       $l[]=json_encode($e);
    }
    $lu = array_unique($l);
    foreach($lu as $e){
        $r[]=json_decode($e);
    }
    return $r;
}

function filter_match_shipping($items, $array_group){
    $res=[];
    foreach($items as $item){
        if ($item["shipping_group"]==$array_group){
            $res[]=$item;
        }
    }
    return $res;
}

The function of the Woocommerce Filter to add .

add_filter( 'woocommerce_cart_shipping_packages', 'wtf' );

function wtf($wc,$packages=[]){
    
    $cts = $wc["cart"]["get_contents"];
    $groups = arrarr_unique(wp_list_pluck($cts,'shipping_group'));

    //Settings 
    $splitted_packages=[];
    foreach($groups as $g){
        $k=json_encode($g);        
        $splitted_packages[$k]=[];
    }
    //Fill 
    foreach($groups as $g){
        $k=json_encode($g);        
        $splitted_packages[$k][] = filter_match_shipping( $cts,$g );
    }

    // Add grouped items as packages 
    if(is_array($splitted_packages)){
        $c=0;        
        foreach($splitted_packages as $k => $splitted_package_items){
                $packages[] = array(
                    'contents'        => $splitted_package_items,
                    'contents_cost'   => array_sum( wp_list_pluck( $splitted_package_items, 'line_total' ) ),
                    'user'            => array(
                         'ID' => get_current_user_id(),
                    ),
                    'destination'    => array(
                        'country'    => $wc["customer"]["get_shipping_country"],
                        'state'      => $wc["customer"]["get_shipping_state"],
                        'postcode'   => $wc["customer"]["get_shipping_postcode"],
                        'city'       => $wc["customer"]["get_shipping_city"],
                        'address'    => $wc["customer"]["get_shipping_address"],
                        'address_2'  => $wc["customer"]["get_shipping_address_2"]
                    )
                );
        }
    }

    return $packages;

}

$pks = wtf($wc);
echo print_r($pks,1);

It is possible that there are some tweaks to do that I will do with pleasure. For any clarification or problem solving. write me in the comments.

Mario Abbruscato
  • 819
  • 1
  • 7
  • 9
  • Thanks Mario, you mean I need to add the last three parts to `function.php` ? Why you use `get_current_user_id` ? Also if I notice well in the first section I have to repeat it for all product id? – Sallar Rabiei Jan 23 '21 at 21:05
  • 1
    For now do not touch your wocommerce installation. Let me know if you are using Linux or Win or Mac. You need `php client` to test it . My script is an example. I used fake function of woocommerce because i don't have WC in my system . But what is important is the concept. You can copy the entire script in a file , name it x.php and then run it with php client. Now I have to take a break, I will respond with pleasure in a while. – Mario Abbruscato Jan 23 '21 at 21:13
  • thank you Mario, I use woocommerce on Linux VPS. – Sallar Rabiei Jan 23 '21 at 22:09
  • 1
    ok , can you understand what my script do ? – Mario Abbruscato Jan 23 '21 at 22:10
  • No, unfortunately, I can't use it. – Sallar Rabiei Jan 23 '21 at 22:54
  • i added a new answer with only necessary parts fro your `functions.php`. At this point you should be able to overcome the obstacle. Please note that you have not given any information on the data structure of `$ packages` so it was a great stretch of the imagination. You could change the script a bit to fit the data structure of your $ packages from woocommerce – Mario Abbruscato Jan 23 '21 at 22:55
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/227736/discussion-between-mario-abbruscato-and-m-sallar-rabiei). – Mario Abbruscato Jan 23 '21 at 23:24