Magento - OnePage Checkout - Hide Payment method depending on Shipping Method - php

I have asked this question on Magento Stackexchange without any success, hence me now asking here.
I'm using Magento Community Edition 1.9.0.1 and have correctly created and registered my module, but I can't seem to detect the shipping methods. Basically, I want to hide Cash on Delivery if Flat Rate or Free Shipping is chosen. Here is the code I have for my observer class:
class Kol_PaymentToggle_Model_Observer
{
public function paymentMethodIsActive(Varien_Event_Observer $observer) {
$event = $observer->getEvent();
$method = $event->getMethodInstance();
$result = $event->getResult();
$quote = $observer->getEvent()->getQuote();
$shippingMethod = $quote->getShippingAddress()->getShippingMethod();
if($shippingMethod == "standardshipping" || $shippingMethod == "free") {
if($method->getCode() == 'cashondelivery' ) {
$result->isAvailable = false;
}
}
}
}
I'm guessing that I haven't used the correct shipping method code names or payment method code names, but I'm unsure. Anyone has any advice?
I only have 3 shipping methods enabled:
Collect In Store Title = Collect in Store Method Name = Collect In Store (Extension link)
Flat Rate Title = Standard Delivery Method Name = Standard Shipping
Free Shipping Title = Free Delivery Method Name = Free
Output of config.xml
<?xml version="1.0"?>
<config>
<modules>
<Kol_PaymentToggle>
<version>0.0.1</version>
</Kol_PaymentToggle>
</modules>
<frontend>
<events>
<payment_method_is_active>
<observers>
<paymentfilter_payment_method_is_active>
<type>singleton</type>
<class>Kol_PaymentToggle_Model_Observer</class>
<method>paymentMethodIsActive</method>
</paymentfilter_payment_method_is_active>
</observers>
</payment_method_is_active>
</events>
</frontend>
</config>

As for I got, you trying to hide some payment methods based on shipping method. For this you don't need to observe things at all. Simply you can do this, just follow me,
Every methods(in one page check out) post the methods which are chosen to the next level. so you can get the shipping method which are chosen, in payment method level. Just print the post things in
app/design/frontend/base/default/template/checkout/onepage/payment/methods.phtml
in this add below one,
<?php print_r($_POST); ?>
So now you can get the shipping methods which are chosen previous step. And note it, so now, you can add just simple logic (if else) condition in same file for hiding payment,
For example here I want hide check / money order payment method, if shipping method is flat. Here the payment method code is checkmo. you can get payment method code by simply printing that variable like echo $_code = $_method->getCode(); in same file. so here just add simple if else ,
<?php
$methods = $this->getMethods();
$oneMethod = count($methods) <= 1;
?>
<?php if (empty($methods)): ?>
<dt>
<?php echo $this->__('No Payment Methods') ?>
</dt>
<?php else:
foreach ($methods as $_method):
echo $_code = $_method->getCode();
if($_POST['shipping_method'] == 'flatrate_flatrate') {
if($_code == 'checkmo') {
continue;
}
}
?>
Here,
if($_POST['shipping_method'] == 'flatrate_flatrate') {
if($_code == 'checkmo') {
continue;
}
}
checks the shipping method and skip the payment method which we don't want to display. That's it. Please comment here if you have any doubt.
Note:
shipping_method => flatrate_flatrate
paymet_method => checkmo

Though the accepted answer works, it is not an elegant solution since it prompts us to check the post data in a phtml file which is not a good method. Instead, you should definitely look for an event to do this job.
Luckily we have a perfect event to do this job and which is payment_method_is_active. Please see the detailed answer here.
Basically, you need to set the payment method inactive through this method as you can see in the answer.

In addition to the solution for the OnePage Checkout IWD provided by #Simbus82:
$shipping = Mage::getSingleton('checkout/session')->getQuote()->getShippingAddress()->getShippingMethod();
foreach ($methods as $_method):
$_code = $_method->getCode();
if ( $shipping == 'matrixrate_matrixrate_140' or $shipping == 'matrixrate_matrixrate_148' ) {
if($_code != 'cashondelivery') {
continue;
}
}

Related

Magento custom price value not converting by changing currency

