Stop multishipping from splitting orders in magento - php

The issue:
I need to stop the multishipping core module from splitting quantities into separate orders. I almost need it to act like Cart page, where I update quantity and it won't split them. I have a customer that may order 100 items (of the same thing) for one store and 20 for another. That then creates 120 separate orders and 120 lines to go through and add addresses.
I have searched everywhere and haven't found much help. Below I believe is the code that is associated with the splitting (core/Mage/Checkout/Model/Type/Multishipping). Also attached is what the issue is. Any direction is much appreciated.
ver 1.7
protected function _addShippingItem($quoteItemId, $data)
{
$qty = isset($data['qty']) ? (int) $data['qty'] : 1;
//$qty = $qty > 0 ? $qty : 1;
$addressId = isset($data['address']) ? $data['address'] : false;
$quoteItem = $this->getQuote()->getItemById($quoteItemId);
if ($addressId && $quoteItem) {
/**
* Skip item processing if qty 0
*/
if ($qty === 0) {
return $this;
}
$quoteItem->setMultishippingQty((int)$quoteItem->getMultishippingQty()+$qty);
$quoteItem->setQty($quoteItem->getMultishippingQty());
$address = $this->getCustomer()->getAddressById($addressId);
if ($address->getId()) {
if (!$quoteAddress = $this->getQuote()->getShippingAddressByCustomerAddressId($address->getId())) {
$quoteAddress = Mage::getModel('sales/quote_address')->importCustomerAddress($address);
$this->getQuote()->addShippingAddress($quoteAddress);
}
$quoteAddress = $this->getQuote()->getShippingAddressByCustomerAddressId($address->getId());
if ($quoteAddressItem = $quoteAddress->getItemByQuoteItemId($quoteItemId)) {
$quoteAddressItem->setQty((int)($quoteAddressItem->getQty()+$qty));
} else {
$quoteAddress->addItem($quoteItem, $qty);
}
/**
* Require shiping rate recollect
*/
$quoteAddress->setCollectShippingRates((boolean) $this->getCollectRatesFlag());
}
}
return $this;
}

So if anyone else has this issue I was able to figure it out. I was in the wrong function.
getQuoteShippingAddressesItems()
Located in core/Mage/Checkout/Model/Type/Multishipping.php around line 116.
Changed the following code:
for ($i = 0, $n = $item->getQty(); $i < $n; $i++)
To
for ($i = 0, $n = 1; $i < $n; $i++)
$addressItem->setQty($item->getQty)

Related

Modify WooCommerce's API REST response only for specific client/key

