Following is the observer.php file for an extension that restricts Cash On Delivery Payment Method Based On Pincode On The Checkout Page.
This extension works perfectly fine with the built in Cash On Delivery Payment Method in Magento.
My issue is that, when a customer on my website goes to the checkout page to complete his order, the cash on delivery payment method is not visible initially. Only when the customer enters his Zip Code, and if that particular zip code is available for COD, the COD payment method appears. If that zipcode is not eligible, the COD option continues to be invisible.
I want the COD option to be visible initially when the zip code has not been entered and after the customer enters the zip code and if that zip code is not available, a message should be displayed saying that COD is not available.
I know this particular code needs to be altered a bit to achieve that. Kindly help me out if possible.
Observer.php
class Mfp_Cod_Model_Observer {
public function getCashOnDelvery(Varien_Event_Observer $observer)
{
$event = $observer->getEvent();
$method = $event->getMethodInstance();
$result = $event->getResult();
$isModuleEnable = Mage::getStoreConfig('cod/cod/enable');
if ($isModuleEnable) {
if ($method->getCode() == 'msp_cashondelivery' ) {
$quote = Mage::getSingleton('checkout/cart')->getQuote();
$add = $quote->getShippingAddress();
$postcode = $add->getData('postcode');
$comparisonMode = Mage::getStoreConfig('cod/cod/mode');
$zipCodes = Mage::getStoreConfig('cod/cod/zipcode');
$isExist = false;
if (trim($zipCodes) == '') {
$result->isAvailable = true;
} else {
if(strpos($zipCodes, $postcode) !== false) {
$isExist = true;
}
if ($isExist != true) {
$zipCodesArray = explode(',', nl2br($zipCodes));
if (count($elementLineArray) > 1) {
foreach ($zipCodesArray as $codzipLine) {
$elementLineArray = explode('-', $codzipLine);
if (count($elementLineArray) == 2 && ( $postcode >= $elementLineArray[0] && $postcode <= $elementLineArray[1] )) {
$isExist = true;
break;
} else if($postcode == $codzipLine) {
$isExist = true;
break;
}
}
}
}
$returnValue = '';
$returnValue = ($isExist)?true:false;
$result->isAvailable = $returnValue;
}
}
}
}
}
you can simply check if zipcode is not added like :
after
$postcode = $add->getData('postcode');
add
if(!isset($postcode) || empty($postcode)) {
return true;
}
In order to get shipping address of checkout page use
$address = $observer->getEvent()->getOrder()
->getShippingAddress();
in $address object you will get complete information of shipping address including postcode.
Related
I'm creating a buy-now button cs-cart add-on. I've created an add-on and I've made the add-to-cart functionality work. But cant redirect it to checkout page. I have used "fn_redirect("checkout")" function for the redirection. And used this function below this:
"fn_add_product_to_cart($_REQUEST['product_data'], $cart, $auth);"
What should I do for redirection?
Edit:
My controller (buy_now.php)
if ($mode == 'add') {
if (empty($auth['user_id']) && Registry::get('settings.General.allow_anonymous_shopping') != 'allow_shopping')
{
return array(CONTROLLER_STATUS_REDIRECT, "auth.login_form?return_url=" . urlencode($_REQUEST['return_url']));
}
// Add to cart button was pressed for single product on advanced list
if (!empty($dispatch_extra)) {
if (empty($_REQUEST['product_data'][$dispatch_extra]['amount'])) {
$_REQUEST['product_data'][$dispatch_extra]['amount'] = 1;
}
foreach ($_REQUEST['product_data'] as $key => $data) {
if ($key != $dispatch_extra && $key != 'custom_files') {
unset($_REQUEST['product_data'][$key]);
}
}
}
$prev_cart_products = empty($cart['products']) ? array() : $cart['products'];
fn_add_product_to_cart($_REQUEST['product_data'], $cart, $auth);
fn_save_cart_content($cart, $auth['user_id']);
$previous_state = md5(serialize($cart['products']));
$cart['change_cart_products'] = true;
fn_calculate_cart_content($cart, $auth, 'S', true, 'F', true);
if (md5(serialize($cart['products'])) != $previous_state && empty($cart['skip_notification'])) {
$product_cnt = 0;
$added_products = array();
if (!empty($added_products)) {
Registry::get('view')->assign('added_products', $added_products);
if (Registry::get('config.tweaks.disable_dhtml') && Registry::get('config.tweaks.redirect_to_cart')) {
Registry::get('view')->assign('continue_url', (!empty($_REQUEST['redirect_url']) && empty($_REQUEST['appearance']['details_page'])) ? $_REQUEST['redirect_url'] : $_SESSION['continue_url']);
}
fn_redirect(Registry::get('config.https_location') . "/checkout");
// $msg = Registry::get('view')->fetch('views/checkout/components/product_notification.tpl');
//fn_set_notification('I', __($product_cnt > 1 ? 'products_added_to_cart' : 'product_added_to_cart'), $msg, 'I');
$cart['recalculate'] = true;
} else {
fn_set_notification('N', __('notice'), __('product_in_cart'));
}
}
unset($cart['skip_notification']);
$_suffix = '.checkout';
}
`
hook template : add_to_cart.post.tpl
{$id = "buy_now_{$product.product_id}"}
<button id="opener_{$id}" name="dispatch[buy_now.add..{$product.product_id}]" class=" vs-button buynow_btn_">Buy Now</button>
I'd consider putting your fn_redirect("checkout") code into an if/else statement to see what's happening with it.
If you have this code:
fn_add_product_to_cart($_REQUEST['product_data'], $cart, $auth);
Then surely you can put the redirection to the checkout page underneath it?
$redirect_url = "http://testcheckout.com?test=test"; //change this!
$errorMessage = "I should have redirected already....";
if (!empty($redirect_url)) {
//if it's not empty then it should redirect.
fn_redirect($redirect_url);
//Then put a die statement in here just to check it's not executing
//anything else and you can see if the redirect has a problem:
die(print_r($errorMessage, true ));
}
else {
//otherwise there is a problem with the supplied redirect url (empty)
die(print_r($redirect_url, true ));
}
I'd also take a look at this page which might help more:
http://forum.cs-cart.com/topic/6873-direct-buy-button/
Hope that helps!
I have an extension to provide different rates for shipping, this module has got an option in the admin panel that let you choose the free shipping method if the cart price rule is met( if cart price rule requirement are respected, then set this method as free).
The problem is that I can choose only one method, but in the same country I offer 2 different carrier to delivery the package (lowest price for different regions) and this is a problem.
The extension save the value in the core_config_data table under path=carriers/premiumrate/free_method. I don't have clear how magento access this information so I have run these comand through SSH:
grep -rnw 'app' -e "carriers/premiumrate/free_method"
with no result, so I have tried:
grep -rnw 'app' -e "free_method"
Results:
app/code/core/Mage/Shipping/Helper/Data.php:159: $freeMethod = Mage::getStoreConfig('carriers/' . $arr[0] . '/free_method', $storeId);
app/code/core/Mage/Shipping/Model/Carrier/Abstract.php:56: protected $_freeMethod = 'free_method';
Next search:
grep -rnw 'app' -e "freeMethod"
Result:
app/code/core/Mage/Shipping/Helper/Data.php:159: $freeMethod = Mage::getStoreConfig('carriers/' . $arr[0] . '/free_method', $storeId);
app/code/core/Mage/Shipping/Helper/Data.php:160: return $freeMethod == $arr[1];
app/code/core/Mage/Shipping/Model/Carrier/Abstract.php:343: $freeMethod = $this->getConfigData($this->_freeMethod);
app/code/core/Mage/Shipping/Model/Carrier/Abstract.php:344: if (!$freeMethod) {
app/code/core/Mage/Shipping/Model/Carrier/Abstract.php:351: if ($item->getMethod() == $freeMethod) {
app/code/core/Mage/Shipping/Model/Carrier/Abstract.php:363: $this->_setFreeMethodRequest($freeMethod);
app/code/core/Mage/Shipping/Model/Carrier/Abstract.php:373: && $rate->getMethod() == $freeMethod
So I opened app/code/core/Mage/Shipping/Model/Carrier/Abstract.php and these are the lines (370-378; I have added the complete function at the bottom) that I have found interesting:
if (count($rates) > 1) {
foreach ($rates as $rate) {
if ($rate instanceof Mage_Shipping_Model_Rate_Result_Method
&& $rate->getMethod() == $freeMethod
) {
$price = $rate->getPrice();
}
}
}
Is it the correct place that I have to edit?
if I remove the if condition, will I destroy my site?
What should I do to set any shipping free if the cart rule is
active/valid?
Is there a better way to find out what I have to edit
instead of random grep?
Complete function:
protected function _updateFreeMethodQuote($request)
{
if ($request->getFreeMethodWeight() == $request->getPackageWeight() || !$request->hasFreeMethodWeight()) {
return;
}
$freeMethod = $this->getConfigData($this->_freeMethod);
if (!$freeMethod) {
return;
}
$freeRateId = false;
if (is_object($this->_result)) {
foreach ($this->_result->getAllRates() as $i=>$item) {
if ($item->getMethod() == $freeMethod) {
$freeRateId = $i;
break;
}
}
}
if ($freeRateId === false) {
return;
}
$price = null;
if ($request->getFreeMethodWeight() > 0) {
$this->_setFreeMethodRequest($freeMethod);
$result = $this->_getQuotes();
if ($result && ($rates = $result->getAllRates()) && count($rates)>0) {
if ((count($rates) == 1) && ($rates[0] instanceof Mage_Shipping_Model_Rate_Result_Method)) {
$price = $rates[0]->getPrice();
}
if (count($rates) > 1) {
foreach ($rates as $rate) {
if ($rate instanceof Mage_Shipping_Model_Rate_Result_Method
&& $rate->getMethod() == $freeMethod
) {
$price = $rate->getPrice();
}
}
}
}
} else {
/**
* if we can apply free shipping for all order we should force price
* to $0.00 for shipping with out sending second request to carrier
*/
$price = 0;
}
/**
* if we did not get our free shipping method in response we must use its old price
*/
if (!is_null($price)) {
$this->_result->getRateById($freeRateId)->setPrice($price);
}
}
i'm making some tests to develop a custom payment gateway addon in cscart
The development has been very simple and intuitive, but there is a thing i am going crazy
for 1 week. After the transaction as been completed the user is redirected to index.php?dispatch=checkout.complete&order_id=20036 but the green popup notification dosn't appear as on the other payments....
The code looks like normal.. i look on all other payments script and all is regular
the final function in /core/fn.cart.php fn_order_placement_routines valorize correctly the $_SESSION var with the notification data fn_set_notification('N',.....
this is the code
if (!defined('AREA') ) { die('Access denied'); }
if (defined('PAYMENT_NOTIFICATION')) {
if ($mode == 'notify' && !empty($_REQUEST['order_id'])) {
if (fn_check_payment_script('gateway.php', $_REQUEST['order_id'], $processor_data)) {
$order_id = $_REQUEST['order_id'];
$order_info = fn_get_order_info($order_id);
$pp_response = array(
'reason_text' => '',
'order_status' => 'F'
);
if (empty($processor_data)) {
$processor_data = fn_get_processor_data($order_info['payment_id']);
}
$returnvalue = $_POST['PROCESSING_RESULT'];
if ($returnvalue && strstr($returnvalue,"ACK")) {
$pp_response['order_status'] = "E";
$pp_response['reason_text'] .= "Status: OK";
}else {
$pp_response['order_status'] = "N";
$pp_response["reason_text"] = fn_get_lang_var('text_transaction_cancelled');
}
if (isSet($_REQUEST['IDENTIFICATION_UNIQUEID'])) {
$pp_response['transaction_id'] = $_REQUEST['IDENTIFICATION_UNIQUEID'];
}
[b] fn_finish_payment($_REQUEST['order_id'], $pp_response, false);
fn_order_placement_routines($_REQUEST['order_id'], true);[/b]
}
}
} else {
if ($mode == 'place_order') {
//call the gateway, assign response url etc
// $current_location."/".$index_script."?dispatch=payment_notification.notify&payment=gateway.php&order_id=".$order_id;
}
}
the fn_order_place_routine should be show by default the green or red popup based on the status of the order.... nothing.. dosn't appear
thanks in advance
As I can see you are using custom order status for sucessfull transaction:
$pp_response['order_status'] = "E";
So, to display notification the 'inventory' option for this status should be set as 'Decreased'. In this case the E status will be included in the result of the fn_get_order_paid_statuses() function and the following code in the fn_order_placement_routines function will work:
if (in_array($status, fn_get_order_paid_statuses())) {
if ($action == 'repay') {
fn_set_notification('N', __('congratulations'), __('text_order_repayed_successfully'));
} else {
fn_set_notification('N', __('order_placed'), __('text_order_placed_successfully'));
}
}
Best regards.
The default form.js file appears to be erroring when "add to cart" is clicked. This prevents the value of the quantity field from correctly being passed. Magento substitutes the default value instead. This script doesn't error and the quantity field works just fine in other browsers.
Furthermore, the add to cart button works correctly when I switch back to the default theme. Any ideas on where I should start. does anything here stand out as not being IE8 friendly? I haven't modified any of the add to cart functions, nor the form.js file.
Update: I have a browserstack account. Debugging shows that VarienForm is undefined which throws two "'productAddToCartForm' is null or not an object" errors. Form.js which defines VarienForm is being loaded in the header so it should be available for the inline JS.
since you can't see line numbers here is the line in question:
this.regionSelectEl.options.add(option);
Here is the function:
update: function()
{
if (this.regions[this.countryEl.value]) {
var i, option, region, def;
def = this.regionSelectEl.getAttribute('defaultValue');
if (this.regionTextEl) {
if (!def) {
def = this.regionTextEl.value.toLowerCase();
}
this.regionTextEl.value = '';
}
this.regionSelectEl.options.length = 1;
for (regionId in this.regions[this.countryEl.value]) {
region = this.regions[this.countryEl.value][regionId];
option = document.createElement('OPTION');
option.value = regionId;
option.text = region.name.stripTags();
option.title = region.name;
if (this.regionSelectEl.options.add) {
this.regionSelectEl.options.add(option); //***this is line 266***
} else {
this.regionSelectEl.appendChild(option);
}
if (regionId==def || (region.name && region.name.toLowerCase()==def) ||
(region.name && region.code.toLowerCase()==def)
) {
this.regionSelectEl.value = regionId;
}
}
if (this.disableAction=='hide') {
if (this.regionTextEl) {
this.regionTextEl.style.display = 'none';
}
this.regionSelectEl.style.display = '';
} else if (this.disableAction=='disable') {
if (this.regionTextEl) {
this.regionTextEl.disabled = true;
}
this.regionSelectEl.disabled = false;
}
this.setMarkDisplay(this.regionSelectEl, true);
} else {
if (this.disableAction=='hide') {
if (this.regionTextEl) {
this.regionTextEl.style.display = '';
}
this.regionSelectEl.style.display = 'none';
Validation.reset(this.regionSelectEl);
} else if (this.disableAction=='disable') {
if (this.regionTextEl) {
this.regionTextEl.disabled = false;
}
this.regionSelectEl.disabled = true;
} else if (this.disableAction=='nullify') {
this.regionSelectEl.options.length = 1;
this.regionSelectEl.value = '';
this.regionSelectEl.selectedIndex = 0;
this.lastCountryId = '';
}
this.setMarkDisplay(this.regionSelectEl, false);
}
Region selection should only be triggered in cart or checkout. Check if your template uses own JS-Files in skin/frontend/your_theme/js. I think your template was made for a different Magento version so the CSS-Selectors in your phtml-files don't fit the JS anymore. So you have either to use Matching JS-Files or compare your product template to the one in base template and adapt CSS classes.
I am working with SilverStripe, and I am working on making a newspage.
I use the DataObjectAsPage Module( http://www.ssbits.com/tutorials/2012/dataobject-as-pages-the-module/ ), I got it working when I use the admin to publish newsitems.
Now I want to use the DataObjectManager Module instead of the admin module to manage my news items. But this is where the problem exists. Everything works fine in draft mode, I can make a new newsitem and it shows up in draft. But when I want to publish a newsitem, it won't show up in the live or published mode.
I'm using the following tables:
-Dataobjectaspage table,
-Dataobjectaspage_live table,
-NewsArticle table,
-NewsArticle_Live table
The Articles have been inserted while publishing in the Dataobjectaspage table and in the NewsArticle table... But not in the _Live tables...
Seems the doPublish() function hasn't been used while 'Publishing'.
So I'm trying the use the following:
function onAfterWrite() {
parent::onAfterWrite();
DataObjectAsPage::doPublish();
}
But when I use this, it gets an error:
here is this picture
It seems to be in a loop....
I've got the NewsArticle.php file where I use this function:
function onAfterWrite() {
parent::onAfterWrite();
DataObjectAsPage::doPublish();
}
This function calls the DataObjectAsPage.php file and uses this code:
function doPublish() {
if (!$this->canPublish()) return false;
$original = Versioned::get_one_by_stage("DataObjectAsPage", "Live", "\"DataObjectAsPage\".\"ID\" = $this->ID");
if(!$original) $original = new DataObjectAsPage();
// Handle activities undertaken by decorators
$this->invokeWithExtensions('onBeforePublish', $original);
$this->Status = "Published";
//$this->PublishedByID = Member::currentUser()->ID;
$this->write();
$this->publish("Stage", "Live");
// Handle activities undertaken by decorators
$this->invokeWithExtensions('onAfterPublish', $original);
return true;
}
And then it goes to DataObject.php file and uses the write function ():
public function write($showDebug = false, $forceInsert = false, $forceWrite = false, $writeComponents = false) {
$firstWrite = false;
$this->brokenOnWrite = true;
$isNewRecord = false;
if(self::get_validation_enabled()) {
$valid = $this->validate();
if(!$valid->valid()) {
// Used by DODs to clean up after themselves, eg, Versioned
$this->extend('onAfterSkippedWrite');
throw new ValidationException($valid, "Validation error writing a $this->class object: " . $valid->message() . ". Object not written.", E_USER_WARNING);
return false;
}
}
$this->onBeforeWrite();
if($this->brokenOnWrite) {
user_error("$this->class has a broken onBeforeWrite() function. Make sure that you call parent::onBeforeWrite().", E_USER_ERROR);
}
// New record = everything has changed
if(($this->ID && is_numeric($this->ID)) && !$forceInsert) {
$dbCommand = 'update';
// Update the changed array with references to changed obj-fields
foreach($this->record as $k => $v) {
if(is_object($v) && method_exists($v, 'isChanged') && $v->isChanged()) {
$this->changed[$k] = true;
}
}
} else{
$dbCommand = 'insert';
$this->changed = array();
foreach($this->record as $k => $v) {
$this->changed[$k] = 2;
}
$firstWrite = true;
}
// No changes made
if($this->changed) {
foreach($this->getClassAncestry() as $ancestor) {
if(self::has_own_table($ancestor))
$ancestry[] = $ancestor;
}
// Look for some changes to make
if(!$forceInsert) unset($this->changed['ID']);
$hasChanges = false;
foreach($this->changed as $fieldName => $changed) {
if($changed) {
$hasChanges = true;
break;
}
}
if($hasChanges || $forceWrite || !$this->record['ID']) {
// New records have their insert into the base data table done first, so that they can pass the
// generated primary key on to the rest of the manipulation
$baseTable = $ancestry[0];
if((!isset($this->record['ID']) || !$this->record['ID']) && isset($ancestry[0])) {
DB::query("INSERT INTO \"{$baseTable}\" (\"Created\") VALUES (" . DB::getConn()->now() . ")");
$this->record['ID'] = DB::getGeneratedID($baseTable);
$this->changed['ID'] = 2;
$isNewRecord = true;
}
// Divvy up field saving into a number of database manipulations
$manipulation = array();
if(isset($ancestry) && is_array($ancestry)) {
foreach($ancestry as $idx => $class) {
$classSingleton = singleton($class);
foreach($this->record as $fieldName => $fieldValue) {
if(isset($this->changed[$fieldName]) && $this->changed[$fieldName] && $fieldType = $classSingleton->hasOwnTableDatabaseField($fieldName)) {
$fieldObj = $this->dbObject($fieldName);
if(!isset($manipulation[$class])) $manipulation[$class] = array();
// if database column doesn't correlate to a DBField instance...
if(!$fieldObj) {
$fieldObj = DBField::create('Varchar', $this->record[$fieldName], $fieldName);
}
// Both CompositeDBFields and regular fields need to be repopulated
$fieldObj->setValue($this->record[$fieldName], $this->record);
if($class != $baseTable || $fieldName!='ID')
$fieldObj->writeToManipulation($manipulation[$class]);
}
}
// Add the class name to the base object
if($idx == 0) {
$manipulation[$class]['fields']["LastEdited"] = "'".SS_Datetime::now()->Rfc2822()."'";
if($dbCommand == 'insert') {
$manipulation[$class]['fields']["Created"] = "'".SS_Datetime::now()->Rfc2822()."'";
//echo "<li>$this->class - " .get_class($this);
$manipulation[$class]['fields']["ClassName"] = "'$this->class'";
}
}
// In cases where there are no fields, this 'stub' will get picked up on
if(self::has_own_table($class)) {
$manipulation[$class]['command'] = $dbCommand;
$manipulation[$class]['id'] = $this->record['ID'];
} else {
unset($manipulation[$class]);
}
}
}
$this->extend('augmentWrite', $manipulation);
// New records have their insert into the base data table done first, so that they can pass the
// generated ID on to the rest of the manipulation
if(isset($isNewRecord) && $isNewRecord && isset($manipulation[$baseTable])) {
$manipulation[$baseTable]['command'] = 'update';
}
DB::manipulate($manipulation);
if(isset($isNewRecord) && $isNewRecord) {
DataObjectLog::addedObject($this);
} else {
DataObjectLog::changedObject($this);
}
$this->onAfterWrite();
$this->changed = null;
} elseif ( $showDebug ) {
echo "<b>Debug:</b> no changes for DataObject<br />";
// Used by DODs to clean up after themselves, eg, Versioned
$this->extend('onAfterSkippedWrite');
}
// Clears the cache for this object so get_one returns the correct object.
$this->flushCache();
if(!isset($this->record['Created'])) {
$this->record['Created'] = SS_Datetime::now()->Rfc2822();
}
$this->record['LastEdited'] = SS_Datetime::now()->Rfc2822();
} else {
// Used by DODs to clean up after themselves, eg, Versioned
$this->extend('onAfterSkippedWrite');
}
// Write ComponentSets as necessary
if($writeComponents) {
$this->writeComponents(true);
}
return $this->record['ID'];
}
Look at the $this->onAfterWrite();
It probably goes to my own function on NewsArticle.php and there starts the loop! I'm not sure though, so i could need some help!!
Does anyone knows how to use the doPublish() function?
The reason that is happening is that in the DataObjectAsPage::publish() method, it is calling ->write() - line 11 of your 3rd code sample.
So what happens is it calls ->write(), at the end of ->write() your onAfterWrite() method is called, which calls publish(), which calls write() again.
If you remove the onAfterWrite() function that you've added, it should work as expected.
The doPublish() method on DataObjectAsPage will take care of publishing from Stage to Live for you.