Creating Invoice & Capturing on Shipment - api
We've got some API integrations that will periodically create shipments for orders.
What I'd like to do is create an observer to also create an appropriate invoice & capture payment when this shipment is created. I have this tied to sales_order_shipment_save_after:
public function autoInvoice($observer){
$shipment = $observer->getEvent()->getShipment();
$order = $shipment->getOrder();
$items = $shipment->getItemsCollection();
$qty = array();
foreach($items as $item)
$qty[$item['order_item_id']] = $item['qty'];
$invoice = Mage::getModel('sales/order_invoice_api');
$invoiceId = $invoice->create($order->getIncrementId(), $qty);
$invoice->capture($invoiceId);
}
(The code for the actual capture is somewhat naive, but bear with me.)
What's strange is that this code works just fine -- the shipment is created, the invoice is created and marked as 'Paid.' However, the order itself stays in limbo and retains a status 'Pending.'
Looking into it further, the items on the order itself have the correct quantities for both Ordered and Shipped, but there's no listing of the quantity Invoiced. I think this is what's causing the status hangup. It's as though the qty_invoiced on the sales_order_item table is getting reverted somehow.
Again, the Invoice shows the right items, so I'm quite confused here.
Any ideas?
This is indeed a very interesting one #bahoo.
maybe try:
$shipment = $observer->getEvent()->getShipment();
$order = $shipment->getOrder();
$qty = array();
$invoice = Mage::getModel('sales/order_invoice_api');
$invoiceId = $invoice->create($order->getIncrementId(), $qty);
$invoice->capture($invoiceId);
After lots of testing using the API, I found if I created the invoice first, then the shipment, Magento Enterprise 1.13.01. would correctly set the order status to Complete. Trying to make the shipment first, then the invoice, would result in the Pending Order status remaining even if all items had been invoiced and shipped.
The bridging system code below uses information about orders placed in Magento, routed to the bridging system via an Observer on checkout_submit_all_after, sent to NetSuite via web services, and fulfilled in NetSuite. The bridging system gets sales order fulfillments from NetSuite via web service, and saves items shipped, package, and tracking information, to use in the code below.
Code shows creating invoice, then shipment and tracking.
Note that while testing, I just saw Magento incorrectly create an invoice for all three items in an order, even though it was passed an array that just contained two of the items. Magento did correctly create a shipment record for just the two items. Puzzling that the invoice API and the shipment API when passed the same array of items and quantities have such different behavior. Anyone else seen this?
$proxy = new SoapClient($proxyUrl); /* V2 version */
$sessionId = $proxy->login($apiUser, $apiKey);
try
{ /* try to create invoice in Magento */
$invoiceIncrementId = $proxy->salesOrderInvoiceCreate($sessionId, $orderIncrementId, $shipItemsQty, 'invoice created', false, false);
}
catch( SoapFault $fault )
{
$error = $fault->getMessage(); /* will return 'Cannot do invoice for order' if invoice already exists for these items */
}
if (!stristr($error,'Cannot do invoice') and !empty($error))
{ /* some other invoicing problem, log what returned, on to next order */
$ins = "insert into order_error_log values(NULL, ".$orderId.", '".date("Y-m-d H:i:s")."', '".$program."', '".$error."')";
$result = $mysqli->query($ins);
$upd = "update orders set orderStatusId = ".ERROR_ADDING_MAGENTO_INVOICE.",
dateStatusUpdated = '".date("Y-m-d H:i:s")."'
where id = ".$orderId;
$result = $mysqli->query($upd);
continue;
}
if ((stristr($error,'Cannot do invoice') or empty($error)) and $complete)
{ /* if all fulfilled, may change status */
$upd = "update orders set orderStatusId = ".STORE_INVOICE_CREATED.",
dateStatusUpdated = '".date("Y-m-d H:i:s")."'
where id = ".$orderId;
$result = $mysqli->query($upd);
}
/* send Magento salesOrderShipmentCreate and get returned shipment Id, re-using proxy login and session */
$comment = 'Fulfillment(s) shipped on: '.$netsuiteShipDate;
try
{ /* returns value such as string(9) "100002515" */
$shipmentIncrementId = $proxy->salesOrderShipmentCreate($sessionId, $orderIncrementId, $shipItemsQty, $comment);
}
catch( SoapFault $fault )
{
$error = $fault->getMessage().': SOAP error received when trying to add shipment to Magento for morocco order id '.$orderId.
' store order id '.$orderIncrementId.' with items qty array of: '.var_dump($itemsQty);
$ins = "insert into order_error_log values(NULL, ".$orderId.", '".date("Y-m-d H:i:s")."', '".$program."', '".$error."')";
$result = $mysqli->query($ins);
$upd = "update orders set orderStatusId = ".MAGENTO_SOAP_EXCEPTION.",
dateStatusUpdated = '".date("Y-m-d H:i:s")."'
where id = ".$orderId;
$result = $mysqli->query($upd);
continue; /* on to next order */
}
/* Using that shipmentId, send info re each package shipped for these fulfillments to Magento via salesOrderShipmentAddTrack. */
foreach ($packageIds as $packageId => $package)
{
try
{
$trackingNumberId = $proxy->salesOrderShipmentAddTrack($sessionId, $shipmentIncrementId,
$package['carrier'], 'tracking number', $package['trackNumber']);
}
catch( SoapFault $fault )
{
$error = $fault->getMessage().': SOAP error received when trying to add tracking number '.$package['trackNumber'].' to
Magento for morocco order id '.$orderId.' store order id '.$orderIncrementId;;
$ins = "insert into order_error_log values(NULL, ".$orderId.", '".date("Y-m-d H:i:s")."', '".$program."', '".$error."')";
$result = $mysqli->query($ins);
$upd = "update orders set orderStatusId = ".MAGENTO_SOAP_EXCEPTION.",
dateStatusUpdated = '".date("Y-m-d H:i:s")."'
where id = ".$orderId;
$result = $mysqli->query($upd);
continue;
}
}
Related
Prestashop function query to the template
I am in prestashop 1.6 and in classes/Cart.php i've tested and i can communicate with the template file. In the query that i've made to return the expect result works on phpmyadmin, and works in cart.php to tpl file call, but only when i write the id number of the customer, and i want to work with the variable id customer (something with THIS or like prestashop way of work - (int)$id_customer - ). Any help please!!!!! //code for cart.php public static function payLater($id_customer) { $sql = "SELECT ps_customer.pay_later FROM ps_customer LEFT JOIN ps_cart ON ps_customer.id_customer = ps_cart.id_customer ORDER BY ps_cart.id_cart DESC LIMIT 1 WHERE ps_cart.id_customer =".(int)$id_customer; //if i change (int)$id_customer for a real id number like 43 it shows correct info $result = Db::getInstance()->getValue($sql); return $result; } //code for template file {Cart::payLater(Tools::getvalue('pay_later'))}
{Cart::payLater($customer.id)} in tpl file. I have edited in your function also. please check below for empty customer id. public static function payLater($id_customer) { if((int)$id_customer == 0){ return; } // check if customer id is blank $sql = "SELECT ps_customer.pay_later FROM ps_customer LEFT JOIN ps_cart ON ps_customer.id_customer = ps_cart.id_customer ORDER BY ps_cart.id_cart DESC LIMIT 1 WHERE ps_cart.id_customer =".(int)$id_customer; //if i change (int)$id_customer for a real id number like 43 it shows correct info $result = Db::getInstance()->getValue($sql); return $result; }
Xero Api - Expense Mark as Paid
Using Xero.Api for Xero accounting, I am looking to update expenses from Authorised to Paid. At present, I am getting the following error: "Invalid status change. An expense claim with status 'AUTHORISED', cannot be updated to have the status 'PAID'" Is this possible to do through the API, if so what are the minimum field changes so this is processed? Many thanks. public static void SetExpensePaid(Xero.Api.Example.Applications.Private.Core api,List<Guid> guids) { var account = api.Accounts.Find(); var bank = account.Where(x => x.Name == "Bank").FirstOrDefault(); foreach (var g in guids) { var exp =api.ExpenseClaims.Find(g); var amount = exp.AmountDue; exp.AmountPaid = amount; exp.AmountDue = 0; exp.Status = Xero.Api.Core.Model.Status.ExpenseClaimStatus.Paid; Payment payment = new Payment() { Account = bank, BankAmount = (decimal?)amount, Date = DateTime.Today, IsReconciled = false, Amount = (decimal?)amount }; api.Payments.Create(payment); exp.Payments.Add(payment); api.ExpenseClaims.Update(exp); } }
For anyone else wondering, managed to get a response from Xero.... Normally, for invoices or credit notes, you need to make full payments to them using the Payments endpoint to mark them as PAID. Unfortunately, you cannot pay an expense claim via the Xero API at this time. Payment needs to be done in the Xero app. https://developer.xero.com/documentation/api/expense-claims#POST So can't be done at present.
Automatically increase stock after order - Prestashop
I manually manage my stocks on Prestashop. I am looking for a solution to automatically return the initial stock after a sales order. For example, a product is ordered in two copies with an initial stock of 7. I would like the stock to remain at 7 after the order and not at 5. Do you know a technique that would allow me to realize this automatically?
Put a Hook on Order Confirmation (displayOrderConfirmation) in a new module (you can generate one at https://validator.prestashop.com/) and check whats inside the cart then put it again in your stocks : public function hookDisplayOrderConfirmation($params) { $order = $params['order']; $cart = new Cart($order->id_cart); $products = $cart->getProducts(); foreach ($products as $product) { $removed_qty = (int) $product['quantity']; $past_qty = (int) StockAvailable::getQuantityAvailableByProduct($product['id_product'], $product['id_product_attribute']); $new_qty = $removed_qty + $past_qty; StockAvailable::setQuantity($product['id_product'], $product['id_product_attribute'], $new_qty); } }
How to access order data on Big Commerce on confirmation screen
I need to add some JavaScript to the order confirmation page that includes details about the order. Although I can access the order id through a BigCommerce global variable, I cannot work out how to get the rest of the order details into my JavaScript. For instance, I can access the BigCommerce order_id global %%GLOBAL_OrderId%% and use that in a JavaScript alert, but I also need to access the following: order total order tax order shipping order postcode And foreach product in the order product_id unit_price quantity There these global items but when I try to access them they are blank, I presume that I need to loop through the cart contents. %%GLOBAL_ProductModel%% %%GLOBAL_ProductPrice%% %%GLOBAL_ProductQty%% I have read all the docs I can find. Can anyone give me an idea of how to achieve this. I need the values so I can pass them to a third party JS function for their use. All of that is waiting and ready but I cannot get the data out of Big Commerce templating system. The data is there, on the order.html template page, as the social sharing panel reads it, but again I cannot see how the social sharing snippet is accessing it.
I created a hacky script just for you that pulls the product data (as well as some order details). It parses the data from the %%GLOBAL_ConversionCode%% template variable, and as such this script should be inserted in order.html immediately after the %%GLOBAL_ConversionCode%% variable. Specifically, %%GLOBAL_ConversionCode%% outputs to: <!-- Include the conversion tracking code for all analytics packages --> <!-- Start conversion code for analytics_googleanalytics --> <script type="text/javascript"> if(typeof(pageTracker) != 'undefined') { pageTracker._addTrans( '196', 'store-name', '0.00', '2.12', '1.92', 'Austin', 'Texas', 'United States' ); pageTracker._addItem( '196', '2004', 'TAKE YOUR TIME: Sweet Body Butter', '', '24.96', '1' ); pageTracker._trackTrans(); } </script> Solution: <script> //-------------- Main --------------// //** Create the order data array from analytics script **// var data = parseAnalyticsData(getAnalyticsScript()); //console.log(data); /** * Retrieve the order details as an object, properties are: * id - The order ID. * shipping - The order shipping cost. * tax - The order tax cost. * shippingTax - The order shipping tax cost. * city - The order shipping city. * state - The order shipping state. * country - The order shipping country. */ var orderDetails = getOrderDetails(data); console.log("Order ID = %d", orderDetails.id); console.log("Order shipping city = %s", orderDetails.city); console.log("Order subtotal = %f", orderDetails.subtotal); /** * Retrieve the order product details, as an array of product objects. * Properties are: * id - The product ID. * description - The product description. * tax - The product tax cost. * price - The product price per product. * qty - The product quantity purchased. */ var products = getOrderProducts(data); //** Loop through the products array to access each product **// console.log("Total number of products = %d", products.length); for (x=0; x<products.length; x++) { console.log("--------"); console.log("Item # ", x+1); console.log("Product ID = %f", products[x].id); console.log("Product QTY = %f", products[x].qty); console.log("Product Price = %f", products[x].price); console.log("--------"); } //-------------- Functions --------------// /** * Parses the DOM to retrieve the order data analytics script. */ function getAnalyticsScript() { var scripts = document.getElementsByTagName('script'); var thisScriptTag = scripts[scripts.length - 2]; var data = thisScriptTag.textContent || thisScriptTag.innerText; return data; } /** * Parses the raw analytics script element to remove all script * text, and parse just the order related data into an array. * #param script <String> - The raw order analytics script. * #return <mixed> - Array containing the order data. */ function parseAnalyticsData(data) { String.prototype.replaceAll = function(search, replacement) { var target = this; return target.split(search).join(replacement); }; // This is hacky, and probably inefficient, but it removes all // script related text, so the end result is just a comma separated // array of the order and product data. data = data.replace("if(typeof(pageTracker) != 'undefined') {", ''); data = data.replaceAll( 'pageTracker._addTrans(', ''); data = data.replaceAll( ' pageTracker._trackTrans();', ''); data = data.replaceAll( 'pageTracker._addItem(', ''); data = data.replaceAll(');', ''); data = data.replace('}', ''); data = data.replace( /\n/g, ",").replaceAll( ",,",","); data = data.replace(/\s/g,''); data = data.split(','); data = cleanArray(data); // Remove all empty values from array. return data; } /** * Removes all empty data from array. * #param array <mixed> - The array to clean. */ function cleanArray(array) { var newArray = new Array(); for (var i = 0; i < array.length; i++) { if (array[i]) { newArray.push(array[i]); } } return newArray; } /** * Parse Analytics Data for Order Details * #param data <mixed> - The order analytics data. * #return <mixed> - Object containing the order details. */ function getOrderDetails(data) { String.prototype.replaceAll = function(search, replacement) { var target = this; return target.split(search).join(replacement); }; return { id : parseFloat(data[0].replaceAll("'",'')), subtotal : ( parseFloat(data[2].replaceAll("'",'')) - (parseFloat(data[3].replaceAll("'",'')) + parseFloat(data[4].replaceAll("'",'')) ) ), total : parseFloat(data[2].replaceAll("'",'')), tax : parseFloat(data[3].replaceAll("'",'')), shipping : parseFloat(data[4].replaceAll("'",'')), city : data[5].replaceAll("'",''), state : data[6].replaceAll("'",''), country : data[7].replaceAll("'",'') } } /** * Parse Analytics Data for All Order Product Details. * #param data <mixed> - The order analytics data. * #return <mixed> - Array containing individual product details. */ function getOrderProducts(data) { String.prototype.replaceAll = function(search, replacement) { var target = this; return target.split(search).join(replacement); }; var counter = -1; // Keep index of details per product. var productsArray = []; // Init empty array to hold all products. var product = {}; // Init empty object to hold single product data. //** Product data starts at index 8 **// for (x=8; x<data.length; x++) { counter++; switch (counter) { case 1: product.id = parseFloat(data[x].replaceAll("'",'')); break; case 2: product.description = data[x].replaceAll("'",''); break; case 3: product.tax = parseFloat(data[x].replaceAll("'",'')); break; case 4: product.price = parseFloat(data[x].replaceAll("'",'')); break; case 5: product.qty = parseFloat(data[x].replaceAll("'",'')); counter = -1; // reset counter productsArray.push(product); // push product to products array product = {}; break; } } return productsArray; } </script>
how to update(subtract) the value of database after customer order ASP.NET
i have an add product function and it registers well in my database. it looks like this after that, customer orders and what i have currently is that it only updates the label (available stock) not in the database. here is my code for that after button click: DataTable dtResult = k.SaveCustomerDetails(); for (int i = 0; i < dt.Rows.Count; i++) // loop on how many products are added by the user { ShoppingCart SaveProducts = new ShoppingCart() { CustomerID = Convert.ToInt32(dtResult.Rows[0][0]), ProductID = Convert.ToInt32(dt.Rows[i]["ProductID"]), TotalProducts = Convert.ToInt32(dt.Rows[i]["ProductQuantity"]), //subtracts the product quantity depending //on the customer order }; SaveProducts.SaveCustomerProducts(); } var customerId = Convert.ToInt32(dtResult.Rows[0][0]); Response.Redirect(String.Format("OrderSummary.aspx?Id={0}", customerId.ToString())); what I want to happen is to update the product quantity as well in the database? any idea on how to do it? please and thank you sirs