I have successfully created a code to modify the Woocommerce's REST API response for order requests. It converts our store's other currency (CLP) to USD.
The problem is that I need to use this modification only for a specific 3rd party API client/key.
I would be grateful if you could guide me or help me to achieve it.
Below are my snippets, both for WC Rest API V2/3 and Legacy.
add_filter('woocommerce_rest_prepare_shop_order_object', 'filter_order_response', 10, 3);
function filter_order_response($response, $post, $request){
if ($response->data["currency"] == 'CLP') {
$ex = 812;
$response->data["currency"] = 'USD';
$response->data["total"] = round($response->data["total"]/$ex,1);
$response->data["discount_total"] = round($response->data["discount_total"]/$ex,1);
for($i = 0; $i < count($response->data['line_items']); $i++)
{
$response->data["line_items"][$i]["total"] = round($response->data["line_items"][$i]["total"]/$ex,1);
$response->data["line_items"][$i]["subtotal"] = round($response->data["line_items"][$i]["subtotal"]/$ex,1);
$response->data["line_items"][$i]["price"] = round($response->data["line_items"][$i]["price"]/$ex,1);
}
for($i = 0; $i < count($response->data['coupon_lines']); $i++)
{
$response->data["coupon_lines"][$i]["discount"] = round($response->data["coupon_lines"][$i]["discount"]/$ex,1);
}
for($i = 0; $i < count($response->data['refunds']); $i++)
{
$response->data["refunds"][$i]["total"] = round($response->data["refunds"][$i]["total"]/$ex,1);
}
return $response;
}
else {
return $response;
}
}
Legacy WC API filter:
add_filter('woocommerce_api_order_response', 'filter_order_response_legacy', 20, 4);
function filter_order_response_legacy($order_data, $order, $fields, $server){
if ($order_data["currency"] == 'CLP') {
add_filter('woocommerce_api_customer_response', 'filter_customer_response_legacy', 10, 4);
$ex = 850;
$order_data["currency"] = 'USD';
$order_data["total"] = round($order_data["total"]/$ex,1);
$order_data["subtotal"] = round($order_data["subtotal"]/$ex,1);
$order_data["total_discount"] = round($order_data["total_discount"]/$ex,1);
for($i = 0; $i < count($order_data['line_items']); $i++)
{
$order_data["line_items"][$i]["total"] = round($order_data["line_items"][$i]["total"]/$ex,1);
$order_data["line_items"][$i]["subtotal"] = round($order_data["line_items"][$i]["subtotal"]/$ex,1);
$order_data["line_items"][$i]["price"] = round($order_data["line_items"][$i]["price"]/$ex,1);
}
for($i = 0; $i < count($order_data['coupon_lines']); $i++)
{
$order_data["coupon_lines"][$i]["amount"] = round($order_data["coupon_lines"][$i]["amount"]/$ex,1);
}
$order_data["customer"]["total_spent"] = round($order_data["customer"]["total_spent"]/$ex,1);
return $order_data;
}
else {
return $order_data;
}
}
function filter_customer_response_legacy($customer_data, $customer, $fields, $server){
$ex = 850;
$customer_data["total_spent"] = round($customer_data["total_spent"]/$ex,1);
return $customer_data;
}
You can get the current request's user ID and their consumer key by doing something like this:
add_filter( 'woocommerce_rest_prepare_shop_order_object', 'filter_order_response', 10, 3 );
function filter_order_response( $response, $order, $request ) {
global $wpdb;
$consumer_key = $request->get_param( 'oauth_consumer_key' );
$user_id = (int) $wpdb->get_var(
$wpdb->prepare(
"SELECT user_id FROM {$wpdb->prefix}woocommerce_api_keys WHERE consumer_key = %s",
wc_api_hash( $consumer_key )
)
);
// Your code goes here...
return $response;
}
My recommendation would be to check the user ID under which the REST call is being handled.
WooCommerce allows you to map a set of credentials to a specific user. You can create a dedicated user, with the appropriate role and permission, take note of its ID, then check for the user ID as follows:
if(get_current_user_id() === <THE SPECIFIC ID>) {
// Run your custom logic
}
Further suggestions
If you're running a multi-currency shop, I would also suggest to replace the check for "CLP" with "not USD". Example:
if ($response->data["currency"] != 'USD') {
// Convert the amounts
}
This would allow you to return a USD value for orders placed in any currency, not just CLP.
If there's a chance that the client will fetch the same orders more than once, it could be a good idea to save the exchange rate used to calculate the USD amounts against the order meta, then use it for future calls. Exchange rates change over time, and keeping the one used for the first reply would allow your code to return consistent values.

Laravel post data

I am adding a order on my database with diferent taxes and I was suprise with this function that work prefectly on local envoirement is not working wen I deploy the website $tva6, $tva10 and $tva20 still 0.
$tva6 = 0;
$tva10 = 0;
$tva20 = 0;
$cart = session('orderList.orderList');
foreach ($cart as $item){
if ($item['tva'] === 6){
$tva6 += ($item['price'] * $item['qty']);
}
if ($item['tva'] === 10){
$tva10 += ($item['price'] * $item['qty']);
}
if ($item['tva'] === 20){
$tva20 += ($item['price'] * $item['qty']);
}
}
$tva6 = $tva6 - ($tva6 / 1.055);
$tva10 = $tva10 - ($tva10 / 1.1);
$tva20 = $tva20 - ($tva20 / 1.2);
$total = session('orderList.deliSup') + session('DBcart.totalPrice');
$order = Order::create([
"user_id"=>Auth::id(),
"orderId"=>session('orderList.orderIdNaga'),
"orderList"=>session('orderList.orderList'),
"payMethod"=>session('orderList.payMethod'),
"utensils"=>session('orderList.utensils'),
"infoOrder"=>session('orderList.infoOrder'),
"totalPrice"=>$total,
"tva6"=>$tva6,
"tva10"=>$tva10,
"tva20"=>$tva20,
"deliSup"=>session('orderList.deliSup'),
"deliTime"=>session('orderList.deliTime'),
"deliDate"=>session('orderList.deliDate')
]);
Any idea ? thx
edit: $cart is a array with product infos
Maybe $item['tva'] is not integer, use intval for example
foreach ($cart as $item) {
if (intval($item['tva']) === 6) {
$tva6 += ($item['price'] * $item['qty']);
}
}
Try debugging $cart to see if the session is actually returning the array you expect.
Otherwise, check the script where the session is generated, as the problem may be there.
If I'm right in my thinking above. send us the script where the session is generated.

MariabDB synchronous UPDATE query

