Jump to content

[Override] Calculate shipping cost grouping products by Supplier


rubensaid
 Share

Recommended Posts

I found a better solution!

Just activate advanced stock management, then edit products to allow use advanced stock management for them and finally linked these products with a specific warehouse (could be one for each supplier).

Prestashop will calculate cost independently and also will split orders.

 

Hi,

I want to share a code I was working out and now it's functionally.

This is an override for Cart.php class that make possible to calculate shipping cost grouping products by supplier.

<?php

class Cart extends CartCore
{
	/**
	 * Return package shipping cost
	 *
	 * @param integer $id_carrier Carrier ID (default : current carrier)
	 * @param booleal $use_tax
	 * @param Country $default_country
	 * @param Array $product_list
	 * @param array $product_list List of product concerned by the shipping. If null, all the product of the cart are used to calculate the shipping cost
	 *
	 * @return float Shipping total
	 */
	public function getPackageShippingCost($id_carrier = null, $use_tax = true, Country $default_country = null, $product_list = null, $id_zone = null)
	{
		if ($this->isVirtualCart())
			return 0;

		if (!$default_country)
			$default_country = Context::getContext()->country;

		if (!is_null($product_list))
			foreach ($product_list as $key => $value)
				if ($value['is_virtual'] == 1)
					unset($product_list[$key]);

		$complete_product_list = $this->getProducts();
		if (is_null($product_list))
			$products = $complete_product_list;
		else
			$products = $product_list;

		if (Configuration::get('PS_TAX_ADDRESS_TYPE') == 'id_address_invoice')
			$address_id = (int)$this->id_address_invoice;
		elseif (count($product_list))
		{
			$prod = current($product_list);
			$address_id = (int)$prod['id_address_delivery'];
		}
		else
			$address_id = null;
		if (!Address::addressExists($address_id))
			$address_id = null;

		$cache_id = 'getPackageShippingCost_'.(int)$this->id.'_'.(int)$address_id.'_'.(int)$id_carrier.'_'.(int)$use_tax.'_'.(int)$default_country->id;
		if ($products)
			foreach ($products as $product)
				$cache_id .= '_'.(int)$product['id_product'].'_'.(int)$product['id_product_attribute'];

		if (Cache::isStored($cache_id))
			return Cache::retrieve($cache_id);

		// Order total in default currency without fees
		$order_total = $this->getOrderTotal(true, Cart::ONLY_PHYSICAL_PRODUCTS_WITHOUT_SHIPPING, $product_list);

		// Start with shipping cost at 0
		$shipping_cost = 0;
		// If no product added, return 0
		if (!count($products))
		{
			Cache::store($cache_id, $shipping_cost);
			return $shipping_cost;
		}

		if (!isset($id_zone))
		{
			// Get id zone
			if (!$this->isMultiAddressDelivery()
				&& isset($this->id_address_delivery) // Be carefull, id_address_delivery is not usefull one 1.5
				&& $this->id_address_delivery
				&& Customer::customerHasAddress($this->id_customer, $this->id_address_delivery
			))
				$id_zone = Address::getZoneById((int)$this->id_address_delivery);
			else
			{
				if (!Validate::isLoadedObject($default_country))
					$default_country = new Country(Configuration::get('PS_COUNTRY_DEFAULT'), Configuration::get('PS_LANG_DEFAULT'));

				$id_zone = (int)$default_country->id_zone;
			}
		}

		if ($id_carrier && !$this->isCarrierInRange((int)$id_carrier, (int)$id_zone))
			$id_carrier = '';

		if (empty($id_carrier) && $this->isCarrierInRange((int)Configuration::get('PS_CARRIER_DEFAULT'), (int)$id_zone))
			$id_carrier = (int)Configuration::get('PS_CARRIER_DEFAULT');

		$total_package_without_shipping_tax_inc = $this->getOrderTotal(true, Cart::BOTH_WITHOUT_SHIPPING, $product_list);
		if (empty($id_carrier))
		{
			if ((int)$this->id_customer)
			{
				$customer = new Customer((int)$this->id_customer);
				$result = Carrier::getCarriers((int)Configuration::get('PS_LANG_DEFAULT'), true, false, (int)$id_zone, $customer->getGroups());
				unset($customer);
			}
			else
				$result = Carrier::getCarriers((int)Configuration::get('PS_LANG_DEFAULT'), true, false, (int)$id_zone);

			foreach ($result as $k => $row)
			{
				if ($row['id_carrier'] == Configuration::get('PS_CARRIER_DEFAULT'))
					continue;

				if (!isset(self::$_carriers[$row['id_carrier']]))
					self::$_carriers[$row['id_carrier']] = new Carrier((int)$row['id_carrier']);

				$carrier = self::$_carriers[$row['id_carrier']];

				// Get only carriers that are compliant with shipping method
				if (($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT && $carrier->getMaxDeliveryPriceByWeight((int)$id_zone) === false)
				|| ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_PRICE && $carrier->getMaxDeliveryPriceByPrice((int)$id_zone) === false))
				{
					unset($result[$k]);
					continue;
				}

				// If out-of-range behavior carrier is set on "Desactivate carrier"
print "Git3 ".$this->getTotalWeight();
				if ($row['range_behavior'])
				{
					$check_delivery_price_by_weight = Carrier::checkDeliveryPriceByWeight($row['id_carrier'], $this->getTotalWeight(), (int)$id_zone);

					$total_order = $total_package_without_shipping_tax_inc;
					$check_delivery_price_by_price = Carrier::checkDeliveryPriceByPrice($row['id_carrier'], $total_order, (int)$id_zone, (int)$this->id_currency);

					// Get only carriers that have a range compatible with cart
					if (($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT && !$check_delivery_price_by_weight)
					|| ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_PRICE && !$check_delivery_price_by_price))
					{
						unset($result[$k]);
						continue;
					}
				}

				if ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT)
					$shipping = $carrier->getDeliveryPriceByWeight($this->getTotalWeight($product_list), (int)$id_zone);
				else
					$shipping = $carrier->getDeliveryPriceByPrice($order_total, (int)$id_zone, (int)$this->id_currency);

