Edit WC shipping method on orders - php

I want to simply change the shipping method of an order on Woocommerce.
I have seen a few posts on editing shipping methods on orders but I'm not 100% clear on the exact protocol of how it works, I have a rough idea but no success yet! If you can help me to get the order to update its shipping method that would be awesome! (this is the middle of a large project so I can't share the whole plugin, let me know if you need anymore details happy to provide anything that's necessary!)
I have this small bit of code that returns me a list of orders that need shipping method updated and the new shipping method rateID that it has to be changed to, that all works fine but i have included it so you can see where it comes from.
Once the array of orders to be updated has been constructed, it then needs to be processed using the set_shipping() method. One at a time working through the array items and updating the shipping method of each, from the original to the new one provided in the toUpdate array
UPDATED: i have tried using the WC_API_Orders() class as set_shipping is a method of that. still coming up with the unrecognised error.
'Class 'Inc\Api\Callbacks\WC_API_Orders' not found'
private function updateRound()
{
if (! empty($_POST['update'])) {
$updateorderID= $_POST['orderID']; //get list of orderID to change
$updateRound= $_POST['newRound']; //list of rounds new orders want swapped to
$updateList= $_POST['update']; //uses a check box to confirm user wants to update row (stops accidentally updating the whole list)
// print_r($_POST['update']); //shows an output to check results.
$toUpdate = []; //this is the array for all the shippingmmethods to update on set order etc.
//make list of order ID's to update and their new shipping method
foreach ($updateList as $key => $value) {
$toUpdate[] = [
'orderID' => $key,
'newRound' => $updateRound[$key]
];
}
}
$absOrder = new WC_API_Orders();
foreach ($toUpdate as $updateOrder) {
//echo "</br>OrderID: ".$updateOrder['orderID']." Round updated to ".$updateOrder['newRound'];
echo "order ".$updateOrder['orderID']."updating";
$absOrder->wc_get_order($updateOrder['orderID']);
echo " order retreived";
$absOrder->set_shipping($order, $updateOrder['newRound'], 'update' );
echo "updated </br>";
}
}
This is for a very local veg delivery scheme, people can only place an order if their postcode is in the set group, so that's not so important, this shipping method we use to change from 'wed round' to 'thurs round' etc. Simple really no concern for delivery companies etc, its all in house. Tax etc. doesn't matter, it's all done elsewhere, this is simply meant to be a way of editing orders onto delivery rounds.

Related

Understanding magento sales_flat_order table

I am trying to understand the use of different columns present in magento sales_flat_order table..
this is what i have got so far...
protect_code
used to ensure the order being loaded is the correct one for the guest's cookie value, but not sure why magento sets this field for even logged in users..
magento table "sales_flat_order" field "protect_code" explanation
Cancelled, Invoice, Refunded ...
discount_canceled, discount_invoiced, discount_refunded
shipping_amount, shipping_canceled, shipping_invoiced, shipping_refunded
shipping_tax_amount, shipping_tax_refunded
subtotal_canceled.....
tax_amount.....
total_canceled....
then again repeated for base_discount_canceled...
I already know everything with base has to do with base currency and the other is to with store currency..
Not able to understand need of so many columns discount_canceled, discount_invoiced, discount_refunded
Magento can issue partial refunds, so can understand for refunded..
but for invoice, canceled it can rather have only one field
eg. invoice = true and use grandtotal, basegrandtotal fields ..
if this was a design issue than why this is present even in magento 2?
If i am missing some feature of magento which requires this, then i am more than interested to know about it ..
edit_increment
edit_increment
relation_child_id, relation_child_real_id, relation_parent_id, relation_parent_real_id
I found this in app/code/core/Mage/Adminhtml/Model/Sales/Order/Create.php
public function createOrder()
..
..
$oldOrder = $this->getSession()->getOrder();
$originalId = $oldOrder->getOriginalIncrementId();
if (!$originalId) {
$originalId = $oldOrder->getIncrementId();
}
$orderData = array(
'original_increment_id' => $originalId,
'relation_parent_id' => $oldOrder->getId(),
'relation_parent_real_id' => $oldOrder->getIncrementId(),
'edit_increment' => $oldOrder->getEditIncrement()+1,
'increment_id' => $originalId.'-'.($oldOrder->getEditIncrement()+1)
);
..
..
$this->getSession()->getOrder()->setRelationChildId($order->getId());
$this->getSession()->getOrder()->setRelationChildRealId($order->getIncrementId());
But i am not sure , for what condition this code is executed
Some old orders in the database are having edit_increment as 1....
Few other fields..
adjustment_negative , adjustment_positive , ext_customer_id , ext_order_id
P.S- Using magento enterprise..