Following code is used to set custom price for simple product. Custom price set in cart as needed but when i switch currency then custom price value remains same with current currency symbol.
$item->setCustomPrice($customPrice);
$item->setOriginalCustomPrice($customPrice);
$item->getProduct()->setIsSuperMode(true);
Is there any way to set custom price that work with currency switching.
I found a solution to do this.
First Step:
Add item to quote with custom price using following code suggested by #Ashish Raj
$baseCurrencyCode = Mage::app()->getStore()->getBaseCurrencyCode();
$currentCurrencyCode = Mage::app()->getStore()->getCurrentCurrencyCode();
$price = $customPrice;
$customPrice = $Current_currency_price = Mage::helper('directory')->currencyConvert($price, $baseCurrencyCode, $currentCurrencyCode);
$item->setCustomPrice($customPrice);
$item->setOriginalCustomPrice($customPrice);
$item->getProduct()->setIsSuperMode(true);
Second Step:
Second step is to create an controller post dispatch observer by adding following code in config.xml file of module
<events>
<controller_action_postdispatch>
<observers>
<frontend_currency_change>
<class>modulename/observer</class>
<method>hookToControllerActionPostDispatch</method>
</frontend_currency_change>
</observers>
</controller_action_postdispatch>
</events>
And add following code to observer class
public function hookToControllerActionPostDispatch($observer) {
if ($observer->getEvent()->getControllerAction()->getFullActionName() == 'directory_currency_switch') {
$quote = Mage::getSingleton('checkout/session')->getQuote();
if ($quote && $quote->hasItems()) {
foreach ($quote->getAllVisibleItems() as $item):
//add condition for target item
$customPrice = 23;//use custom price logic
$baseCurrencyCode = Mage::app()->getStore()->getBaseCurrencyCode();
$currentCurrencyCode = Mage::app()->getStore()->getCurrentCurrencyCode();
$customPrice = Mage::helper('directory')->currencyConvert($customPrice, $baseCurrencyCode, $currentCurrencyCode);
$item->setCustomPrice($customPrice);
$item->setOriginalCustomPrice($customPrice);
$item->getProduct()->setIsSuperMode(true);
$quote->collectTotals()->save();
endforeach;
}
}
}
This is working for me. Hope this will help to someone having same issue.
I will prefer if somebody have better solution.
Thanks.
Use below code hope it will help you ...
First Step:
//you need to find base currency code and then find current currency code...
$baseCurrencyCode = Mage::app()->getStore()->getBaseCurrencyCode();
$currentCurrencyCode = Mage::app()->getStore()->getCurrentCurrencyCode();
$price = $customPrice;
Second Step:
//convert price from base currency to current currency
$customPrice = $Current_currency_price = Mage::helper('directory')->currencyConvert($price, $baseCurrencyCode, $currentCurrencyCode);
3rd Step:
then you can use your code:
$item->setCustomPrice($customPrice);
$item->setOriginalCustomPrice($customPrice);
$item->getProduct()->setIsSuperMode(true);

Magento: Create coupon code rule that discount differently base on the current discount of items in cart

