magento - shipping to multiple addresses but keeping payment to one charge - php

I'm trying to find a way to allow an order to be split for multiple shipping addresses, but to keep the charge to the customer as a one time payment rather than having to charge for each shipment separately. Is there a way to do this? Currently, customers are getting charged multiple times, corresponding to their "multiple orders", as magento splits the orders up into many smaller ones. This is a problem from the credit card company side since they are getting suspicious of so many consecutive charges to the card.
Any ideas?

Well, I'm not going to be able to provide a full solution because to be honest, the solution would take quite a bit of time and testing but I'll give out comprehensive information that could you to the right direction instead.
Basically, upfront -- there isn't an option to do this in default Magento.
So instead, we are going to rewrite a certain model called Mage_Checkout_Model_Type_Multishipping. If you're into the proper way of rewriting a model, please read this post by Inchoo.
But if you're lazy and looking into a quick way of rewriting, then please copy the whole file app/code/core/Mage/Checkout/Model/Type/Multishipping.php to app/code/local/Mage/Checkout/Model/Type/Multishipping.php
Once you've finished either of the two above, you can move on to rewriting stuff we need into this class. Check out the function around line 498:
public function createOrders()
{
$orderIds = array();
$this->_validate();
$shippingAddresses = $this->getQuote()->getAllShippingAddresses();
$orders = array();
if ($this->getQuote()->hasVirtualItems()) {
$shippingAddresses[] = $this->getQuote()->getBillingAddress();
}
try {
foreach ($shippingAddresses as $address) {
$order = $this->_prepareOrder($address);
$orders[] = $order;
....
}
foreach ($orders as $order) {
$order->place();
$order->save();
...
}
....
Mage::dispatchEvent('checkout_submit_all_after', array('orders' => $orders, 'quote' => $this->getQuote()));
return $this;
} catch (Exception $e) {
Mage::dispatchEvent('checkout_multishipping_refund_all', array('orders' => $orders));
throw $e;
}
}
So we can clearly see here that it's trying to create an order for each address and then (the most important part), triggering the checkout_submit_all_after event which is monitored by the payment modules (order and quote information are passed to all of the available/active payment modules and the payment method set inside the quote will determine if a certain payment module receiving the dispatched event can process it or not).
With that in mind, you can have a couple of options now:
1.) if you can live with just one order, then try and rewrite the createOrders routine to just combine all items again into one order, ->place() it, then pass into dispatchEvent for good measure.
2.) if you need multiple orders, well -- this is where it becomes convoluted as there's a slim chance that you can do either of the following:
create a payment first then link it into the orders (almost impossible to do)
create the orders and another 'combined' order in which you will pass into the dispatchEvent. sounds filthy and/or wrong so don't bother maybe?
So yeah, you guys are pretty much asking for a lot here so the best I can do is to shed some light on where you can first do serious damage. I'd do it myself, but it's above my pay-grade here.
Some additional pointers:
Watch out for the session methods right after order creation, know where they lead too
Also be wary of all the dispatched events. They all have a purpose why they are there and where they are.
Good luck!

Make sure this option is enabled:
If I'm not mistaken, when customer clicks one page checkout button, ship to multiple adresses option wouldn't be shown to him. But if he clicks on main checkout button it would.

Related

Disable Automatic cart/checkout_update add manual option to update before placing order