I'm working with PHP and MariaDB and I run into a problem.
I update a value to multiple rows, and then SELECT there rows to make a new calculation the data for another task.
The problem here that I get the wrong number. I guess that the MariaDB has not finished the UPDATE query, but it return the finished flag to PHP and then the PHP proceeds the SELECT query. [I just guess]
I open to any idea. If I'm wrong, please correct me.
Thank you for sharing
This is my code
$modelAdminOrderBidSys = $this->load->model('Admin\Order\BidSys');
$acceptedItem = typeCast($modelAdminOrderBidSys->getItem($cartItemId));
if (!$acceptedItem) {
return array(
'result' => 'error',
'message' => 'Cannot find item #' . $cartItemId
);
}
$acceptedItem['lastOffer'] = $acceptedItem['offer'];
$acceptedItem['accepted'] = 1;
$acceptedItem['isBot'] = 0;
$modelAdminOrderBidSys->updateItem($cartItemId, array2object($acceptedItem));
$cartItems = typeCast($modelAdminOrderBidSys->getItems($acceptedItem['cartId']));
$accepted = 1;
$total = 0;
$offer = 0;
$lastOffer = 0;
foreach($cartItems as $cartItem) {
if ((int)$cartItem['accepted'] < 1) {
$accepted = 0;
}
$total += (float)$cartItem['total'];
$offer += (float)$cartItem['offer'];
$lastOffer += (float)$cartItem['lastOffer'];
}
$postField = new \stdClass();
$postField->accepted = $accepted;
$postField->total = $total;
$postField->offer = $offer;
$postField->lastOffer = $lastOffer;
$modelAdminOrderBidSys->updateCart($acceptedItem['cartId'], $postField);
It sounds like your SELECT transaction starts before the UPDATE has committed. Try changing the transaction_isolation (in config) / tx_isolation (at runtime with SET GLOBAL) to READ-COMMITTED. Default is REPEATABLE-READ.

Weighted Load Balancing Algorithm into PHP Application

I want to resolve weighted an Adapter from an factory which could be configured by user (enable/disable and weight %).
Example:
AdapterW ≃ 20% of transaction
AdapterX ≃ 30% of transaction
AdapterY ≃ 40% of transaction
AdapterZ ≃ 10% of transaction
I can grant that all items will never sum more than one hundred (100%), but sometimes any adapter could be deactivated.
I have the following parameters:
public function handleAdapter()
{
$isWActive = (boolean)$this->_config[self::W];
$isXActive = (boolean)$this->_config[self::X];
$isYActive = (boolean)$this->_config[self::Y];
$isZActive = (boolean)$this->_config[self::Z];
$WPercentage = (int)$this->_config[self::LOAD_BALANCE_W];
$XPercentage = (int)$this->_config[self::LOAD_BALANCE_X];
$YPercentage = (int)$this->_config[self::LOAD_BALANCE_Y];
$ZPercentage = (int)$this->_config[self::LOAD_BALANCE_Z];
.
.
.
return (self::W | self::X | self::Y | self::Z);
}
How can i balance weighted between this adapters dynamically?
Edit
created a gist to a executable code: https://gist.github.com/markomafs/5d892d06d6670909f9b4
This may not be the best approach, but you can try something like this:
public function handleAdapter()
{
//an array to return the balanced entries
$balancedEntries[] = false;
//verifies which of the options are active
$isWActive = (boolean)$this->_config[self::W];
$isXActive = (boolean)$this->_config[self::X];
$isYActive = (boolean)$this->_config[self::Y];
$isZActive = (boolean)$this->_config[self::Z];
//get configured percentage of each
$WPercentage = (int)$this->_config[self::LOAD_BALANCE_W];
$XPercentage = (int)$this->_config[self::LOAD_BALANCE_X];
$YPercentage = (int)$this->_config[self::LOAD_BALANCE_Y];
$ZPercentage = (int)$this->_config[self::LOAD_BALANCE_Z];
//here you fill the array according to the proportion defined by the percentages
if ($isWActive) {
for ($i = 0; $i < $WPercentage; $i++) {
$balancedEntries[] = self::W;
}
}
if ($isXActive) {
for ($i = 0; $i < $XPercentage; $i++) {
$balancedEntries[] = self::X;
}
}
if ($isYActive) {
for ($i = 0; $i < $YPercentage; $i++) {
$balancedEntries[] = self::Y;
}
}
if ($isZActive) {
for ($i = 0; $i < $ZPercentage; $i++) {
$balancedEntries[] = self::Z;
}
}
return $balancedEntries;
}
And then, in case you want a proportion of 1 to 100 (as in percentages):
$balancedResult = $balancedEntries[array_rand($balancedEntries, 1)];
Since array_rand will return 1 key from the original array, you use it to get it's value.
Another try, this should work for your case - But it only work if you have an adapter as a single char string, this is not visible by your question.
public function handleAdapter()
{
# a map with all adapters
$map = array(
self::W => self::LOAD_BALANCE_W,
self::X => self::LOAD_BALANCE_X,
self::Y => self::LOAD_BALANCE_Y,
self::Z => self::LOAD_BALANCE_Z
);
# generate a string map with one char per percentage point
$stringMap = "";
foreach($map as $key => $value){
# skip if disabled
if(!$this->_config[$key]) continue;
# repeat the key for each percentage point
$stringMap .= str_repeat($key, (int)$this->_config[$value]);
}
# return a random string char from the map
return $stringMap[rand(0, strlen($stringMap) - 1)];
}
Edit: I've misunderstood the question, the answer is wrong.
I understand your question so that you always want to return the adapter with the lowest load to force traffic to this adapter.
public function handleAdapter()
{
$isWActive = (boolean)$this->_config[self::W];
$isXActive = (boolean)$this->_config[self::X];
$isYActive = (boolean)$this->_config[self::Y];
$isZActive = (boolean)$this->_config[self::Z];
$WPercentage = (int)$this->_config[self::LOAD_BALANCE_W];
$XPercentage = (int)$this->_config[self::LOAD_BALANCE_X];
$YPercentage = (int)$this->_config[self::LOAD_BALANCE_Y];
$ZPercentage = (int)$this->_config[self::LOAD_BALANCE_Z];
$map = array();
if($isWActive) $map[self::W] = $WPercentage;
if($isXActive) $map[self::X] = $XPercentage;
if($isYActive) $map[self::Y] = $YPercentage;
if($isZActive) $map[self::Z] = $ZPercentage;
asort($map);
return key($map);
}
Edit: Fixed wrong sort(), you need asort() to maintain the index.