The requirements are if the items are already on discount, then discount extra x%, if the items are not on discount, then discount y%. I need to handle the case that both type of these above items appear in the same cart? Is there a existing solutions, or if I need to implement myself, where would I start. Thank you
There is an event in magento,
sales_quote_collect_totals_after
This is fired whenever your total is calculated, what you can do is set a flag in session on click on the button to apply discount, and in this above event's observer method, check if it is set then apply discount.
In your config.xml
<global>
<events>
<sales_quote_collect_totals_after>
<observers>
<class>Custom_Module_Model_Observer</class>
<method>collectTotals</method>
</observers>
</sales_quote_collect_totals_after>
</events>
</global>
Make a Observer.php in
Custom
/Module
/Model
/Observer.php
Make a function in Observer.php
public function collectTotals(Varien_Event_Observer $observer)
{
$quote=$observer->getEvent()->getQuote();
$quoteid=$quote->getId();
//check condition here if need to apply Discount
if($disocuntApply) $discountAmount =5;
if($quoteid) {
if($discountAmount>0) {
$total=$quote->getBaseSubtotal();
$quote->setSubtotal(0);
$quote->setBaseSubtotal(0);
$quote->setSubtotalWithDiscount(0);
$quote->setBaseSubtotalWithDiscount(0);
$quote->setGrandTotal(0);
$quote->setBaseGrandTotal(0);
$canAddItems = $quote->isVirtual()? ('billing') : ('shipping');
foreach ($quote->getAllAddresses() as $address) {
$address->setSubtotal(0);
$address->setBaseSubtotal(0);
$address->setGrandTotal(0);
$address->setBaseGrandTotal(0);
$address->collectTotals();
$quote->setSubtotal((float) $quote->getSubtotal() + $address->getSubtotal());
$quote->setBaseSubtotal((float) $quote->getBaseSubtotal() + $address->getBaseSubtotal());
$quote->setSubtotalWithDiscount(
(float) $quote->getSubtotalWithDiscount() + $address->getSubtotalWithDiscount()
);
$quote->setBaseSubtotalWithDiscount(
(float) $quote->getBaseSubtotalWithDiscount() + $address->getBaseSubtotalWithDiscount()
);
$quote->setGrandTotal((float) $quote->getGrandTotal() + $address->getGrandTotal());
$quote->setBaseGrandTotal((float) $quote->getBaseGrandTotal() + $address->getBaseGrandTotal());
$quote ->save();
$quote->setGrandTotal($quote->getBaseSubtotal()-$discountAmount)
->setBaseGrandTotal($quote->getBaseSubtotal()-$discountAmount)
->setSubtotalWithDiscount($quote->getBaseSubtotal()-$discountAmount)
->setBaseSubtotalWithDiscount($quote->getBaseSubtotal()-$discountAmount)
->save();
if($address->getAddressType()==$canAddItems) {
$address->setSubtotalWithDiscount((float) $address->getSubtotalWithDiscount()-$discountAmount);
$address->setGrandTotal((float) $address->getGrandTotal()-$discountAmount);
$address->setBaseSubtotalWithDiscount((float) $address->getBaseSubtotalWithDiscount()-$discountAmount);
$address->setBaseGrandTotal((float) $address->getBaseGrandTotal()-$discountAmount);
if($address->getDiscountDescription()){
$address->setDiscountAmount(-($address->getDiscountAmount()-$discountAmount));
$address->setDiscountDescription($address->getDiscountDescription().', Amount Waived');
$address->setBaseDiscountAmount(-($address->getBaseDiscountAmount()-$discountAmount));
}else {
$address->setDiscountAmount(-($discountAmount));
$address->setDiscountDescription('Amount Waived');
$address->setBaseDiscountAmount(-($discountAmount));
}
$address->save();
}//end: if
} //end: foreach
//echo $quote->getGrandTotal();
foreach($quote->getAllItems() as $item){
//We apply discount amount based on the ratio between the GrandTotal and the RowTotal
$rat=$item->getPriceInclTax()/$total;
$ratdisc=$discountAmount*$rat;
$item->setDiscountAmount(($item->getDiscountAmount()+$ratdisc) * $item->getQty());
$item->setBaseDiscountAmount(($item->getBaseDiscountAmount()+$ratdisc) * $item->getQty())->save();
}
}
}
}
collectTotals function will be called, whenever the quote totals is updated, so there is no need to call it explicitly.
Check for how it works here.
Setting magento session variables, check here.
hope it helps!

Magento - How to trigger event (change user group) after purchasing a specific product