Adding products that already in $_SESSION array

I have a tiny problem with adding products into global array. This may be used in shop cart. Here is the part of code that we focus in:
if ( isset($_POST['id']) ){ // If the product is adding to cart. This product Id is sended via form.
$productid = mysql_real_escape_string($_POST['id']);
$cartproduct = mysql_query("select * from stuff where id = '$productid'");
$Addrow=mysql_fetch_assoc($cartproduct);
if ($Addrow['qty']<$_POST['qty']){ // the product quantity that will add to cart can't be greater than in database
$_POST['qty']=$Addrow['qty'];
}
$new_product = array(array('name'=>$Addrow['name'], 'id'=>$Addrow['id'], 'price'=>$Addrow['price'], 'qty'=>$_POST['qty'])); // Creating new product info in array
if (isset($_SESSION['cart'])){ // If the cart exist
foreach ($_SESSION['cart'] as $add_product){
if ($add_product['id']==$_POST['id']){ // checking that product is already in $_SESSION
$exist = TRUE;
}else{
$exist = FALSE;
}
}
if ($exist == TRUE){ // If The product is in the $_SESSION: Update amount
// I dont have code for it.
}else{ // The product is not in array, add it.
$_SESSION["cart"] = array_merge($_SESSION["cart"], $new_product);
}
}else{ // If the cart is not exist
$_SESSION['cart']=$new_product;
}
}
And the problem is when I try to add the product that already in array. The function is adding it as new product...
The second problem is with remove these products. I can't do this using this:
foreach ($_SESSION['cart'] as $remove){
if($_GET["id"] == $remove['id']){
unset($_SESSION["cart"][$remove]);
}
}
Anyone can help to solve it?
I would suggest to change the array a bit. Inside 'cart', use the product id as a key for the products. That way, you can easily find and update products in the array.
You can just change the cart array in the session. Because keys are unique in the array, setting a value for the key will overwrite the previous one.
So I've added a slightly modified version of your inner piece of code. It performs three steps:
Add the post variables to normal variables. I find this easier to work with, and you can do all kinds of other checks before continuing (like checking if quantity > 0 etc).
Get the existing product from the array or initialize a new product. This uses array_key_exists, because I think that's the purest check, but people also use isset($_SESSION['cart'][$productId]), which should also work. Anyway, such a check is better (faster, easier) than using a loop, but it will only work if you switch to using product ids for the keys.
Simply set or update the quantity and write the updated prodct back into the array. If the product existed before, it will just overwrite the previous value.
Code becomes:
// Use a variable. It's easier and more readable.
$productId = $_POST['id'];
$quantity = $_POST['qty'];
// Your other checks go here. Left out for brevity.
// Get the current product from the cart, if it exists.
// If not, create a new product.
if (array_key_exists($productId, $_SESSION['cart'])) {
// Product found, get it and update its quantity.
$product = $_SESSION['cart'][$productId];
$product['qty'] += $quantity;
} else {
// Product not found. Initialize a new one.
$product = array(
'name' => $Addrow['name'],
'id' => $Addrow['id'],
'price' => $Addrow['price'],
'qty' => $quantity);
}
// Write updated or new product back to the array, and use the product id as key.
$_SESSION['cart'][$productId] = $product;
Some other tips:
Don't use the mysql_* functions if you have the opportunity to switch to mysqli or PDO. The mysql functions are deprecated.
Make sure to check the query result. Maybe something went wrong (or someone forged a request), and the product id cannot be found in the database. In that case, $Addrow will probably be false or null. Make sure to check for this and display an appropriate error instead of updating cart, possibly corrupting your cart.
If the quantity cannot be added, I wouldn't silently lower the quantity, because the user will think they found a bug. Instead, clearly state that such a quantity is not available.
And you may want to reconsider that. After all, maybe other stock will be delivered today, or other people might order the last item simultaneously. So it's better to check it later, when the order is well saved and you want to process it.
Showing information about available quantities in the cart will give your competition insight in the amount of stock you have, from which they can deduce other information too. Also, they may even place fake orders to make products unavailable on your website. It's a dog-eat-dog world. Be careful what information you show.

