I'm struggling with a very stupid problem.
I've edited the saveOrder() method in app/code/core/Mage/Checkout/Type/Onepage.php.
This because I wanted to prevent Magento from sending order confirmation email for some payment methods.
Instead of standard emails I'm sending a new one (coded in transactional emails in backend) with different information.
All it's ok, I've done something like:
if($order->getPayment()->getMethodInstance()->getCode()!='X') {
$order->sendNewOrderEmail();
} else {
$name = $order->getBillingAddress()->getName();
$mailer = Mage::getModel('core/email_template_mailer');
$emailInfo = Mage::getModel('core/email_info');
$emailInfo->addTo($order->getCustomerEmail(), $name);
$mailer->addEmailInfo($emailInfo);
$templateId = 3;
$storeId = Mage::app()->getStore()->getId();
$sender = Array('name' => 'XXX', 'email' => 'xxx#xxx.xxx');
$mailer->setSender($sender);
$mailer->setStoreId($storeId);
$mailer->setTemplateId($templateId);
$mailer->setTemplateParams(array(
'order' => $order
)
);
$mailer->send();
}
All works fine except for the total of the order. In the transactional email I'm printing
{{var order.getGrandTotal()}}
but I'm getting the value "0.999953719008" for a 1 euro price product and I don't know how to solve this. (The test product has got a discount)
I've tried creating a script which loads a previously registered order and sends the email using the same email template. In this case all works like a charm!
So I suppose that the problem is because the order isn't saved yet.
I've just tried passing the grand total as another variable using
$mailer->setTemplateParams(array(
'order' => $order,
'total' => $order->getGrandTotal()
)
);
and printing
{{var total}}
in the template and in this case there isn't any value for the variable.
How can I manage to solve this?
Thank in advance!
p.s.: I'm using an installation of 1.6 version of Magento.
I've had this before as well. It's due to php float rounding issues:
http://php.net/manual/en/language.types.float.php
Use round() to display your results correctly to the user. With bcadd() you can avoid the rounding issues if you change the price somewhere with custom code.
Related
How can I get Current date in Magento email template?
{{var dateAndTime}} doesn't work. I need to get current date in which the email is sent.
You have 2 options.
Option 1. is to rewrite the method that sends the e-mail and pass the current date as a parameter.
Let's say for example that you want to show the date in the order e-mail.
For that you will need to rewrite the Mage_Sales_Model_Order::sendNewOrderEmail method.
You need to change this:
$mailer->setTemplateParams(array(
'order' => $this,
'billing' => $this->getBillingAddress(),
'payment_html' => $paymentBlockHtml
)
);
To this:
$mailer->setTemplateParams(array(
'order' => $this,
'billing' => $this->getBillingAddress(),
'payment_html' => $paymentBlockHtml,
'dateAndTime' => Mage::getModel('core/date')->date('Y-m-d H:i:s'), //change format as needed.
)
);
Then you will be able to use {{var dateAndTime}} in your new order email template.
This is handy if you want to use your date and time in only one template.
If you want a more general case you need to create your own directive see option 2.
Option 2 creating your own {{}} directive.
Let's say that you want in every e-mail to use {{dateAndTime}} to print to current date and time.
You need to rewrite the class Mage_Widget_Model_Template_Filter and add a new method to it.
See this detailed explanation about how to do it.
Your new method should look like this:
public function dateAndTimeDirective($construction) {
return Mage::getModel('core/date')->date('Y-m-d H:i:s');
}
You can even take it up a notch and be able to pass the date format as a parameter like this:
{{dateAndTime format="Y-m-d"}}
in this case your method that handles the directive should look like this:
public function dateAndTimeDirective($construction) {
$params = $this->_getIncludeParameters($construction[2]);
$format = isset($params['format']) ? $params['format'] : 'Y-m-d H:i:s'
return Mage::getModel('core/date')->date($format);
}
Go to your email template from admin panel. Use block directive as follows
current date is :
{{block type='core/template' area='frontend' template='page/html/date.phtml'}}
Now create a new file date.phtml in your template as follows
app/design/frontend/yourtheme/yourtemplate/page/html/date.phtml
Add following code in php tags (use php date function and echo current date i.e date('d-m-Y'))
<pre><code>
$date = date('d-m-Y');
echo $date;
</code></pre>
Hope this helps!
I am using the omnipay setup here: https://github.com/adrianmacneil/omnipay to process a paypal express checkout.
The process works fine in that the user is redirected to paypal -> they login and choose to pay -> they get returned to my site at which point I capture the payment.
The problem I've got is that I need to capture the address they have entered into paypal as their billing / shipping address.
To send the user across to paypal I have the following:
$gateway = GatewayFactory::create('PayPal_Express');
$gateway->setUsername('XX-USERNAME_XX');
$gateway->setPassword('XX_PASSWORDXX');
$gateway->setSignature('XX_SIG_XX');
$gateway->setTestMode(true);
$response = $gateway->purchase(
array(
'cancelUrl'=>'http://www.XXX.co.uk/',
'returnUrl'=>'http://www.XXX.co.uk/paypalexpress_confirm',
'amount' => $totalamount,
'currency' => 'GBP'
)
)->send();
$response->redirect();
When the user is returned I have the following:
$gateway = GatewayFactory::create('PayPal_Express');
$gateway->setUsername('XX-USERNAME_XX');
$gateway->setPassword('XX_PASSWORDXX');
$gateway->setSignature('XX_SIG_XX');
$gateway->setTestMode(true);
$response = $gateway->completePurchase(
array(
'cancelUrl'=>'http://www.XXX.co.uk/',
'returnUrl'=>'http://www.XXX.co.uk/paypalexpress_confirm',
'amount' => $totalamount,
'currency' => 'GBP'
)
)->send();
echo $responsemsg=$response->getMessage();
echo '<br><br><br>';
$data = $response->getData();
print_r($data);
Nothing in the response message or the raw data contains the customer address.
Has anyone got this working as i'm struggling and it's the final step to complete the transaction.
For those who are trying to get this work it's as Adrian said.
You first do the normal omnipay paypal payment and then afterwards:
get the token you were given
preform a second call to paypal using the call getexpresscheckoutdetails method
this returns all the info you need
API info here: https://cms.paypal.com/uk/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_nvp_r_GetExpressCheckoutDetails
The php script paypal provide to do it all for you:
https://cms.paypal.com/cms_content/ES/es_ES/files/developer/nvp_ECGetExpressCheckout_php.txt
omnipay\paypal\ProGateway.php add new function
public function fetchExpressCheckoutDetail(array $parameters = array())
{
return $this->createRequest('\Omnipay\PayPal\Message\FetchExpressCheckoutRequest', $parameters);
}
omnipay\paypal\src\Message add new file FetchExpressCheckoutRequest.php
namespace Omnipay\PayPal\Message;
class FetchExpressCheckoutRequest extends AbstractRequest
{
public function getData()
{
$data = $this->getBaseData('GetExpressCheckoutDetails');
$this->validate('transactionReference');
$data['TOKEN'] = $this->getTransactionReference();
$url = $this->getEndpoint()."?USER={$data['USER']}&PWD={$data['PWD']}&SIGNATURE={$data['SIGNATURE']}&METHOD=GetExpressCheckoutDetails&VERSION={$data['VERSION']}&TOKEN={$data['TOKEN']}";
parse_str (file_get_contents( $url ),$output);
$data = array_merge($data,$output);
return $data;
}
}
Usage:
$response = $gateway->completePurchase($params)->send();
$data = $response->getData();
$gateway->fetchExpressCheckoutDetail(array('transactionReference'=>$data['TOKEN']))->getData();
It will be not the best. But it works. :)
If it's not returned by the $response->getData() method, you might need to call PayPal's GetExpressCheckoutDetails API method to get the extra details about the transaction.
Omnipay doesn't support this out of the box, so you will probably need to copy and customize one of the existing requests to make a separate API call after you have confirmed payment.
is it somehow possible to use {{var invoice.increment_id}} in the creditmemo e-mails? It don't work if I do so...
<p><strong>Bestellnummer:</strong> {{var order.increment_id}}</p>
<p><strong>Datum:</strong> {{var creditmemo.created_at}}</p>
<p><strong>Rechnung:</strong> {{var invoice.increment_id}}</p>
<p>Liebe(r) {{htmlescape var=$order.getCustomerName()}},<br/><br/>
Doesn't work. Does someone has a hint for me? Thanks!
EDIT
Is that correct?
Edit the class Mage_Sales_Model_Order_Creditmemo and adding:
//Get Invocie from order Object
if ($order->hasInvoices()) {
// "$_eachInvoice" is each of the Invoice object of the order "$order"
foreach ($order->getInvoiceCollection() as $_eachInvoice) {
$invoice = $_eachInvoice->getIncrementId();
}
//$invoice = $order->getInvoiceCollection();
}
aswell as
$mailer->setTemplateParams(array(
'order' => $order,
'creditmemo' => $this,
'comment' => $comment,
'invoice' => $invoice,
'billing' => $order->getBillingAddress()
)
);
and afterwards calling the variable {{var invoice}} in my email template?
But it doesn't show, what I'm missing here?
You should rewrite Mage_Sales_Model_Order_Creditmemo model in your module and add needed logic to sendUpdateEmail() method to get invoice object in credit memo email templates.
See my frist post. I got it right. Thanks to Zyava.
The problem was, you have to place this code one method above the sendUpdateEMail(), in sendEmail().
Thanks!
I have my controller, my function to insert into the database, and the form.
I just want to insert the user in the database if he enter the email is not already registered in the database.
My controller:
$nome = $this->_request->getParam('nome');
$senha = $this->_request->getParam('senha');
$confirmar = $this->_request->getParam('confirmar');
$email = $this->_request->getParam('email');
$usuarios = new Application_Model_DbTable_Usuarios();
$usuarios->addUsuario($nome, $senha, $email);
My DbTable_Usuario class that contains my function that inserts the user in the database
public function addUsuario($nome, $senha, $email) {
$data = array(
'id' => 'NULL',
'nome' => $nome,
'senha' => $senha,
'email' => $email,
'nivel' => '0'
);
$this->insert($data);
}
And my zend_form
$email = new Zend_Form_Element_Text('email');
$email->setLabel('Email:')
->setRequired(true)
->addValidator('EmailAddress')
->addValidator('NotEmpty');
I have some way to add something to the form it checks if the mail entered already exists in the database? If it exists, it displays the message, and if not, insert it in the bank.
Add a Zend_Validate_Db_NoRecordExists validator to the email field.
To give a closer insight on a specific problem using NoRecordExists. The Validator is added just at the way that other validators are
$email->addValidator('Db_NoRecordExists', false, array('table'=>'usario', 'field'=>'email'));
Depending on your form setup, you will use the exact same field for EDITING a user, too. When editing a user the NoRecordExists is a little tricky. As for once, you shouldn't be allowed to change into an existing email, but you should be able to update your other data and keep your email (which though is existing in your db in the current row).
You therefore need to remove the current row from that rule. There are several approaches, which you can see from my own question, but i think the following works best from controller level:
$form = new UserForm();
$form->getElement('email')->getValidator('Db_NoRecordExists')->setExclude(array(
'field' => 'id',
'value' => $idToEdit
))
I need to set up the validation rules to validate the related items on a specific object, ie: A user can have no more than 3 products related to it.
I believe DataMapper can check for this validation using _related_max_size rule, but I can't figure out how to use it on the $validation array in the model.
So far I've tried this in both my user and product models:
var $validation = array(
'product' => array(
'rules' => array('max_size' => 3)
)
);
Can somebody show me an example on how to set up this at the model, controller and finally the view?
Edit: What I mean is, a user has many products, and can create a certain amount of them, let's say 3 products, when that amount is reached, the user can no longer create products, and this validation rule should not permit the user to create more products.
This would be the DB Schema:
Users table
------------------
id | username |
------------------
Products table
------------------------
id | user_id | name |
------------------------
More info here: http://codeigniter.com/forums/viewthread/178045/P500/
Thanks!
EDIT:
Ok, I got it all working now… Except, I need to do the following:
var $validation = array(
'product' => array(
'label' => 'productos',
'rules' => array('required','max_size' => $products_limit)
)
);
The $products_limit comes from the “plan” the user has associated, and it’s stored in the session when the user logs in. When I try to run this I get:
Parse error: syntax error, unexpected T_VARIABLE in /var/www/stocker/application/models/user.php on line 11
Is there any way to make this setting dynamic?
In model
var $validation = array(
array(
'field' => 'username',
'label' => 'Username',
'rules' => array('required')
)
);
In controller. $this -> $object = new Your_model();
$object->validate();
if ($object->valid)
{ $object->save();
// Validation Passed
}
else
{ $data['error'] = $object->error;
// Validation Failed
}
In view.
echo $error->field_name
I never use Codeigniter before, but give me a chance to help you. So far I didn't found any built-in validation in Code-igniter (correct me if I'm wrong).
One workaround that I could think of is to Callback:Your own Validation Functions. Below is a snip. Pardon me if it didn't work as you want.
In Model: (create something like)
function product_limit($id)
{
$this->db->where('product_id',$id);
$query = $this->db->get('products');
if ($query->num_rows() > 3){
return true;
}
else{
return false;
}
}
In controller: (create something like)
function productkey_limit($id)
{
$this->product_model->product_exists($id);
}
public function index()
{
$this->form_validation->set_rules('username', 'Username', 'callback_product_limit');
}
For more information Please refer to the manual page which gives more complete. I am also new to CodeIgniter. But I hope this helps you, not complicate you.
First, set up a custom validation rule in libraries/MY_Form_validation.php
If the file doesn't exist, create it.
Contents of MY_Form_validation.php:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Form_validation extends CI_Form_validation
{
function __construct($config = array())
{
parent::__construct($config);
}
function valid_num_products()
{
//Perhaps it would be better to store a maxProducts column in your users table. That way, every user can have a different max products? (just a thought). For now, let's be static.
$maxProducts = 3;
//The $this object is not available in libraries, you must request an instance of CI then, $this will be known as $CI...Yes the ampersand is correct, you want it by reference because it's huge.
$CI =& get_instance();
//Assumptions: You have stored logged in user details in the global data array & You have installed DataMapper + Set up your Product and User models.
$p = new Product();
$count = $p->where('user_id', $CI->data['user']['id'])->count();
if($count>=$maxProducts) return false;
else return true;
}
}
Next, set up your rule in config/form_validation.php.
Contents of form_validation.php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
$config = array
(
'addProduct' => array
(
array
(
'field' => 'name',
'label' => 'Product Name',
'rules' => 'required|valid_num_products'
)
)
);
Next, set up your error message in language/english/form_validation_lang.php. Add the following line:
$lang['valid_num_products'] = "Sorry, you have exceeded your maximum number of allowable products.";
Now in the Controller, you'll want something along the lines of:
class Products extends MY_In_Controller
{
function __construct()
{
parent::__construct();
$this->load->library('form_validation');
}
function add()
{
$p = $this->input->post();
//was there even a post to the server?
if($p){
//yes there was a post to the server. run form validation.
if($this->form_validation->run('addProduct')){
//it's safe to add. grab the user, create the product and save the relationship.
$u = new User($this->data['user']['id']);
$x = new Product();
$x->name = $p['name'];
$x->save($u);
}
else{
//there was an error. should print the error message we wrote above.
echo validation_errors();
}
}
}
}
Finally, you might wonder why I've inherited from MY_In_Controller. There is an excellent article written by Phil Sturgeon over on his blog entitled Keeping It Dry. In the post he explains how to write controllers that inherit from access-controlling Controllers. By using this paradigm, controllers that inherit from MY_In_Controller can be assumed to be logged in, and the $this->data['user']['id'] stuff is therefore assumed to be available. In fact, $this->data['user']['id'] is SET in MY_In_Controller. This helps you seperate your logic in such a way that you're not checking for logged in status in the constructors of your controllers, or (even worse) in the functions of them.