My question is how to trigger an action after a successful order (with ID 11)?
I read from previous questions that I need to create an observer for checkout_onepage_controller_success_action but not much is explained after that.
Below is the code that (from the same previous question) I used to change the product:
$special_cat = 11; // Special product category
$customerId = Mage::getSingleton('customer/session')->getCustomerId();
$lastOrderId = Mage::getSingleton('checkout/session')->getLastOrderId();
$order = Mage::getSingleton('sales/order');
$order->load($lastOrderId);
$allitems = $order->getAllItems();
foreach($allitems as $item)
{
$product = Mage::getModel('catalog/product')->load($item->getProductId());
$categoryIds = $product->getCategoryIds();
if (in_array($special_cat, $categoryIds)) {
$mem_group_id = $item->getSku(); // $item->getSku() is customer group name
$customer_detail = Mage::getSingleton('customer/session')->getCustomer();
$customer_detail->setGroupId($mem_group_id);
$customer_detail->save();
}
}
Do I need to create an extension for this or do I need to edit the core files? Where should I create the Observer?
Yes. You need create extension for this. It's not a big deal. So you want change user group for a customer after order is success. Right ?. For this you need observe the checkout process and put your code in that. And you were right, checkout_onepage_controller_success_action is the correct observer for this.
Here you go..
app/code/local/Packagename/Modulename/etc/config.xml
<?xml version="1.0"?>
<config>
<modules>
<Packagename_ModuleName>
<version>0.1.0</version>
</Packagename_ModuleName>
</modules>
<global>
<models>
<modulename>
<class>Packagename_ModuleName_Model</class>
<resourceModel>modulename_mysql4</resourceModel>
</modulename>
</models>
<events>
<checkout_onepage_controller_success_action> <!-- identifier of the event we want to catch -->
<observers>
<checkout_onepage_controller_success_action_handler> <!-- identifier of the event handler -->
<type>model</type> <!-- class method call type; valid are model, object and singleton -->
<class>modulename/observer</class> <!-- observers class alias -->
<method>changeUserGroup</method> <!-- observer's method to be called -->
<args></args> <!-- additional arguments passed to observer -->
</checkout_onepage_controller_success_action_handler>
</observers>
</checkout_onepage_controller_success_action>
</events>
</global>
</config>
Here Packagename_ModuleName_Model is your new class. and changeUserGroup is your new method. Here only we will put our code for user group change things.
so,
app/code/local/Packagename/Modulename/Model/Observer.php
<?php
class Packagename_ModuleName_Model_Observer
{
public function changeUserGroup(Varien_Event_Observer $observer)
{
//$customer = $observer->getCustomer();
$special_cat = 11; // Special product category
$customerId = Mage::getSingleton('customer/session')->getCustomerId();
$lastOrderId = Mage::getSingleton('checkout/session')->getLastOrderId();
$order = Mage::getSingleton('sales/order');
$order->load($lastOrderId);
$allitems = $order->getAllItems();
foreach($allitems as $item)
{
$product = Mage::getModel('catalog/product')->load($item->getProductId());
$categoryIds = $product->getCategoryIds();
if (in_array($special_cat, $categoryIds)) {
$mem_group_id = $item->getSku(); // $item->getSku() is customer group name
$customer_detail = Mage::getSingleton('customer/session')->getCustomer();
$customer_detail->setGroupId($mem_group_id);
$customer_detail->save();
}
}
}
}
And finally enable your module in,
app/etc/modules/Packagename_ModuleName.xml
<?xml version="1.0"?>
<config>
<modules>
<Packagename_ModuleName>
<active>true</active>
<codePool>local</codePool>
<version>0.1.0</version>
</Packagename_ModuleName>
</modules>
</config>
Normally we get the details from $observer when we observe something. But in your case the customer_id and order-id is available in session. So we can get those things from session. That's it.
If you have any doubt please comment here.
It's never a good practise to edit the core files. If you want to change the functionality of default Magento, you should override specific files.
Here is a good tutorial on overriding
A much better way is to create an extension and observe specific events. In your case, observe sales_order_place_after event and check whether the order is containing product with ID 11 or not? If yes, then change the customer group.
For More info on observer check this out.
Hope it helps.
no need to create extension for it.
Just go to in your onepage_controller_action and add following code.
$mem_catid = 982; //CATEGORY ID products to filter
$_customerId = Mage::getSingleton('customer/session')->getCustomerId();
$lastOrderId = Mage::getSingleton('checkout/session')->getLastOrderId();
$order = Mage::getSingleton('sales/order');
$order->load($lastOrderId);
$allitems = $order->getAllItems();
foreach($allitems as $item)
{
$product = Mage::getModel('catalog/product')->load($item->getProductId());
$categoryIds = $product->getCategoryIds();
if (in_array($mem_catid, $categoryIds)) {
$mem_group_id = $item->getSku(); // $item->getSku() is customer group name
$customer_detail = Mage::getSingleton('customer/session')->getCustomer();
$customer_detail->setGroupId(6);//add customer group id here which you want to set.
$customer_detail->save();
}
}

Magento - get price rules from order