Bigcommerce API Update Inventory Levels (both normal inventory tracking and tracking by options)

Update 11/11/13:
After doing some testing I'm running into some more problems using the bigcommerce php api. Searching a sku's property works fine for SKU's that are attached to options, but updating them I seem to be running into some problems...
Here is a sample snippet:
$filter = array('sku' => '230000120078');
$skus = Bigcommerce::getSkus($filter);
foreach($skus as $sku){
$options = array('inventory_level' => 3);
Bigcommerce::updateSku($sku->id, $options);
}
I must be missing something but I can't find to much documentation online. BigCommerce Documentation for updating sku makes it seem as if I have this right, but it doesn't seem to be working. Any suggestions?
Original Post:
This is more of a general question before I look to deep into solutions...
I read this post about updating inventory levels by SKUs and the answer suggests you can only update levels by product ids. Does this mean if you have a store with some products that track inventory by option sets (with SKUs attached to each option) you cannot update the inventory levels for each of these options?
Currently I just wrote a script that connects to our RFID database and grabs the current inventory levels and throws them into a csv file which I then upload every morning, but I'm trying to fully automate the process so I don't have to worry about selling out of items in between the 24 hours of new inventory.
Does anyone have any experience with this using the Bigcommerce api?
So...
This requires a hybrid solution; if you look at the quick start guide (not very helpful) you'll find this
Currently, there are two ways to search for SKUs - GET
/products?sku="something" or GET /products/skus?sku="something". The
first call only returns product level SKU and not option level SKUs.
What they neglect to mention is that to get the product level SKU, the only method that works is the top one.
My solution was to include another function in the library that adjusted the path to just /products?skup=xxxxx
public static function getSkuTop($filter = false)
{
$filter = Filter::create($filter);
return self::getCollection('/products' . $filter->toQuery(), 'Sku');
}
and in my call I have a few simple bits of logic
$filter = array('sku' => '10026');
$skus = Bigcommerce::getSkus($filter);
if(is_array($skus)){
foreach($skus as $sku){
$sku->inventory_level = 27;
$sku->update();
}
} else {
$skus = Bigcommerce::getSkuTop($filter);
if(is_array($skus)){
foreach($skus as $sku){
$options = array('inventory_level' => 28);
Bigcommerce::updateProduct($sku->id, $options);
}
}
}
May not be so great, but it's the only way I can get it to work!
Yes, I have been working on that same issue and the solution I came up with was to check if the SKU on the product level is blank. If so, the the inventory_level is managed by in the SKU resources underneath the product.
As far as actually updating the SKU I figured out the easiest way and that is the following. Instead of trying to run the updateSku function I do this...
$filter = array('sku' => '230000120078');
$skus = Bigcommerce::getSkus($filter);
foreach($skus as $sku){
$sku->inventory_level = 4;
$sku->update();
}
This does update the inventory for the associated sku.
As far as Paul's response, he is absolutely correct, and it lead me to this answer.

Magento - Insufficient Stock Notification on Product Page