Codeignighter Cart class base fee

I have a unique shop where some products incur a base fee, lets say
A photographer charges $20 for the first hour then $1 there-after.
I am passing a variable into my codeignighter cart ; so for 5 hours I would pass the variable into cart->insert();
$item['id'] = 1;
$item['qty'] = 5;
$item['base'] = 20.00;
I made some changes to the cart class so this would work and has been fine so far, what I now need and cant seem to figure this out is when there are options it considers it a different product and this fee is charged once per rowid.
I would like my class to only allow 1 charge for the item regardless of the various options.
Below are the three functions I created inside my Cart class
and I call set_base($item) in the _save_cart() function.
private function set_base($item)
{
if( $this->base_exist($item) )
{
return FALSE;
}
// Only allow the base cost for 1 row id, it doesnt matter which one, just one
$this->_base_indexes['rowid'][$item['id']] = $item['rowid'];
$this->_cart_contents['cart_total'] += $item['base'];
return TRUE;
}
private function base_exist($item)
{
if ( array_key_exists($item['id'] , $this->_base_indexes['applied']) )
{
if ( ( $item['rowid'] == $this->_base_indexes['applied'][$item['id']] ) )
{
return TRUE;
}
}
return FALSE;
}
private function base_reset()
{
$this->_base_indexes = array();
$this->_base_indexes['applied'] = array();
return $this->_base_indexes;
}
Inside _save_cart();
I call
$this->base_reset();
Inside the cart_contents() loop I have added;
if(isset($val['base']))
{
$this->set_base($val);
}
$this->_cart_contents['cart_total'] += ($val['price'] * $val['qty']);
Hope this was clear :/
OK i changed it a little bit,
the foreach loop in the save_cart function now looks like; and I can remove the three functions I had before.
foreach ($this->_cart_contents as $key => $val)
{
// We make sure the array contains the proper indexes
if ( ! is_array($val) OR ! isset($val['price']) OR ! isset($val['qty']))
{
continue;
}
if(isset($val['base']))
{
//If it doesnt exist, add the fee
if (!(isset($this->_base_indexes[$val['id']]) == $val['rowid']) )
{
$this->_base_indexes[$val['id']] = $val['rowid'];
$this->_cart_contents['cart_total'] += $val['base'];
$sub = ($this->_cart_contents[$key]['price'] * $this->_cart_contents[$key]['qty']) + $val['base'];
}
else
{
//$this->_cart_contents[$key]['base'] = 0;
$sub = ($this->_cart_contents[$key]['price'] * $this->_cart_contents[$key]['qty']);
}
}
$this->_cart_contents['cart_total'] += ($val['price'] * $val['qty']);
$this->_cart_contents['total_items'] += $val['qty'];
$this->_cart_contents[$key]['subtotal'] = $sub;
}

Categories