does anyone know how one can get the catalog- and cart price rules from an order?
I know that I can get the discount percentage from an order item via the method getDiscountPercent(), but how can I get all the rules that were applied to the whole order?
For example, I have a rule "Customer Group X gets 20% off all items in the store".
Now I want to determine which rules were actually applied when the order has been submitted by the user. I need this for an order export interface where I have to supply all discounts that the user got.
Thanks in advance!
Have a look in the sales_flat_order_item table. there is a field called applied_rule_ids which will give you the id of the rule applied to that item. Also you can find out in this table how much discount was applied and the percentage.
Example
//The order I want to check
$order_id = 859;
//Get the list of items for your order
$items = Mage::getModel('sales/order_item')
->getCollection()
->addFilter('order_id',array('eq'=>$order_id));
//loop through each item
foreach($items as $item){
//if the item has not had a rule applied to it skip it
if($item->getAppliedRuleIds() == '')continue;
/*
* I cant remember in the database they might be comma separated or space if multiple rules were applied
* the getAppliedRuleIds() function is the one you want
*/
foreach(explode(",",$item->getAppliedRuleIds()) as $ruleID){
//Load the rule object
$rule = Mage::getModel('catalogrule/rule')->load($ruleID);
// Throw out some information like the rule name what product it was applied to
echo "<p>".$item->getSku()." had rule ".$rule->getName()."(".$item->getAppliedRuleIds().") applied </p>";
}
}
/* Example of getting catalog rules applied for specific product */
$productId = 6;
$user = Mage::getSingleton('customer/session')->getCustomer();
$product = Mage::getModel('catalog/product')->load($productId);
$storeId = $product->getStoreId();
$websiteId = Mage::app()->getStore($storeId)->getWebsiteId();
$dateTs = Mage::app()->getLocale()->storeTimeStamp($storeId);
$customerGroupId = $user->getGroupId();
$resource = Mage::getResourceModel('catalogrule/rule');
$rules = $resource->getRulesFromProduct($dateTs, $websiteId, $customerGroupId, $productId);
Yeah, it's kind of a pain that Magento doesn't leave any history around in the order record when you use sale pricing with regard to how that base price was calculated.
Fortunately it's open source, so you can patch this in if you like.
I recently wrote an observer that fires when the order record is loaded, to address this very issue. It cross-references the line base_price with the product's current full-retail price. If there is a mismatch, it adds a couple fields to the order item to help expose this info to any external script that's listening (in our case, a custom order fulfillment script that relays orders to SAP using Magento's SOAP API).
Here's the basic idea - make a module in app/code/local/YourCo/SalePricing
and set up an observer class in app/code/local/YourCo/SalePricing/Model/Promo/Observer.php
<?php
class YourCo_SalePricing_Model_Promo_Observer
{
public function __construct()
{
}
// tag order items that have a sale-price
// with original retail price and total sale discount for this line
public function report_sale_pricing($observer)
{
$event = $observer->getEvent();
$order = $event->getOrder();
$items = $order->getAllItems();
foreach ($items as $item) {
// heads up, you may want to do this for other types as well...
if ($item->getProductType() == Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE) {
$regular_price = $this->_lookupFullRetail($item,$order->getStoreId());
$sale_price = $item->getBasePrice();
if ($regular_price - $sale_price > 0.005) {
$qty = $item->getQtyOrdered();
$total_sale_discount = ($regular_price * $qty) - ($sale_price * $qty);
$item->setFullRetailPrice((string)$regular_price);
$item->setSaleDiscount((string)$total_sale_discount);
}
}
}
}
private function _lookupFullRetail(&$item,$store_id)
{
$mpid = $item->getProductId();
$p = Mage::getModel('catalog/product')->setStoreId($store_id)->load($mpid);
return $p->getPrice();
}
}
Your module's etc/config.xml needs to tell magento when to run your observer:
<?xml version="1.0"?>
<config>
<global>
<models>
<yourco_salepricing>
<class>YourCo_SalePricing_Model</class>
</yourco_salepricing>
</models>
<events>
<sales_order_load_after>
<observers>
<yourco_salepricing>
<type>singleton</type>
<class>YourCo_SalePricing_Model_Promo_Observer</class>
<method>report_sale_pricing</method>
</yourco_salepricing>
</observers>
</sales_order_load_after>
</events>
</global>
</config>
Make sure to activate your new module in app/etc/modules/... and clear the config cache.
Now, when you load the order, you can loop over each item and check for $item->getFullRetailPrice() -- if it's there, you know the item was on sale (well, either that or the price has gone up since the order was placed). You still don't know why, ie which sale price rule was in force, but for our application we didn't really care, and getting that info to be saved with the order would have been a much tougher mod.
What might help you though:
$order = Mage::getModel('sales/order')->load(20569);
echo Mage::helper('sales')->__('Discount (%s)', $order->getDiscountDescription());
This prints out all the discount rules name which have been applied to the order.

Skip Checkout in Magento for a downloadable product

I am using Magento to build an eBooks site. For the release, we plan to have a number of free downloadable books. We were hoping that it would be possible to use the normal Magento 'catalog' functionality to add categories with products underneath. However, since these are free downloadable products, it doesn't really make sense to send users through the checkout when they try to download.
Does anyone know of a way to create a free downloadable product which bypasses the checkout altogether? I have noticed that there is a 'free sample' option for downloadable products, but I would prefer not to use this if I can as I plan to use this field for its intended purpose when I add paid products.
[EDIT]
I have noticed that some of you have voted this question down for 'lack of question clarity'. For clarity, I want:
to know if it is possible to
create a downloadable product in
Magento which doesn't require users
to go through the usual checkout
process (since it is free)
and
which is not the 'Free Sample' field
of a downloadable product
Unfortunately I don't think I can ask this any more eloquently.
[/EDIT]
This code will allow logged-in customers to "place an order" for a Free, Virtual product while bypassing checkout and redirect them straight to the My Downloads section of their account.
Add the following line to your catalog/product/list.phtml in the spot you want it.
<?php if($_product->isVirtual() && $_product->getFinalPrice()==0) { ?>
<?php echo $this->__('Download and Install') ?>
<?php } ?>
Then create a new module with a controllers/CheckoutController.php containing this code:
public function purchaseAction() {
if (!Mage::getSingleton('customer/session')->isLoggedIn()) {
$this->_redirectUrl(Mage::getBaseUrl().'customer/account');
return;
}
$request = $this->getRequest();
$id = $request->getParam('id');
$product = Mage::getModel('catalog/product')
->load($id)
->setStoreId(Mage::app()->getStore()->getId());
if(!($product->getIsVirtual() && $product->getFinalPrice() == 0)){
Mage::getSingleton('checkout/session')->addError($this->__('Method only available for Free Downloadable Items'));
return $this;
}
$onepage = Mage::getSingleton('checkout/type_onepage');
/* #var $onepage Mage_Checkout_Model_Type_Onepage */
try{
$quote = $onepage->getQuote();
/* #var $quote Mage_Sales_Model_Quote */
$quote->addProduct($product);
Mage::getSingleton('checkout/session')->setCartWasUpdated(true);
$onepage->initCheckout();
$payment=array('method'=>'free');
$onepage->savePayment($payment);
$onepage->saveOrder();
$this->getResponse()->setRedirect('/downloadable/customer/products');
}
catch(Exception $e){
$result = $e->getMessage();
Mage::getSingleton('checkout/session')->addError($result);
}
}
You'll need to handle the Exceptions a little better, but that should be functionally correct.
My best blind guess (looking at the blocks and models in Mage_Downloadable) is using the product type instance. So, somewhere in your product templates, you may be able to do this:
// $_product is the current product
$links = $product->getTypeInstance(true)->getLinks();
if(count($links)) {
foreach($links as $link) {
print "<a href='". $this->getUrl('downloadable/download/link', array(
'id' => $item->getLinkHash(),
'_secure' => true,
'_nosid' => true
)) . "'>Download</a>";
}
}
If not, I hope that at least gets you on the right path.
Hope that helps. Thanks,
Joe
You can loop through a list of your downloadable links and add a link for each one.
$links=Mage::getModel('downloadable/link')
->getCollection()
->addFieldToFilter('product_id',array('eq'=>$_product->getId()));
foreach($links as $link){
echo "<a href='" . $link->getLink_url() . "'>Download</a>";
}

Categories