I'm overriding the Mage_Core_Model_Email_Template-class send()-method in order to send an SMS to the customer at the same time as the email is being sent.
What I'm not sure how to do is to get the customer information connected to the order. Usally all the information is inserted in the email template as variables, but I need to access it in PHP from the mentioned class. And it needs to be the customer information, like name, phone and so on from the order object since orders may be placed without registering on the webshop (also to get the correct phonenumber for the specific order).
Any help would be highly appreciated, I have no idea how to even get the orderId from within this class. Also would like to be able to list these customer specific field-names in an admin-view for choosing the telephone field as the source of SMS recipient, so if there's a way to output all the customer specific field-names connected to orders that would be great too.
Thanks in advance! :)
I still say that event-capture is the practical way, but I respect your point of view. So in the other way, the sendMail method of Mage_Sales_Model_Order_Invice contains the "order" object as template parameter:
$mailer->setTemplateParams(array(
'order' => $order,
'invoice' => $this,
'comment' => $comment,
'billing' => $order->getBillingAddress(),
'payment_html' => $paymentBlockHtml
)
);
The mailer object is an instance of Mage_Core_Model_Email_Template_Mailer class, i recommend to overwrite the send method of this class, and then you can access the data you need via method $this->getTemplateParams(), and the address and customer information from the order object, as you can find in my previous answer.
Magento has an event-driven architecture, so in issues like this it is much better to create an Observer to catch an event. How to create observers This event in your case is "sales_order_invoice_register". For this you need in your module's config eg. app/code/local/Stack/Test/etc/config.xml:
<events>
<sales_order_invoice_register>
<observers>
<stacktest>
<type>singleton</type>
<class>stacktest/observer</class>
<method>sendSMS</method>
</stacktest>
</observers>
</sales_order_invoice_register>
</events>
And in you Observer, which is app/code/local/Stack/Test/Model/Observer.php:
class Stack_Test_Model_Observer extends Mage_Core_Model_Observer
{
public function sendSMS($observer)
{
#get the customer
$order = $observer->getEvent()->getOrder();
Mage::log($order->getBillingAddress());
Mage::log($order->getShippingAddress())
Mage::log($order->getCustomerEmail());
Mage::log($order->getCustomerFirstname());
Mage::log($order->getCustomerLastname());
...
You can get all data like this, and send the sms directly from the Observer class, no need to override the send method.
If you are overriding the send function you can use its parameter $variables, an array, which contains, among other data, an order object ($variables['order']). This is true for the order email, invoice, shipment, and creditmemo emails.
This make my suggestion of "registering an order" redundant.
The first thing that comes to mind is to capture an event dispatched when an order is placed and save the order object from the event into the Magento registry. Then you can fetch that registry entry from your overridden send function. This should work assuming the SMS is sent during the request that submits the order, and not later, like when the SMS are dispatched from a queue processor.
So this idea can be implemented like this:
Register the observer
<config>
<!-- -->
<global>
<!-- -->
<events>
<sales_order_place_after>
<observers>
<yournodename>
<class>YourPackage_YourModule_Model_Observer</class>
<method>registerOrder</method>
</yournodename>
</observers>
</sales_order_place_after>
</events>
<!-- -->
</global>
<!-- -->
</config>
Implement the observer
class YourPackage_YourModule_Model_Observer
{
public function registerOrder(Varien_Event_Observer $observer)
{
$order = $observer->getEvent()->getOrder();
Mage::register('currentOrder', $order);
}
}
Access the registry entry:
public function send()
{
$order = Mage::registry('currentOrder');
if ($order instanceof Mage_Sales_Model_Order) {
$address = $order->getBillingAddress();
if (!$order->getCustomerId()) {
//guest order
} else {
$customer = Mage::getModel('customer/customer')
->load($order->getCustomerId());
}
}
}
Related
I have to implement some feedback customer code for getting enroled in their store database.
the code is :
try {
$Client = new TrustedShop('key');
$Client->SetEmail();
$Client->AddProduct('Name of first purchased product');
$Client->AddProduct('Name of second purchased product');
$Client->Send();
Nice and simple.
The logic is that after some days, they will send my customer a feedback for asking about their shopping experience on the store.
I have currently embeded it into /public_html/app/design/frontend/base/default/template/persistent/checkout/onepage/billing.phtml
with the $Client->SetEmail($this->getAddress()->getEmail());
No problems here, but (and a big but :) ) how can i retrive the customer's ordered sku in billing phtml?
And if the ordered contains 2 or 10 skus how can i send them to my partner ?
Is this the corect path to implement the code, or there is a better location in magento where i can retrive both the customer email and the ordered skus ?
P.S. Sorry if this is a stupidly simplistic question, newbie on the throttle here :)
EDIT:
the "dirty hack" solution:
At the billing.phtml the order is not yet placed, the customer can step back and quit shopping. It is not the good point to insert the logic.
Instead, use the checkout/success.phtml template for the hack. You can do the following there:
<?php
$order = Mage::getModel('sales/order')->loadByIncrementId($this->getOrderId());
Mage::log($order->getCustomerEmail());
Mage::log($order->getCustomerFirstname());
Mage::log($order->getCustomerLastname());
foreach ($order->getAllVisibleItems() as $item) {
Mage::log($item->getSku());
};
?>
And that's it, you have all the data to implement the custom logic.
The RECOMMENDED solution starts here:
As i understand, you will have to send the name or SKU of ordered products an the customer's email to an external client.
Magento has an event-driven architecture, so in issues like this it is much better to create an Observer to catch an event. (link to "how to create Observer") The event in your case is "sales_order_place_after". For this you need in your module's config eg. app/code/local/YOUR/MODULE/etc/config.xml:
<events>
<sales_order_place_after>
<observers>
<any_unique_name_of_your_observer>
<type>singleton</type>
<class>yourclass/observer</class>
<method>sendOrderInfo</method>
</any_unique_name_of_your_observer>
</observers>
</sales_order_place_after>
</events>
And in you Observer, which is app/code/local/YOUR/MODULE/Model/Observer.php:
class YOUR_MODULE_Model_Observer extends Mage_Core_Model_Observer
{
public function sendOrderInfo($observer)
{
#get the order
$order = $observer->getEvent()->getOrder();
Mage::log($order->getBillingAddress());
Mage::log($order->getShippingAddress())
Mage::log($order->getCustomerEmail());
Mage::log($order->getCustomerFirstname());
Mage::log($order->getCustomerLastname());
foreach ($order->getAllVisibleItems() as $item) {
$sku = $item->getSku();
...
This is an example how you can retrieve the data you need, and then you can implement your custom logic inside the method of the Observer.
EDIT: a more detailed example to get the sku of ordered items
I have an extension for product registration that dispatches an event after the registration is saved. Another extension uses that event to generate a coupon for a virtual product if it is related to the registered product.
I need to get back data on the generated coupon to send to the user in an email along with the details of their product registration.
Is there a way to return data from the observer back to where the event is dispatched?
There is a trick available in Magento for your purpose. Since you can pass event data to the observers, like product or category model, it also possible to create a container from which you can get this data.
For instance such actions can be performed in dispatcher:
$couponContainer = new Varien_Object();
Mage::dispatchEvent('event_name', array('coupon_container' => $couponContainer));
if ($couponContainer->getCode()) {
// If some data was set by observer...
}
And an observer method can look like the following:
public function observerName(Varien_Event_Observer $observer)
{
$couponContainer = $observer->getEvent()->getCouponContainer();
$couponContainer->setCode('some_coupon_code');
}
Enjoy and have fun!
No, there's nothing built in to the system for doing this. The Magento convention is to create a stdClass or Varien_Object transport object.
Take a look at the block event code
#File: app/code/core/Mage/Core/Block/Abstract.php
...
if (self::$_transportObject === null)
{
self::$_transportObject = new Varien_Object;
}
self::$_transportObject->setHtml($html);
Mage::dispatchEvent('core_block_abstract_to_html_after',
array('block' => $this, 'transport' => self::$_transportObject));
$html = self::$_transportObject->getHtml();
...
Since self::$_transportObject is an object, and PHP objects behave in a reference like manner, any changes made to the transport object in an observer will be maintained. So, in the above example, if an observer developer said
$html = $observer->getTransport()-setHtml('<p>New Block HTML');
Back up in the system block code self::$_transportObject would contain the new HTML. Keep in mind that multiple observers will have a chance to change this value, and the order observers fire in Magento will be different for each configured system.
A second approach you could take is to use Magento's registry pattern. Register a variable before the dispatchEvent
So, horray - I'm attempting to create a new custom Payment Gateway. It's designed to do auth/capture via a third-party API, but does NOT need to redirect to the third-party site.
From my understanding, when an order is placed/finalized in Magento, and the gateway is set to "Authorize and Capture", it should be firing off the 'capture' method from the gateway model. Currently, it's not doing this.
Of course, if I specifically capture from the Admin Order view, it'll attempt to capture, but this needs to happen immediately upon checkout (and again, it's my understanding that it already should).
In my gateway Model, I have the following (truncated for readability):
<?php
class Example_Gateway_Model_Payment extends Mage_Payment_Model_Method_Cc
{
protected $_code = 'example';
protected $_isGateway = true;
protected $_canAuthorize = true;
protected $_canCapture = true;
protected $_canUseInternal = true;
protected $_canUseCheckout = true;
// This is an empty block class that extends Mage_Payment_Block_Form_Cc
protected $_formBlockType = 'example/form_example';
public function authorize(Varien_Object $payment, $amount)
{
Mage::log('Authorizing!');
}
public function capture(Varien_Object $payment, $amount)
{
Mage::log('** Capturing **');
// Third-party API stuff would go here, with exceptions being thrown if the gateway determines they've provided an invalid card, etc.
}
public function assignData($data)
{
Mage::log('Assigning Data');
}
}
This Payment model itself definitely works - I get logging output for assignData() and validate(), as well as __construct() if I add it. But no matter what I do, neither the capture or authorize methods fire when actually placing the order.
My config.xml reads somewhat like this:
<?xml version="1.0"?>
<config>
<modules>
<Example_Gateway>
<version>0.0.5</version>
</Example_Gateway>
</modules>
<global>
<blocks>
<gateway>
<class>Example_Gateway_Block</class>
</gateway>
</blocks>
<models>
<gateway>
<class>Example_Gateway_Model</class>
</gateway>
</models>
<helpers>
<gateway>
<class>Example_Gateway_Helper</class>
</gateway>
</helpers>
</global>
<frontend>
<!-- Snip.. Nothing special here -->
</frontend>
<default>
<payment>
<gateway>
<sort_order>0</sort_order>
<model>gateway/payment</model>
<enabled>1</enabled>
<order_staus>processing</order_status>
<payment_action>authorize_capture</payment_action>
<cctypes>VI,MC,AE,DI</cctypes>
<useccv>1</useccv>
</gateway>
</payment>
</default>
</config>
I don't believe there's a need for a resource model as I don't need any additional tables; my expectation is that it will simply use sales_flat_order_payment and related tables to store any gateway-related/-provided data (txn id, etc)
Similarly, I'm simply extending the default CC block to get the standard payment form.
What am I missing? It has to be something small and simple that I'm overlooking.
Thanks in advance!
UPDATE:
So far, I have implemented a workaround that uses an observer to the checkout_type_onepage_save_order event that calls the capture() method manually - but I'm pretty sure this is not the right way to go.
I'm not wrong in thinking that Magento should automatically call capture() upon the initial order save, if the gateway is set to authorize_capture, right..?
Solved! You need this:
protected $_isInitializeNeeded = false;
I have NO IDEA why this is necessary, but at this point I've given up trying to figure out the why of magento in favor of actually getting things done. I had the exact same problem as you, and when I traced it through the source code I found that Payment.php was not calling _authorize when isInitializeNeeded returned true. So, stick that line in your model, and it will work.
I think the method should be : "authorize_capture" and not "capture" as stated in the config
<payment_action>authorize_capture</payment_action>
like that:
public function authorize_capture(Varien_Object $payment, $amount)
{
Mage::log('** Capturing **');
// Third-party API stuff would go here, with exceptions being thrown if the gateway determines they've provided an invalid card, etc.
}
i had a similar problem that the "authorize" method was not fired at all because "authorize_action" was empty. I was able to solve this by hardcoding it in the method itself. "getConfigPaymentAction" is called to get the authorize method.
public function getConfigPaymentAction() {
return 'authorize';
}
Well, I used an observer to manually call the capture method.
Not the most elegant solution, but it works well enough.
I have a two part question about customizing my Magento store.
When someone buys a downloadable product, I want to generate a licence code and include it in the invoice.
I have added a product attribute called ‘license_code’ to my product’s default attribute set and I want to set its value with php when a customer checks out.
What is the event to observe that will let me access the products in the cart just after they are purchased but before the invoice is created?
I also need to know what script to use to set a product’s attribute value during that event.
Thank you for your help!
Possible events are sales_order_place_before or sales_convert_quote_*.
You cannot save your 'license_code' attribute because that will affect all products, a product does not store it's values when ordered. Instead a better idea would be to manipulate the options of an order item.
function salesConvertQuoteItemToOrderItem(Varien_Event_Observer $observer)
{
$orderItem = $observer->getOrderItem();
$options = $orderItem->getProductOptions();
$options['licence_code'] = YOUR-DOWNLOADABLE-CODE-HERE;
$orderItem->setProductOptions($options);
}
Retrieving the code later is essentially the same process with getProductOptions(), the order item objects are already used on the order view pages so are easy to find and use in your theme.
Ok I think I got it figured out.
I set up my event observers as follows:
<events>
<sales_order_item_save_before>
<observers>
<downloadable_observer>
<class>Licensing_Catalog_Model_Observer</class>
<method>generate_licenses</method>
</downloadable_observer>
</observers>
</sales_order_item_save_before>
</events>
and then my observing function as:
public function generate_licenses($observer)
{
$orderItem = $observer->getEvent()->getItem();
$options = $orderItem->getProductOptions();
$options['licence_code'] = 'YOUR-DOWNLOADABLE-CODE-HERE';
$orderItem->setProductOptions($options);
return $this;
}
Thank you so much for the help, clockworkgeek!
I would like to be able to run some functionality with a module that I am building whenever a customer registers an account, but I can't seem to find any event that is fired upon a new customer registration.
Does anybody know of an event that is dispatched for that?
Whenever I'm looking for an event, I'll temporarily edit the Mage.php file to output all the events for a particular request.
File: app/Mage.php
public static function dispatchEvent($name, array $data = array())
{
Mage::log('Event: ' . $name); //not using Mage::log, as
//file_put_contents('/tmp/test.log','Dispatching '. $name. "\n",FILE_APPEND); //poor man's log
Varien_Profiler::start('DISPATCH EVENT:'.$name);
$result = self::app()->dispatchEvent($name, $data);
#$result = self::registry('events')->dispatch($name, $data);
Varien_Profiler::stop('DISPATCH EVENT:'.$name);
return $result;
}
and then perform whatever action it is I'm trying to hook into. Magento events are logically named, so scanning/sorting through the resulting logs usually reveals what I'm after.
customer_register_success is what you are looking for:
<config>
<frontend>
<events>
<customer_register_success>
<observers>
<your_module>
<type>singleton</type>
<class>your_module/observer</class>
<method>yourMethod</method>
</your_module>
</observers>
</customer_register_success>
</events>
</frontend>
</config>
I discovered how to achieve this today. It involves using one of the generic controller events. This node in the config.xml will hook into the right event:
<events>
....
<controller_action_postdispatch_customer_account_createPost>
<observers>
<your_module_here>...etc
The controller_action_postdispatch_REQUESTPATH event is thrown for every controller that extends Mage_Core_Controller_Front_Action (which is basically all of them) which makes it very easy to target. Ditto for controller_action_predispatch_REQUESTPATH.
I'm a bit surprised that none of the answers if solving the case completely.
Customer create can happen
by url customer/account/create
by register in checkout
I solved it by tracking two events:
config.xml
<events>
<controller_action_postdispatch_customer_account_createpost>
<observers>
<myextensionkey_create_account>
<class>myextensionkey/observer</class>
<method>createAccount</method>
<type>singleton</type>
</myextensionkey_create_account>
</observers>
</controller_action_postdispatch_customer_account_createpost>
<checkout_submit_all_after>
<observers>
<myextensionkey_checkout_create_account>
<class>myextensionkey/observer</class>
<method>createAccountCheckout</method>
<type>singleton</type>
</myextensionkey_checkout_create_account>
</observers>
</checkout_submit_all_after>
</events>
and in Observer.php
public function createAccount($observer) { ... } //Nothing special here
public function createAccountCheckout($observer) {
if ($observer->getQuote()->getData('checkout_method') != Mage_Checkout_Model_Type_Onepage::METHOD_REGISTER) {
return;
}
Edit: I changed
<controller_action_predispatch_customer_account_createpost>
into
<controller_action_postdispatch_customer_account_createpost>
because on predispatch the account is not created yet. There can be an error for example if the email already exists in the shop.
There isn't a direct event for this, but you could use the customer_save_commit_after event. This event also guarantees you that the customer is save in the shop's database. The problem with this event is that is triggered twice. Bellow is an hack that allows you to use this event - the observer function is listed:
public function customer_save_commit_after($p_oObserver) {
$l_oCustomer = $p_oObserver->getCustomer();
if ($l_oCustomer->isObjectNew() && !$l_oCustomer->getMyCustomKeyForIsAlreadyProcessed()) {
$l_oCustomer->setMyCustomKeyForIsAlreadyProcessed(true);
// new customer
}
else {
// existing customer
}
return false;
}
Hope this helps someone!
You have to consider also when the user register on-the-fly on checkout: a Register on chekout. Thinking on this case, you can catch the "checkout_type_onepage_save_order_after" event with your own Observer class, and then this code...
if($observer->getEvent()->getQuote()->getCheckoutMethod(true) == Mage_Sales_Model_Quote::CHECKOUT_METHOD_REGISTER){
(...)
}
Anybody may say: Mage_Sales_Model_Quote->getCheckoutMethod() is deprecated since 1.4!!,but:
If we call the ortodox method Mage_Checkout_Model_Type_Onepage->getCheckoutMethod(), waiting for something as "METHOD_REGISTER" this is executed:
if ($this->getCustomerSession()->isLoggedIn()) {
return self::METHOD_CUSTOMER;
}
... "METHOD_CUSTOMER" is the name for a checkout with an already registrated user, not our case.... but yes!, because....
...the registration operation is perfomed before "checkout_type_onepage_save_order_after" event. Then we a have a METHOD_CUSTOMER now. Ups, something inconsistent?
Fortunatly, the Quote remains with the original value: CHECKOUT_METHOD_REGISTER
Any other idea for the registration on checkout?
You can try customer_save_after, the only thing that the registration sends this event twice
Actually there are customer_save_after and customer_save_before (magento 1.5)
If you want to modify on-the-fly some data after form post, pick customer_save_before, change the data you want and that's all (the save action come after, so your change will be taken into account).
$customer->save() just doesn't work in customer_save_after. (fatal error) Use this observer to run a code after customer creation which are NOT related to customer data.
Hope that helps!
customer_register_success
adminhtml_customer_save_after
these two are the default events when a customer is inserted into the database....
first event fires in frontend when a user registers and second event fires in the backend when a customer is created through admin panel...i hope you know how to register an observer for an event...hope this will help you...
I was looking of the same thing. I believe the event is customer_register_success.
You can find a link for all events at:
http://www.nicksays.co.uk/magento_events_cheat_sheet/
I found the event checkout_submit_all_after.
<checkout_submit_all_after>
<observers>
<my_example>
<class>my_example/observer</class>
<method>customerRegistered</method>
</my_example>
</observers>
</checkout_submit_all_after>
In my Observer.php I get the quote object that is passed in.
public function customerRegistered (Varien_Event_Observer $observer) {
$quote = $observer->getQuote();
$checkout_method = $quote->getData();
$checkout_method = $checkout_method['checkout_method'];
if ($checkout_method == Mage_Checkout_Model_Type_Onepage::METHOD_REGISTER) {
}
Do not use $quote->getCheckoutMethod() it gives you login_in instead. Not sure why. Hope this helps.
I've discovered that the customer_login and customer_session_init events are both thrown on account create. You could register a listener for either and then check to see the create date on the account?
The answer to this question is that there isn't an event for that.
You can use the customer_register_success event. It is triggered after the customer is succesfully created. Here is the link of event cheat sheets. Hope it also helps you.
http://www.nicksays.co.uk/magento-events-cheat-sheet-1-7/
customer_register_success is the event dispatched after successful customer registration.
Here's from the code from Mage/Customer/controllers/AccountController.php::454 in magento 1.8:
protected function _dispatchRegisterSuccess($customer)
{
Mage::dispatchEvent('customer_register_success',
array('account_controller' => $this, 'customer' => $customer)
);
}
customer_save_after is the event which gets called after a new customer registration.
Read about all the events here:
http://www.magentocommerce.com/wiki/5_-_modules_and_development/reference/events
event name:customer_registration_is_allowed
I'm not sure if this is you want,you can write a observer to test