I am using Magento 1.7.0.2. Whilst on the product page, if a customer attempts to add a quantity greater than we have in stock they receive a message stating ".. the requested quantity is not available".
Is there any way for magento to either email or log when this occurs? I.e. I receive an automatic email stating a customer has attempted to add X number of item X? This would allow me to identify lost sales due to us not having enough stock of a particular item?
Has anyone come across anything like this before or is this even possible?
Thank you in advance
Mike Prentice
yes this is possible
You have to code for this.
I came across this problem one time and what i have do like this below.
I have make one observer event to check if customer is requesting quantity more then available if so i sent email to admin.
What you can do is create one observer for chekout_cart_add_before event in this event you can put your logic.
Or otherwise you can use magento feature Backorders you can find this in inventory tab,if you enable this then customer can order even requested quantity > available quantity, customer can see one message in cart page about backorder.
There is no standart functionality to notify about low quantity products by email.
But there is RSS notification http://www.magentocommerce.com/wiki/modules_reference/english/mage_adminhtml/system_config/edit/cataloginventory
Extend this functionality to match your needs.
You could write some script which would parse RSS, and send email etc.
EDIT
Here is some extension you may like http://www.magentocommerce.com/magento-connect/low-stock-email-notification.html
But is is not free.
Here's how I've done it so that it sends a google analytics tracking event whenever a customer tries to order more than the available stock level.
First copy: app/code/core/Mage/CatalogInventory/Model/Stock/Item.php
To: app/code/local/Mage/CatalogInventory/Model/Stock/Item.php
so that you're not modifying a core file.
In app/code/local/Mage/CatalogInventory/Model/Stock/Item.php add this function
public function notifyOutOfStock($productId){
$session = Mage::getSingleton('checkout/session');
//Initialise as empty array, or use existing session data
$outOfStockItems = array();
if ($session->getOutOfStock()){
$outOfStockItems = $session->getOutOfStock();
}
try {
$product = Mage::getModel('catalog/product')->load($productId);
$sku = $product->getSKu();
if($sku){
//Add the current sku to our out of stock items (if not already there)
if(! isset($outOfStockItems[$sku]) ) {
$outOfStockItems[$sku] = 0;
}
}
} catch (Exception $e){
//Log your error
}
Mage::getSingleton('checkout/session')->setOutOfStock($outOfStockItems);
}
In that same file is another function called checkQuoteItemQty.
Inside that function you need to call your new function using $this->notifyOutOfStock($this->getProductId()); right after it sets each of the error messages and before the return statement.
So:
public function checkQuoteItemQty($qty, $summaryQty, $origQty = 0)
{
....
if ($this->getMinSaleQty() && ($qty) < $this->getMinSaleQty()) {
$result->setHasError(true)
->setMessage(
$_helper->__('The minimum quantity allowed for purchase is %s.', $this->getMinSaleQty() * 1)
)
->setQuoteMessage($_helper->__('Some of the products cannot be ordered in requested quantity.'))
->setQuoteMessageIndex('qty');
//** Call to new function **
$this->notifyOutOfStock($this->getProductId());
return $result;
}
.....
->setQuoteMessageIndex('qty');
//** Call to new function **
$this->notifyOutOfStock($this->getProductId());
return $result;
.....
What this does is add your product sku to an array in the checkout session.
This means you will have access to that info in the template file right after your page loads displaying the "Insufficient stock" notification.
So in one of your template files you can add some code to render the necessary JavaScript.
I've chosen header.phtml since it loads on every page. (Users can add quantities of items to the cart in the cart page as well as the product view page).
app/design/frontend/CUSTOMNAME/default/template/page/html/header.phtml
Somewhere down the bottom of the code add this:
<!-- GA tracking for out of stock items -->
<script>
try {
<?php
$session = Mage::getSingleton('checkout/session');
if ($session->getOutOfStock()){
$outOfStockItems = $session->getOutOfStock();
foreach($outOfStockItems as $sku=>$value) {
if($value==0){
//Render the GA tracking code
echo "_gaq.push(['_trackEvent', 'AddToCart', 'ProductQtyNotAvailable', '".$sku."']); \r\n";
//Set it to 1 so we know not to track it again this session
$outOfStockItems[$sku] = 1;
}
}
//Update the main session
Mage::getSingleton('checkout/session')->setOutOfStock($outOfStockItems);
}
?>
}
catch(err) {
//console.log(err.message);
}
</script>
Can confirm this works well and in my opinion is better than an email or RSS feed as you can analyse it along with the rest of your analytics.

Odd behavior when saving a product model that has tier pricing from a script in Magento

I am finding some odd behavior when I try to save a product model from a script it Magento Enterprise 1.8 that has tier pricing.
Take this code for example:
// This product has a tier price
$product = Mage::getModel('catalog/product')->load(194760);
$product->setName('Changed Product Title');
$product->save();
When saving a get an exception (detailed below). However if I change nothing in the model, I do not get the exception. I have a feeling that this is due to the fact that I did not update anything so Magento does not do as much work.
// Same product, but I changed nothing and it works
$product = Mage::getModel('catalog/product')->load(194760);
$product->save();
The odd part is that I am able to save the product successfully if I am setting or modifying tier price information (pending I do not create anything that is duplicated)
// This works pending the tier price does not already exist
$mud_array = array();
$mud_array[] = array(
'website_id' => 0,
'cust_group' => 32000,
'price_qty' => 5,
'price' => 6
);
$product = Mage::getModel('catalog/product')->load(194760);
$product->setTierPrice($mud_array);
$product->save();
The exception that I am seeing is as follows:
Fatal error: Uncaught exception 'Mage_Eav_Model_Entity_Attribute_Exception' with message 'SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '194760-1-0-5.0000-0' for key 'UNQ_CATALOG_PRODUCT_TIER_PRICE'' in /path/to/magento/app/code/core/Mage/Eav/Model/Entity/Abstract.php:61
So it appears that when a tier price exists in a product, and you try to change anything inside the model, it will attempt to reinsert all the tier price information.
Has anyone seen this before? Is there a way to work around this? Thanks for any help you can provide.
I had the same problem with EE 1.12, but with the "group_price" attribute. Just managed to fix the error by adding at the begging of my script
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
Ok so I created a fix for this that seems to work.
In /app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Attribute/Backend/Tierprice.php there is a function called savePriceData. From what I can gather this function is responsible for determining if we need to update or insert a tier price.
There is an if block that makes this decision to update or insert based on the information coming from the $priceObject that is passed to the function.
If you are updating a tier price from the admin panel, priceObject has two values value_id and value. These fields both point to columns in the catalog_product_entity_tier_price table. This is what happens in the first part of the if block
If you are creating a new tier price from the admin panel, priceObject has all the columns from the catalog_product_entity_tier_price table (entity_id, all_groups, customer_group_id, qty, value, and website_id). Magento then takes this information and inserts it into the catalog_product_entity_tier_price table.
The actual problem was that when you save a product from a script (aka outside the admin panel) you ALWAYS get a priceObject that contains all the information for an insert. So it will always try to do the insert into the table which if it already exists causes an integrity constraint violation.
So the fix I put together for this was really quite simple. In the second part of the if block, I am doing a check to see if the tier price already is in the table and if it is, I simply do not do the insert. Here is the code:
... first part of if statement
// somehow we need to have this not happen if it already exists
$reader = $this->_getReadAdapter();
$sql = "SELECT * FROM catalog_product_entity_tier_price WHERE ";
foreach($data as $index => $value) {
$sql .= $index . ' = ' . $value . ' AND ';
}
$sql = substr($sql, 0, -4);
$search = $reader->fetchAll($sql);
if(count($search) > 0) {
// It already exists, don't do anything
} else {
$adapter->insert($this->getMainTable(), $data);
}
Obviously, you really should make sure that you copy this file to local before you make this change (or probably better yet, rewrite it in a module). This probably is not the most 100% correct "Magento way" to do this, but I really needed to get a solution for this ASAP.
I am open to better solutions than this, but I wanted to share what I found
(Note: As I am on the Enterprise Edition of Magento, I did not want to share anymore of the source than necessary since it could be a violation of our license agreement)

Categories