Current Issue:
My checkout calculator refreshes shipping rates way too often, currently it update/refreshes shipping rates when I change a payment method, or I choose a different shipping method as well as when I change an address field or modify a quantity in cart & it even does one upon cart opening in browser. (I understand some merchants may need these to update due to fees associated with certain payment gateways or shipping methods, but to me why do I need to pull live shipping rates again when all I did was click on local pickup, or something that shouldn't have any affect at all on shipping carrier rates)
What I'm trying to accomplish:
I would like the checkout/cart calculator to stop auto update/refresh shipping rates all together until the end of checkout where it should place 1 after I've entered all attributing factors into appropriate fields necessary to make the call, at least IMO.
I have read through hundreds of posts but all I'm turning up is ways to ensure checkout calculator refreshes on certain Ajax calls I need it to be disabled for any factor. I'm still pretty new here so I apologize if I'm breaking some protocols please forgive, and any help will be greatly appreciated.
Update:
After thinking on this some more I feel a perfect solution (if possible) would be to place a button in the checkout calc shipping area, or down by the place order button that would allow me or a customer to manually pull the shipping rate API and update totals when necessary and do away with the entire automatic process(this would also benefit me in that woocommerce would stop trying to make a new call after every keystroke), but this would also necessitate the need for a function or something the like to keep an order from being placed unless this manual button had been pressed immediately prior to place order button (and they would need to press it again upon making any address field changes, or changing a shipping method or any other factor that would affect the order totals, exclude things like name, phone number, Company Name, or any other field you can think of that wouldn't affect order total at all).
8 hours later -
I'm still banging my head at this no real results. The only thing I've accomplished is hiding the shipping calc in the cart with this
function disable_shipping_calc_on_cart( $show_shipping ) {
if( is_cart() ) {
return false;
}
return $show_shipping;
}
add_filter( 'woocommerce_cart_ready_to_calc_shipping', 'disable_shipping_calc_on_cart', 99 );
But it doesn't actually stop the API call just hides it. Although I think some time may be saved not having to generate the UI for the field, it's a negligible amount. If only cart would just display order total/tax breakdown like it does in desktop drop down or mobile sidebar that would speed my cart experience up a whole lot. Either way I prefer it not to be in the cart.
Depressing, if only the whole automated update waited for a button press at the end I would save so much time. As well as my customers. I mean doesn't this make sense. Let me describe my workflow through rounds of testing. Eight payment gateways custom configured with rules granting visibility need to all be tested through every variable & here is how it apparently must be done to test the front end(At least in my case, I'm very new to this & I never even considered building a website until I was approached 3-4 months ago about it). I'll shorten the entire experience by going right to the part where I'm entering the cart with 1 of my many orders.
cart opens in browser... Shipping API call 10-15 sec for callback, Update some order quantities if needed... Shipping API call 10-15 sec,(as stated above there is no shipping calc visible here. So thank god I can not be tripped up by another... Shipping API call 10-15 sec while still in the cart), Proceed to checkout, Account credentials auto populate fields... Shipping API call 10-15 sec, I may need to test shipping across a larger distance or commercial/residential rates so change address fields.... shipping API call 10-15 sec, Select Shipping Method... Shipping API call 10-15 sec, Select payment method & ... yep Shipping API call 10-15 sec..... & finally I can place the order through the front end. :( sound exhausting to you? wouldn't it make sense to bypass all those Shipping API calls and just done 1 at the very end of checkout? Hmm that's like 2 min of potentially lost time in the checkout experience. That call ould have been resolved in 1, 10-15 sec manual button press to update.
IDK something like 10 hours in now... I had a little more progress made when I removed update_totals_on_change from all checkout fields but ZIP code. I left ZIP enabled because I was afraid this method might allow you to run through the rest of the order process. Then come back to shipping fields change address and not trigger an update_total and have the order process. At this point I'm not sure if this was necessary or not but I thought if any factor truly affected the order total it would be the zip code & my shipping API will not let an order process if all the other address fields don't correlate with the zip code. So I though better safe. So that will save a bundle of time for those fields(although those fields don't lockout upon API call the way shipping and payment radios do, but at least it saves u waiting on 1 after the very last keystroke. Unless that last keystroke is in the ZIP field, hmm I may need to relocate the field farther up the form to allow it to get started earlier in the process. Whoops long tangent but thinking aloud...) hmmm if only the automated process allowed you to toggle these radios during its call you could potential complete the order in one sitting and allow the process to do its thing the whole time finally resolving itself at the end, seems a little inefficient overhead wise, but is sounding like the most rational idea I've thought of yet...Yeah, unlock all fields/radios, whatever.... while the API is processing... what I'm getting at is a way to get at the greyed out areas that have no way to be interacted with during the update/refresh... Any ideas?
Next day-
After passing out in my labor I didn't have time to test what removing update_totals_on_change from shipping fields did. Unfortunately I discovered this morning that this accomplished nothing. All address fields still auto update on change, disappointing. I'm considering adding better usability plugin to allow for quantity changes at checkout. This would at least eliminate any need to go to the cart page. So that could cut out a step or two.
Another Update:
So I found this bit of code in wp-content/plugins/woocommerce/templates/checkout/payment.php
<?php esc_html_e( 'Since your browser does not support JavaScript, or it is disabled, please ensure you click the <em>Update Totals</em> button before placing your order. You may be charged more than the amount stated above if you fail to do so.', 'woocommerce' ); ?>
<br/><button type="submit" class="button alt" name="woocommerce_checkout_update_totals" value="<?php esc_attr_e( 'Update totals', 'woocommerce' ); ?>"><?php esc_html_e( 'Update totals', 'woocommerce' ); ?></button>
</noscript>
So there seems to be something that will propogate said button if your browser does not support java script. However, I would not want to disable javascript for my whole site, but maybe I can disable it on checkout only and it won't affect much other than generating the above button at checkout. Looking into this, again any advice would be greatly appreciated.
New Info-
So after turning this over again and again. I discovered that by disabling jquery-blockui script It will then allow me to change any field in checkout without locking up during call. However when disabled there is no call being placed upon field changes, so I need to find a way to initiate the call manually and then lock it up somehow so customer must update immediately prior to placing order.
Not a complete answer to your issue but I am struggling with EXACTLY the same problem.
Woocommerce shipping modifications required to support HTTP API shipping calcs
Though I have managed to stop it calling out and calculating the shipping on cart item addition/modification by adding the following to calculate_Shipping method of my custom shipping plugin.
if ('https://aaa.bbb/checkout/' !== $_SERVER['HTTP_REFERER']) {
return;
}
I am starting to think I need to do the same as you were working on by adding a calculate shipping button that must be pressed before ordering. It must also be run again after any address update before checkout was allowed to be completed.
Did you solve your problem in the last months? If so would appreciate your solution!?!

How to get an order's items from an Order ID? (OpenCart 2.0)

I am basically trying to get the items, their costs with an order_id for use in listing on an invoice email which will be sent to the client after purchase or in the future if need be.
Getting order information from the model checkout/order with $this->model_checkout_order->getOrder($order_id); doesn't seem to actually include item-specific information (like name, amount and cost), and thus I cannot finish the email.
Though, I have seen it in the database under the table order_product, which is filled by the same checkout/order model file, but which getOrder() doesn't seem to actually get.
Will I need to make a separate model file to get item-specific info from an order or is there already something that does exactly that which I do not know of? (or am I reading code wrong somewhere?)
Thank you in advance for your help.
To get product info from historical orders as you described, you want catalog/model/account/order.php:
$this->load->model('account/order');
$products = $this->model_account_order->getOrderProducts($order_id);
And just for thoroughness I'd mention if you are dealing with an active cart you can use system/library/cart.php:
$products = $this->cart->getProducts();

Magento - Single Coupon marked as used when payment pending

I have got a problem with Magento single coupon code that is marked as having been used at the time the customer clicks on the Place Order button. If the Paypal payment fails or the client leaves the page before the order is complete, he won't able to go back and re-order with this coupon which is set to be only used once, and has been marked already been used.
I have found a piece of code that decreases the number of times the coupons has been used by the user and allows him to reuse the coupon. Unfortunately, he gets an error when trying to connect the Paypal page when clicking the place order button. In order to be able to use the coupon another time and access the Paypal page, I have to delete the lines in SQL database in tables salesrule_coupon_usage and salesrule_customer with this customer's ID.
Here is the code I need to change to automatically delete coupon usage information for a customer ID:
public function cancel($observer)
{
$order = $observer->getEvent()->getPayment()->getOrder();
if ($order->canCancel()) {
if ($code = $order->getCouponCode()) {
$coupon = Mage::getModel('salesrule/coupon')->load($code, 'code');
if ($coupon->getTimesUsed() > 0) {
$coupon->setTimesUsed($coupon->getTimesUsed() - 1);
$coupon->save();
}
$rule = Mage::getModel('salesrule/rule')->load($coupon->getRuleId());
error_log("\nrule times used=" . $rule->getTimesUsed(), 3, "var/log/debug.log");
if ($rule->getTimesUsed() > 0) {
$rule->setTimesUsed($rule->getTimesUsed()-1);
$rule->save();
}
if ($customerId = $order->getCustomerId()) {
if ($customerCoupon = Mage::getModel('salesrule/rule_customer')->loadByCustomerRule($customerId, $rule->getId())) {
$couponUsage = new Varien_Object();
Mage::getResourceModel('salesrule/coupon_usage')->loadByCustomerCoupon($couponUsage, $customerId, $coupon->getId());
if ($couponUsage->getTimesUsed() > 0) {
/* I can't find any ##$!#$ interface to do anything but increment a coupon_usage record */
$resource = Mage::getSingleton('core/resource');
$writeConnection = $resource->getConnection('core_write');
$tableName = $resource->getTableName('salesrule_coupon_usage');
$query = "UPDATE {$tableName} SET times_used = times_used-1 "
. "WHERE coupon_id = {$coupon->getId()} AND customer_id = {$customerId} AND times_used > 0";
$writeConnection->query($query);
}
if ($customerCoupon->getTimesUsed() > 0) {
$customerCoupon->setTimesUsed($customerCoupon->getTimesUsed()-1);
$customerCoupon->save();
}
}
}
}
}
}
I believe this was an old bug from around 1.4 to maybe 1.6. But whether you've got an old version or not, this can fairly easily be fixed in place if you know your way around Magento.
The problem is, you have code that is updating the salesrule_coupon_usage table right when they click the pay button. This isn't really what you want at all. You want this to be wrapped into the payment transaction. I don't know if this bug is happening because you have custom code or are using a older version of Magento, but I'll tell you how I would fix the problem. Then I'll give you a fix similar to what you proposed:
Magento already has an abstraction called a "transaction". A transaction is used to put and group of objects together that need to either all succeed or all fail, no half-measures. Magento will go through the transaction and attempt to save each of the objects you have placed in it. If any of them fail (for example, payment doesn't go through), all the events that have been already saved are "rolled back".
Luckily, Magento already creates a transaction object to handle payment for the various things that need to update together on successful payment. You can tap into that transaction and use it to update the coupon usage correctly.
Here is the 10,000 foot view of what you need to do.
Find whatever is updating the salesrule_coupon_usage table too early and kill it. We are going to add in our own transaction-safe version, so we don't want it being saved anywhere else. Easiest way to do this is to figure out what model connects to that table and search for the creation of that model. For 1.7 and 1.8, that is the rule/customer model.
Create an observer to catch the start of a payment transaction. In most modern versions of magento this event is called sales_order_payment_place_start and can be witnessed in app/code/core/Mage/Sales/Model/Order/Payment.php
Pull the order out of the event and pull the coupon code out of the event.
Pull the actual model you want to update. It looks like someone couldn't find it in your code, but there should be some model that uses the salesrule_coupon_usage table hiding somewhere. Search though the .xml files for "salesrule_coupon_usage" and see which model using that table. Again, for me, on 1.7, that is the rule/customer model.
Load that model up, with the customer change the value where your coupon code is concerned to indicate that the customer has used the coupon, but don't save yet.
Get the transaction out of the event and register your updated coupon object with the addObject method.
And your done. The transaction will automatically try to save all of the objects that have been added to it. If any part fails (including a failed payment), it'll rollback the whole process and your coupon won't be used. Yay!
Now, in my opinion, the above is the best way to handle the issue, but if you are having issues for some reason, here is an alternative based on catching an unsuccessful payment and then using your code.
Again, here is the 10,000 foot view:
Create an observer to catch the failed payment event. In most versions the event you want is sales_order_payment_cancel.
Run the code you've got... it should do it. But to clarify for others:
Pull the order out of the event, pull the coupon code and customer id out of that.
Update the customer, rule and salesrule_coupon_usage table. (Although there really should be a model for that, I'm sure you can find it)
Now when a sale fails, you go back through and unwind everything manually. This isn't as clean as my first solution, but might be easier for you depending on your familiarity with Magento.
I'm fairly sure that new, clean versions of Magento don't have this issue, so let me offer a fairly obvious suggestion as a third solution.
Update Magento
If Magento is up to date, test disabling any custom modules, because something broke it. I've noticed that Amasty coupon plugins are particularly buggy.
If you've made custom changes to Magento core... good luck with that.
Good luck!

Magento! A Call to a DAL Server on or before or around OnePageController or Success.php

Ok so I have customized the Mage_Checkout_OnepageController
I need to be able to connect to a DAL server and save certain things there. I got everything correct. It's RESTful, so I am sending a POST request. It's works all just fine. But I need to send one more thing aside from quoteData, item details etc. that one more thing is the magento orderId.
I understand that Magento hasn't created the order Id on Mage_Checkout_OnepageController yet, it generates it on Mage_Checkout_Block_Onepage_Success. I have my own X_Request_Block_Checkout_Success. Within this file the following code;
$order = Mage::getModel('sales/order')->loadByIncrementId(Mage::getSingleton('checkout/session')->getLastRealOrderId());
$orderInfo['orderId'] = $order->getIncrementId();
But in this Success.php, this time I can't get the data I get in Mage_Checkout_OnepageController, namely the followings; (any quoteData, shipping address etc)
$jobSiteAddress = Mage::getSingleton('checkout/session')->getQuote()->getShippingAddress()->toArray();
$quote = Mage::getSingleton('checkout/session')->getQuote();
$cartItems = $quote->getAllVisibleItems();
and some other things, I hope you get the idea.
Big Question
So my question; Is there a way to get magento orderId and all cart data either in Mage_Checkout_Block_Onepage_Success or in my Mage_Checkout_OnepageController
I tried
In My OnePageController I have;
$this->getOnepage()->getQuote()->save();
I tried getting the magento order id after this line but no luck
do let me know If I didn't explain clearly
SO, to be clear, you need to have an orderId before magento really generates it ?
Can you explain more why it's needed ? Why don't you do this call after the order is really generated (magento sales/order events can clearly do this)
Anyways, it seems hard to me to get the next orderId (you can't be sure of the next increment_id if 10 customers orders simultaneously, and i don't think you want to create an order if the user haven't pay already) but you should be able to determine the next OrderIncrementId as it's linked to customer history...

How do I add custom shipping and payment options to checkout in Magento?

Running Magento 1.5.1. My webstore contains a lot of products that get shipped direct from the manufacturer. The cost of shipping is extremely variable, and due to the number of products we carry, it is unfeasible to determine shipping costs for many items.
I would like to add a "request quote" option during checkout. The basic idea I have is this:
-Items without a weight or dimensions will be required to go through an RFQ (request for quote) process. During checkout, if the user has an item in their cart that requires an RFQ (because it doesn't have weight or dimensions defined), it will present a single option for shipping: "Request quote for shipping".
-The next step is the payment screen. Instead of actual payment options, a "pay invoice later" or "pay when quote is received" option will be present. The user will be forced to select this (or perhaps we could skip the payment screen altogether?).
This seems like it should be relatively easy to hack together, but I'm wondering if I'm going about this the wrong way? Is there an easier way to do this? My thought is to modify the checkout code to check for weight or dimensions, if not, only show that one shipping option (I'd probably re-purpose "free shipping" to be that option). Then, in the payment screen, check if free shipping is selected in the order, and then present the custom payment option (probably re-purpose cheque or money order option). From there we'd send them an official quote via Paypal or something similar.
Any thoughts? Thanks!!
I'll offer my normal advice: There are a ton of extensions to let this happen. My philosophy is to start looking at extensions before trying to code anything in magento. Is especially helpful when an upgrade breaks something; they'll usually fix upgrade problems that clearly affect their own extensions for free.
In your case there is a free custom shipping option extension I use for "Will Call" which you can use for "RFQ" purposes. I strongly advise against messing with payment modules. I have written 6 or more "payment" things in PHP for live sites and admin back-end use. Until you really understand Magento, you are wasting your time and money writing code. If your question is to get a website working so you can make money, go buy extensions that get the job done...
To go on about writing code, I hand rolled some code in the contact form to add google re-capcha as well as to have customers select from existing product attributes (vehicle year, make, model). I was eventually able to figure out how to have these reflect in the emails we get from the contact form, however, there is no database persistence, which is the proper solution. Further, mucking about in the code quickly and easily breaks Magento in unexpected and surprising ways, hence why my normal Magento reply on here is to strongly urge people to buy extensions. If you're asking the kind of questions like I would ask, you don't know enough to make a professional extension... buy a few proper extensions, analyze them, spend lots of time trying to figure it out.
Any more there are plenty of extensions competing to do the same tasks, so there should be something already working to do what you want.

Categories