				if (!isset($min_shipping_price))
					$min_shipping_price = $shipping;

				if ($shipping <= $min_shipping_price)
				{
					$id_carrier = (int)$row['id_carrier'];
					$min_shipping_price = $shipping;
				}
			}
		}

		if (empty($id_carrier))
			$id_carrier = Configuration::get('PS_CARRIER_DEFAULT');

		if (!isset(self::$_carriers[$id_carrier]))
			self::$_carriers[$id_carrier] = new Carrier((int)$id_carrier, Configuration::get('PS_LANG_DEFAULT'));

		$carrier = self::$_carriers[$id_carrier];

		// No valid Carrier or $id_carrier <= 0 ?
		if (!Validate::isLoadedObject($carrier))
		{
			Cache::store($cache_id, 0);
			return 0;
		}

		if (!$carrier->active)
		{
			Cache::store($cache_id, $shipping_cost);
			return $shipping_cost;
		}

		// Free fees if free carrier
		if ($carrier->is_free == 1)
		{
			Cache::store($cache_id, 0);
			return 0;
		}

		// Select carrier tax
		if ($use_tax && !Tax::excludeTaxeOption())
		{
			$address = Address::initialize((int)$address_id);
			$carrier_tax = $carrier->getTaxesRate($address);
		}

		$configuration = Configuration::getMultiple(array(
			'PS_SHIPPING_FREE_PRICE',
			'PS_SHIPPING_HANDLING',
			'PS_SHIPPING_METHOD',
			'PS_SHIPPING_FREE_WEIGHT'
		));

		// Free fees
		$free_fees_price = 0;
		if (isset($configuration['PS_SHIPPING_FREE_PRICE']))
			$free_fees_price = Tools::convertPrice((float)$configuration['PS_SHIPPING_FREE_PRICE'], Currency::getCurrencyInstance((int)$this->id_currency));
		$orderTotalwithDiscounts = $this->getOrderTotal(true, Cart::BOTH_WITHOUT_SHIPPING, null, null, false);
		if ($orderTotalwithDiscounts >= (float)($free_fees_price) && (float)($free_fees_price) > 0)
		{
			Cache::store($cache_id, $shipping_cost);
			return $shipping_cost;
		}

		if (isset($configuration['PS_SHIPPING_FREE_WEIGHT'])
			&& $this->getTotalWeight() >= (float)$configuration['PS_SHIPPING_FREE_WEIGHT']
			&& (float)$configuration['PS_SHIPPING_FREE_WEIGHT'] > 0)
		{
			Cache::store($cache_id, $shipping_cost);
			return $shipping_cost;
		}

		// Get shipping cost using correct method
		if ($carrier->range_behavior)
		{
			if(!isset($id_zone))
			{
				// Get id zone
				if (isset($this->id_address_delivery)
					&& $this->id_address_delivery
					&& Customer::customerHasAddress($this->id_customer, $this->id_address_delivery))
					$id_zone = Address::getZoneById((int)$this->id_address_delivery);
				else
					$id_zone = (int)$default_country->id_zone;
			}

			$suppliers = Array();
			$weight_per_supplier = Array();
			$products_per_suppliers = Array();

			foreach ($products as $product) {
				$product_supplier = $product['id_supplier'];
				if (!in_array($product_supplier, $suppliers)) {
					$suppliers[] = $product_supplier;
				}
				$products_per_suppliers[$product_supplier][] = $product;
				$weight_per_supplier[$product_supplier] += $product['weight'];
			}

			if (($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT && !Carrier::checkDeliveryPriceByWeight($carrier->id, $this->getTotalWeight(), (int)$id_zone))
			|| ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_PRICE && !Carrier::checkDeliveryPriceByPrice($carrier->id, $total_package_without_shipping_tax_inc, $id_zone, (int)$this->id_currency)
			))
				$shipping_cost += 0;
			else
			{
				if ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT) {
					foreach ($products_per_suppliers as $products_per_supplier) {
						$shipping_cost += $carrier->getDeliveryPriceByWeight($this->getTotalWeight($products_per_supplier), $id_zone);
					}
				} else { // by price
					$shipping_cost += $carrier->getDeliveryPriceByPrice($order_total, $id_zone, (int)$this->id_currency);
				}
			}
		}
		else
		{
			if ($carrier->getShippingMethod() == Carrier::SHIPPING_METHOD_WEIGHT)
				$shipping_cost += $carrier->getDeliveryPriceByWeight($this->getTotalWeight($product_list), $id_zone);
			else
				$shipping_cost += $carrier->getDeliveryPriceByPrice($order_total, $id_zone, (int)$this->id_currency);

		}
		// Adding handling charges
		if (isset($configuration['PS_SHIPPING_HANDLING']) && $carrier->shipping_handling)
			$shipping_cost += (float)$configuration['PS_SHIPPING_HANDLING'];

		// Additional Shipping Cost per product
		foreach ($products as $product)
			if (!$product['is_virtual'])
				$shipping_cost += $product['additional_shipping_cost'] * $product['cart_quantity'];

		$shipping_cost = Tools::convertPrice($shipping_cost, Currency::getCurrencyInstance((int)$this->id_currency));

		//get external shipping cost from module
		if ($carrier->shipping_external)
		{
			$module_name = $carrier->external_module_name;
			$module = Module::getInstanceByName($module_name);

			if (Validate::isLoadedObject($module))
			{
				if (array_key_exists('id_carrier', $module))
					$module->id_carrier = $carrier->id;
				if ($carrier->need_range)
					if (method_exists($module, 'getPackageShippingCost'))
						$shipping_cost = $module->getPackageShippingCost($this, $shipping_cost, $products);
					else
						$shipping_cost = $module->getOrderShippingCost($this, $shipping_cost);
				else
					$shipping_cost = $module->getOrderShippingCostExternal($this);

				// Check if carrier is available
				if ($shipping_cost === false)
				{
					Cache::store($cache_id, false);
					return false;
				}
			}
			else
			{
				Cache::store($cache_id, false);
				return false;
			}
		}

		// Apply tax
		if ($use_tax && isset($carrier_tax))
			$shipping_cost *= 1 + ($carrier_tax / 100);

		$shipping_cost = (float)Tools::ps_round((float)$shipping_cost, 2);
		Cache::store($cache_id, $shipping_cost);

		return $shipping_cost;
	}

	/**
	* Return cart weight
	* @return float Cart weight
	*/
	public function getTotalWeight($products = null)
	{
		if (!is_null($products))
		{
			$total_weight = 0;
			
			$suppliers = Array();
			$weight_per_supplier = Array();
			$products_per_suppliers = Array();
			
			foreach ($products as $product)
			{
				$product_supplier = $product['id_supplier'];
				if (!in_array($product_supplier, $suppliers)) {
					$suppliers[] = $product_supplier;
				}
				$products_per_suppliers[$product_supplier][] = $product;

				if (!isset($product['weight_attribute']) || is_null($product['weight_attribute']))
					$weight_per_supplier[$product_supplier] += $product['weight'] * $product['cart_quantity'];
				else
					$weight_per_supplier[$product_supplier] += $product['weight_attribute'] * $product['cart_quantity'];
			}
			$total_weight = max($weight_per_supplier);
			return $total_weight;
		}

		if (!isset(self::$_totalWeight[$this->id]))
		{
			if (Combination::isFeatureActive())
				$weight_product_with_attribute = Db::getInstance()->getValue('
				SELECT SUM((p.`weight` + pa.`weight`) * cp.`quantity`) as nb
				FROM `'._DB_PREFIX_.'cart_product` cp
				LEFT JOIN `'._DB_PREFIX_.'product` p ON (cp.`id_product` = p.`id_product`)
				LEFT JOIN `'._DB_PREFIX_.'product_attribute` pa ON (cp.`id_product_attribute` = pa.`id_product_attribute`)
				WHERE (cp.`id_product_attribute` IS NOT NULL AND cp.`id_product_attribute` != 0)
				AND cp.`id_cart` = '.(int)$this->id.' GROUP BY p.`id_supplier` ORDER BY nb DESC LIMIT 1');
			else
				$weight_product_with_attribute = 0;

			$weight_product_without_attribute = Db::getInstance()->getValue('
			SELECT SUM(p.`weight` * cp.`quantity`) as nb
			FROM `'._DB_PREFIX_.'cart_product` cp
			LEFT JOIN `'._DB_PREFIX_.'product` p ON (cp.`id_product` = p.`id_product`)
			WHERE (cp.`id_product_attribute` IS NULL OR cp.`id_product_attribute` = 0)
			AND cp.`id_cart` = '.(int)$this->id.' GROUP BY p.`id_supplier` ORDER BY nb DESC LIMIT 1');
			
			self::$_totalWeight[$this->id] = round((float)$weight_product_with_attribute + (float)$weight_product_without_attribute, 3);
		}
		return self::$_totalWeight[$this->id];
	}

}
?>

This code (https://gist.github.com/rubensaid/ec7512dbdc6fd289cd77) was based on mpampols's code (https://gist.github.com/mpampols/26d5780a794e698a5e12)

 

Hope you find this an util complement for you shop.

Cart.php

Edited by comprope (see edit history)

Share this post


Link to post
Share on other sites

I think Screenshots wouldn't help make this clear so I will make an example, please read carefully.

 

Let's sey we have 4 products in our store:

 

Products

Product1, which costs 20.00 USD and weights 3 lbs, its suppplied by Supplier1

Product2, which costs 10.00 USD and weights 2 lbs, its suppplied by Supplier2

Product3, which costs 50.00 USD and weights 8 lbs, its suppplied by Supplier3

Product4, which costs 5.00 USD and weights 0.5 lbs, its suppplied by Supplier1

 

And three suppliers called Supplier1, Supplier2 and Supplier3.

 

Besides, we have two carriers

 

Carriers

Carrier1 can transport products up to 4 lbs and costs 5 USD flat.

Carrier2 can transport products up to 9 lbs and costs 8 USD flat.

 

Once we have install this overrides we could have this behavior:

 

Cart combination #1

Product1 + Product2

- Total products cost: 30.00 USD

- Total products weight: 5 lbs (3 + 2) but max weight per supplier is 3 lbs max(3,2)

So available carriers for this cart are Carrier1 and Carrier2 - if use default PS behavior, just Carrier2 would be available since weight value to filter would be 5 lbs instead of 3 lbs

Cost for Carrier1 is 10 USD (5 + 5) and 16 USD (8 + 8) for Carrier2 - if use default PS behavior, cost for Carrier1 would be 5 USD and Carrier2 8 USD because it wouldn't care these products belongs to different suppliers

 

Cart combination #2

Product1 + Product3 + Product4

- Total products cost: 75.00 USD

- Total products weight: 11.5 lbs (3 + 8 + 0.5) but max weight per supplier is 8 lbs max((3+0.5),8) - because Product1 and Product4 belongs to same supplier

So available carrier for this cart is Carrier2 - if use default PS behavior, no carrier would be available

Cost for Carrier2 is 16 USD (8 + 8) - if use default PS behavior and imaging that Carrier2 behavior is set to apply max cost if not in rage, cost would be just 8 USD

 

Since you have to choose just one carrier for order, same price would be apply to each product line unless you configure ranges.

 

Hope it is now clear what this override does.

 

Regards.

Ruben Felix

Lima, Peru

Edited by rubensaid (see edit history)
  • Like 1

Share this post


Link to post
Share on other sites

Ok,

I copied your code in a .php file add "Cart.php"

I put in: / override / classes

I cleared the cache

I create a CART with 2 products of two different supplier, but it did not change anything.

I did some bad things?

thank you a lot

Share this post


Link to post
Share on other sites

Checklist:

1. Check your products has values for weight in your BO

2. Check configuration for your carries. What is their behavior for out of range?

3. Make sure you delete cache/class_index.php after create override/classes/Cart.php

 

I've made test here:

http://dev.compro.pe/index.php?product_rewrite=printed-chiffon-dress&controller=product&id_lang=1

http://dev.compro.pe/index.php?product_rewrite=blouse&controller=product&id_lang=1

These two products belongs to different suppliers.

Share this post


Link to post
Share on other sites

  • 1 month later...

hello,

 

I have test on your site, and i have no see?

I haven't been using it on my site. But I will be use finally.

I just developed code becasue I though I would use it but finally desist.. however, now I have to use it .. LOL :P

Share this post


Link to post
Share on other sites

  • 2 weeks later...
  • 1 year later...
  • 3 years later...

Hi!

The latest solution of the warehouses works for me, BUT - there is no carrier name displayed, just the amount that customer will pay.

Does someone has the tip how to solve this?
thanx!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...

Important Information

Cookies ensure the smooth running of our services. Using these, you accept the use of cookies. Learn More