I am have implemented custom discounts in magento which is working properly.But, when discount is implemented and someone is trying to pay from magento wallet balance, wallet seems to stop working and the amount paid from wallet does not get deducted from the grand total of the cart.
Discount is applied as follows:
config.xml-
<sales>
<quote>
<totals>
<discount>
<class>Uf_Rewards_Model_Discount</class>
<after>subtotal</after>
</discount>
</totals>
</quote>
</sales>
Discount.php:
class Uf_Rewards_Model_Discount extends Mage_SalesRule_Model_Quote_Discount {
public function collect(Mage_Sales_Model_Quote_Address $address) {
if ($address->getData('address_type') == 'billing')
return $this;
$discount = 10; //your discount percent
$grandTotal = $address->getGrandTotal();
$baseGrandTotal = $address->getBaseGrandTotal();
$totals = array_sum($address->getAllTotalAmounts());
$baseTotals = array_sum($address->getAllBaseTotalAmounts());
$address->setFeeAmount(-$discount);
$address->setBaseFeeAmount(-$discount);
$address->setGrandTotal($grandTotal + $address->getFeeAmount());
$address->setBaseGrandTotal($baseGrandTotal + $address->getBaseFeeAmount());
$address->setDiscountAmount(-$discount);
$address->setBaseDiscountAmount(-$discount);
return $this;
}
public function fetch(Mage_Sales_Model_Quote_Address $address) {
if ($address->getData('address_type') == 'billing')
return $this;
$amt = $address->getFeeAmount();
// $amt=300;
if ($amt != 0) {
Mage::log($amt, NULL, 'obsrvr.log');
$address->addTotal(array(
'code' => 'Discount',
'title' => 'Discount',
'value' => $amt
));
}
return $this;
}
}
Related
I have a problem with a multistore site created with Magento 2.4. Right now the products from the category page and the search page are arranged by name. The options for sorting are: Alphabetical A - Z and Z - A, and by price from low to high and high to low. When I try to sort the products alphabetical everything works fine, but when I try by price it's like a random sort. I also put a custom module but the same result. Instead, if I sort them by product id or by weight it is ok.
if ($currentOrder) {
if ($currentOrder == 'price_asc') {
$subject->getCollection()->setOrder('price', 'asc');
} elseif ($currentOrder == 'price_desc') {
$subject->getCollection()->setOrder('price', 'desc');
} elseif ($currentOrder == 'name_asc') {
$subject->getCollection()->setOrder('name', 'asc');
} elseif ($currentOrder == 'name_desc') {
$subject->getCollection()->setOrder('name', 'desc');
}
}
I also put ->getStoreId(1) after getCollection() but I have same the result. Edit: from what I saw instead of sort by price it's a sort by product id
Steps to Add Magento 2 Sort by Price for Low to High & High to Low Options:
Step 1: Create registration.php file in app\code\Vendor\Extension
Step 2: Create module.xml file in app\code\Vendor\Extension\etc
Step 3: Create di.xml file in app\code\Vendor\Extension\etc
Step 4: Create Toolbar.php file in app\code\Vendor\Extension\Plugin\Catalog\Block
Step 5: Create Config.php file in app\code\Vendor\Extension\Plugin\Catalog\Model
You can refer to the details here.
It Work's for Magento 2.4 versions
I am Assuming you have created a custom module Vendor/module_sort
create di.xml file in vendor/module_sort/etc/
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Catalog\Model\Config">
<plugin name="Vendor_module_sort::addCustomOptions" type="Vendor\module_sort\Plugin\Model\Config" />
</type>
<preference for="Magento\Catalog\Block\Product\ProductList\Toolbar" type="Vendor\module_sort\Plugin\Catalog\Block\Toolbar" />
</config>
Create Config.php in Vendor/module_sort/plugin/Model
<?php
namespace Vendor\module_sort\Plugin\Model;
class Config {
public function afterGetAttributeUsedForSortByArray(\Magento\Catalog\Model\Config $catalogConfig, $options)
{
unset($options['name']);
unset($options['price']);
//New sorting options
$customOption['high_to_low'] = __( 'Price High to Low' );
$customOption['low_to_high'] = __( 'Price Low to High' );
$customOption['name_Z_to_A'] = __( 'Product Title:Z-A' );
$customOption['name_A_to_Z'] = __( 'Product Title:A-Z' );
// $customOption['name'] = $default_options['name'];
//Merge default sorting options with custom options
$options = array_merge($customOption, $options);
return $options;
}
}
Create Toolbar.php in Vendor/module_sort/plugin/Catalog/Block/
<?php
namespace Vendor\module_sort\Plugin\Catalog\Block;
class Toolbar extends \Magento\Catalog\Block\Product\ProductList\Toolbar
{
/**
* Set collection to pager
*
* #param \Magento\Framework\Data\Collection $collection
* #return \Magento\Catalog\Block\Product\ProductList\Toolbar
*/
public function setCollection($collection)
{
$this->_collection = $collection;
$this->_collection->setCurPage($this->getCurrentPage());
// we need to set pagination only if passed value integer and more that 0
$limit = (int)$this->getLimit();
if ($limit) {
$this->_collection->setPageSize($limit);
}
if ($this->getCurrentOrder()) {
if (($this->getCurrentOrder()) == 'position') {
$this->_collection->addAttributeToSort(
$this->getCurrentOrder(),
$this->getCurrentDirection()
);
} else {
if ($this->getCurrentOrder() == 'high_to_low') {
$this->_collection->setOrder('price', 'desc');
} elseif ($this->getCurrentOrder() == 'low_to_high') {
$this->_collection->setOrder('price', 'asc');
}elseif ($this->getCurrentOrder() == 'name_Z_to_A') {
$this->_collection->setOrder('name', 'desc');
}elseif ($this->getCurrentOrder() == 'name_A_to_Z') {
$this->_collection->setOrder('name', 'asc');
}
}
}
return $this;
}
}
How do I make the telephone field not required on shipping but have it required on billing in onepage checkout?
I have followed many forums that point to the below method but this disables the required element on both billing and shipping?
http://swarminglabs.com/magento-making-the-telephone-field-not-required-at-checkout/#comment-2687
I do not know any extension that would allow this. If you want to make it work you should work with Mage_Customer_Model_Form. During checkout process magento calls validateData() method of this model. This method is defined in Mage_Eav_Model_Form. You need to rewrite another model, namely Mage_Sales_Model_Quote_Address as its parent Mage_Customer_Model_Address_Abstract has a valid() method that checks if telephone is not emtpy. So, assuming you have removed is_required and validation_rules for this attribute
in a module etc/config.xml
<config>
<global>
<models>
<customer>
<rewrite>
<form>YourNamespace_YourModule_Model_Customer_Form</form>
</rewrite>
</customer>
<sales>
<rewrite>
<quote_address>YourNamespace_YourModule_Model_Quote_Address</quote_address>
</rewrite>
</sales>
</models>
</global>
</config>
in YourNamespace/YourModule/Model/Customer/Form.php
class YourNamespace_YourModule_Model_Customer_Form extends Mage_Customer_Model_Form {
public function validateData(array $data) {
//perform parent validation
$result = parent::validateData($data);
//checking billing address; perform additional validation
if ($this->getEntity()->getAddressType() == Mage_Sales_Model_Quote_Address::TYPE_BILLING) {
$valid = $this->_validatePhoneForBilling($data);
if ($result !== true && $valid !== true) {
$result[] = $valid;
}
elseif ($result === true && $valid !== true) {
$result = $valid;
}
}
return $result;
}
protected function _validatePhoneForBilling($data) {
$errors = array();
if (empty($data['telephone'])) {
$attribute = $this->getAttribute('telephone');
$label = Mage::helper('eav')->__($attribute->getStoreLabel());
$errors[] = Mage::helper('eav')->__('"%s" is a required value.', $label);
}
if (empty($errors)) {
return true;
}
return $errors;
}
}
in YourNamespace/YourModule/Model/Quote/Address.php
class YourNamespace_YourModule_Model_Quote_Address extends Mage_Sales_Model_Quote_Address {
public function validate() {
if ($this->getAddressType() == self::TYPE_SHIPPING) {
$result = parent::validate();
$errorMsg = Mage::helper('customer')->__('Please enter the telephone number.');
if (is_array($result) && in_array($errorMsg, $result)) {
$result = array_diff($result, array($errorMsg));
}
if (empty($result)) {
return true;
}
return $result;
}
else {
return parent::validate();
}
}
}
I'm trying to write module for add item to row totals in pdf invoice. This is my modules config.xml:
<?xml version="1.0"?>
<config>
<modules>
<Devpassion_Rowtotal>
<version>0.0.1</version>
</Devpassion_Rowtotal>
</modules>
<global>
<pdf>
<totals>
<rowtotal translate="title">
<title>Subtotal less discount</title>
<source_field>rowtotal</source_field>
<model>rowtotal_pdf_model_totalpdf</model>
<font_size>7</font_size>
<display_zero>1</display_zero>
<sort_order>200</sort_order>
</rowtotal>
</totals>
</pdf>
And this is my model class:
class Devpassion_Rowtotal_Pdf_Model_Totalpdf extends Mage_Sales_Model_Order_Pdf_Total_Default {
public function getTotalsForDisplay () {
$order = $this->getOrder();
$item = $this->getItem();
$subtotaldisc = $item->getRowTotal() + $item->getTaxAmount() + $item->getHiddenTaxAmount() ; - $item->getDiscountAmount();
$result = $order->formatPriceTxt($subtotaldisc) ;
$totals = array(array(
'label' => 'Cijena sa popustom',
'amount' => $result,
'font_size' => $fontSize,
)
);
return $totals;
}
}
And nothing shows up on pdf invoice. Can anybody advice please what can be wrong here. Thanks.
I solved my problem, after I looked at my code, there is some mistakes. But my problem is not like yours.
I noticed that you have incorrect code in your config.xml and also in your model class. Try this.
config.xml
<global>
<pdf>
<totals>
<rowtotal translate="title">
<title>Subtotal less discount</title>
<source_field>rowtotal</source_field>
<model>Devpassion_Rowtotal_Pdf_Model_Totalpdf</model>
<font_size>7</font_size>
<display_zero>1</display_zero>
<sort_order>200</sort_order>
</rowtotal>
</totals>
</pdf>
</global>
Model class, Totalpdf.php
class Devpassion_Rowtotal_Pdf_Model_Totalpdf extends Mage_Sales_Model_Order_Pdf_Total_Default {
public function getTotalsForDisplay() {
$order = $this->getOrder();
$item = $this->getItem();
$subtotaldisc = $item->getRowTotal() + $item->getTaxAmount() + $item->getHiddenTaxAmount() - $item->getDiscountAmount();
$result = $order->formatPriceTxt($subtotaldisc);
if($this->getAmountPrefix()){
$result= $this->getAmountPrefix().$result;
}
$fontSize = $this->getFontSize() ? $this->getFontSize() : 7;
$totals = array(array(
'label' => 'Cijena sa popustom:',
'amount' => $result,
'font_size' => $fontSize,
)
);
return $totals;
}
}
I have tried the above code, it doesn't get the value of $item. I have get the subtotal value and discount value from $order object and it worked.
$order = $this->getOrder ();
$subtotaldisc = $order ['subtotal'] + $order ['discount_amount']; //discount amount returns negative value, so add
$result = $order->formatPriceTxt ( $subtotaldisc );
$fontSize = $this->getFontSize () ? $this->getFontSize () : 10;
$totals = array (
array (
'label' => 'Subtotal with Discount',
'amount' => $result,
'font_size' => $fontSize
)
);
return $totals;
I follow #Wakanina answer and change config.xml - path to pdf mode:
<pdf>
<totals>
<rowtotalbezpdv translate="title">
<title>Ukupno s popustom</title>
<source_field>rowtot_alamount</source_field>
<model>Devpassion_Rowtotalbezpdv_Model_Pdf_Total_Totalbezpdf</model>
<font_size>7</font_size>
<display_zero>0</display_zero>
<sort_order>300</sort_order>
</rowtotalbezpdv>
</totals>
</pdf>
And totalpdf Model class:
class Devpassion_Rowtotalbezpdv_Model_Pdf_Total_Totalbezpdf extends Mage_Sales_Model_Order_Pdf_Total_Default
{
public function getTotalsForDisplay(){
$amount = $this->getAmount();
$fontSize = $this->getFontSize() ? $this->getFontSize() : 7;
if(floatval($amount)){
$amount = $this->getOrder()->formatPriceTxt($amount);
$totals = array(
array(
'label' => 'Cijena knjige/a s popustom bez PDV-a',
'amount' => $amount,
'font_size' => $fontSize,
)
);
return $totals;
}
}
public function getAmount(){
$order = $this->getOrder();
$subtotaldiscnopdv = 0;
foreach ($order->getAllItems() as $item) {
$subtotaldiscnopdv += $item->getRowTotal() - $item->getDiscountAmount();
}
return $subtotaldiscnopdv;
}
}
This helps me out and solve my issue.
I want to auto fill the shipping method automatically and not show it on the onepage checkout. I was able to hide the shipping method on my Onepagechekout by changing this on app/code/local/Mage/Checkout/controllers/OnepageController.php:
/**
* save checkout billing address
*/
public function saveBillingAction()
{
if ($this->_expireAjax()) {
return;
}
if ($this->getRequest()->isPost()) {
// $postData = $this->getRequest()->getPost('billing', array());
// $data = $this->_filterPostData($postData);
$data = $this->getRequest()->getPost('billing', array());
$customerAddressId = $this->getRequest()->getPost('billing_address_id', false);
if (isset($data['email'])) {
$data['email'] = trim($data['email']);
}
$result = $this->getOnepage()->saveBilling($data, $customerAddressId);
if (!isset($result['error'])) {
/* check quote for virtual */
if ($this->getOnepage()->getQuote()->isVirtual())
{
$result['goto_section'] = 'payment';
$result['update_section'] = array(
'name' => 'payment-method',
'html' => $this->_getPaymentMethodsHtml()
);
} elseif (isset($data['use_for_shipping']) && $data['use_for_shipping'] == 1)
{
$result['goto_section'] = 'payment';
$result['update_section'] = array(
'name' => 'payment-method',
'html' => $this->_getPaymentMethodsHtml()
);
$result['allow_sections'] = array('shipping');
$result['duplicateBillingInfo'] = 'true';
} else {
$result['goto_section'] = 'shipping';
}
}
$this->saveShippingMethodAction();
$this->getResponse()->setBody(Mage::helper('core')->jsonEncode($result));
}
}
As you can see I changed the link where I redirect the next step after billing action to the payment step.
In order to auto-save a shipping method, I added
$this->saveShippingMethodAction();
at the end of the function, and this method looks like here:
public function saveShippingMethodAction()
{
$this->_expireAjax();
if ($this->getRequest()->isPost()) {
/* $this->savePaymentAction(); */
$data = $this->getRequest()->getPost('shipping_method', 'flatrate_flatrate');
$result = $this->getOnepage()->saveShippingMethod($data);
$this->getResponse()->setBody(Zend_Json::encode($result));
}
}
So what I did is try to include automatically flatrate_flatrate method as the default one.
But when I try to finish a sale, it says I didn't specify a shipping method. Any idea on why it doesn't work?
Now you need to force the shipping_method in the quote and in the session :
$forcedMethod = "your_method_code";
$this->getOnePage()->getQuote()
->getShippingAddress()
->setShippingMethod($forcedMethod)
->save();
Mage::getSingleton('checkout/session')->setShippingMethod($forcedMethod);
And just before you call :
$this->saveShippingMethodAction();
add a :
$this->getRequest()->setPost('shipping_method', $forcedMethod);
It should work ;)
The paypal module tries to map the billing information that is returned (usually nothing) from Paypal over the billing information entered by the user during the checkout process. I've fond the code that does this in NVP.php model.
/**
* Create billing and shipping addresses basing on response data
* #param array $data
*/
protected function _exportAddressses($data)
{
$address = new Varien_Object();
Varien_Object_Mapper::accumulateByMap($data, $address, $this->_billingAddressMap);
$address->setExportedKeys(array_values($this->_billingAddressMap));
$this->_applyStreetAndRegionWorkarounds($address);
$this->setExportedBillingAddress($address);
// assume there is shipping address if there is at least one field specific to shipping
if (isset($data['SHIPTONAME'])) {
$shippingAddress = clone $address;
Varien_Object_Mapper::accumulateByMap($data, $shippingAddress, $this->_shippingAddressMap);
$this->_applyStreetAndRegionWorkarounds($shippingAddress);
// PayPal doesn't provide detailed shipping name fields, so the name will be overwritten
$shippingAddress->addData(array(
'prefix' => null,
'firstname' => $data['SHIPTONAME'],
'middlename' => null,
'lastname' => null,
'suffix' => null,
));
$this->setExportedShippingAddress($shippingAddress);
}
}
/**
* Adopt specified address object to be compatible with Magento
*
* #param Varien_Object $address
*/
protected function _applyStreetAndRegionWorkarounds(Varien_Object $address)
{
// merge street addresses into 1
if ($address->hasStreet2()) {
$address->setStreet(implode("\n", array($address->getStreet(), $address->getStreet2())));
$address->unsStreet2();
}
// attempt to fetch region_id from directory
if ($address->getCountryId() && $address->getRegion()) {
$regions = Mage::getModel('directory/country')->loadByCode($address->getCountryId())->getRegionCollection()
->addRegionCodeFilter($address->getRegion())
->setPageSize(1)
;
foreach ($regions as $region) {
$address->setRegionId($region->getId());
$address->setExportedKeys(array_merge($address->getExportedKeys(), array('region_id')));
break;
}
}
}
Has anyone had any success modifying this process to get back fuller billing information. We need to be able to send "Paid" invoices to customers who pay with Paypal, so we need to capture this information.
i have hacked a workaround for that problem. It is not the cleanest way but i can save the billing address data in the order after paying with PayPal. I spent 2 days working on it and at the end i coded only a few lines. I marked my workaround with the comment #103.
Override method of class Mage_Paypal_Model_Api_Nvp:
protected function _importAddresses(array $to)
{
// Original Code
//$billingAddress = ($this->getBillingAddress()) ? $this->getBillingAddress() : $this->getAddress();
// Workaround #103
if ($this->getBillingAddress())
{
$billingAddress = $this->getBillingAddress();
}
else
{
$chkout = Mage::getSingleton('checkout/session');
$quote = $chkout->getQuote();
$billingAddress = $quote->getBillingAddress();
$billingAddress->setData($billingAddress->getOrigData());
$session = Mage::getSingleton("core/session", array("name"=>"frontend"));
$session->setData("syn_paypal_original_billing_address", serialize($billingAddress->getOrigData()));
}
$shippingAddress = $this->getAddress();
$to = Varien_Object_Mapper::accumulateByMap($billingAddress, $to, array_flip($this->_billingAddressMap));
if ($regionCode = $this->_lookupRegionCodeFromAddress($billingAddress)) {
$to['STATE'] = $regionCode;
}
if (!$this->getSuppressShipping()) {
$to = Varien_Object_Mapper::accumulateByMap($shippingAddress, $to, array_flip($this->_shippingAddressMap));
if ($regionCode = $this->_lookupRegionCodeFromAddress($shippingAddress)) {
$to['SHIPTOSTATE'] = $regionCode;
}
$this->_importStreetFromAddress($shippingAddress, $to, 'SHIPTOSTREET', 'SHIPTOSTREET2');
$this->_importStreetFromAddress($billingAddress, $to, 'STREET', 'STREET2');
$to['SHIPTONAME'] = $shippingAddress->getName();
}
$this->_applyCountryWorkarounds($to);
return $to;
}
And override method in Mage_Paypal_Model_Express_Checkout:
public function returnFromPaypal($token)
{
$this->_getApi();
$this->_api->setToken($token)
->callGetExpressCheckoutDetails();
// import billing address
$billingAddress = $this->_quote->getBillingAddress();
$exportedBillingAddress = $this->_api->getExportedBillingAddress();
// Workaround #103
$session = Mage::getSingleton("core/session", array("name"=>"frontend"));
$dataOrg = unserialize($session->getData("syn_paypal_original_billing_address"));
if (true === is_object($billingAddress))
{
foreach ($exportedBillingAddress->getExportedKeys() as $key) {
if (array_key_exists($key, $dataOrg))
{
$billingAddress->setData($key, $dataOrg[$key]);
}
}
$this->_quote->setBillingAddress($billingAddress);
}
// import shipping address
$exportedShippingAddress = $this->_api->getExportedShippingAddress();
if (!$this->_quote->getIsVirtual()) {
$shippingAddress = $this->_quote->getShippingAddress();
if ($shippingAddress) {
if ($exportedShippingAddress) {
foreach ($exportedShippingAddress->getExportedKeys() as $key) {
$shippingAddress->setDataUsingMethod($key, $exportedShippingAddress->getData($key));
}
$shippingAddress->setCollectShippingRates(true);
}
// import shipping method
$code = '';
if ($this->_api->getShippingRateCode()) {
if ($code = $this->_matchShippingMethodCode($shippingAddress, $this->_api->getShippingRateCode())) {
// possible bug of double collecting rates :-/
$shippingAddress->setShippingMethod($code)->setCollectShippingRates(true);
}
}
$this->_quote->getPayment()->setAdditionalInformation(self::PAYMENT_INFO_TRANSPORT_SHIPPING_METHOD, $code);
}
}
$this->_ignoreAddressValidation();
// import payment info
$payment = $this->_quote->getPayment();
$payment->setMethod($this->_methodType);
Mage::getSingleton('paypal/info')->importToPayment($this->_api, $payment);
$payment->setAdditionalInformation(self::PAYMENT_INFO_TRANSPORT_PAYER_ID, $this->_api->getPayerId())
->setAdditionalInformation(self::PAYMENT_INFO_TRANSPORT_TOKEN, $token)
;
$this->_quote->collectTotals()->save();
}