I am looking for a way to programmatically send an email to the user when a tracking number is assigned to an order. I need to be able to do this programmatically because I am using an outside source to populate the tracking information.
I guess what I am really looking for here is a specific trigger or event that I could use to fire off the email that would normally be sent when the admin clicks the "Send Tracking Information" button. I have skimmed through the core code and have not been able to put my finger on what action is actually being triggered when that button is pushed.
We are using a third party (eBridge) to connect with our sales tools. Once an order has been marked as shipped and a tracking number is input into the eBridge tool, it will talk to magento and input the tracking number into the order. The problem is that it doesn't tell magento to fire off an email to provide the customer with this newly inputted tracking number. What I am trying to do is, once the information is put into magento fire off an email from magentos side. Is this possible? What we want, in a nutshell, is to be able to have magento send off an email with the tracking information without having to manually go into each order and click the "Send Tracking Information" button.
When you add a new shipment to an order via the control panel you can tick a box to send the e-mail. If you need to send this programmatically, the controller for the admin area simply calls the sendEmail method on the Mage_Sales_Model_Order_Shipment.
UPDATE: If the tracking number is being adding to the shipment via the 'standard' method, which is to say the addTrack method of the shipment api, then you would be able to hook into the sales_order_shipment_track_save_after event. Adding an observer that does something along the lines of...
public function sendTrackEmail($observer)
{
$track = $observer->getEvent()->getTrack();
$shipment = $track->getShipment(true);
$shipment->sendEmail();
}
FYI there is an undocumented API call that does exactly this, sendInfo(). I don't know as of what version this was added in, as far as I can tell it's over a year old, I just had to solve this same problem myself and this is one of the first results on Google.
Note: If you're implementing this, you likely do not want to send the email flag to the sales_order_shipment.create() API call, as this will result in two emails going out for the same order, one without a tracking number, then one with it.
addTrack() is likely implemented already, you just need to follow it immediately with sendInfo().
sales_order_shipment.addTrack(sessionId, shipmentIncrementId, carrier, title, trackNumber)
sales_order_shipment.sendInfo(sessionId, comment)
The email sent out is the same that you would get by clicking the "Send Tracking Information" button in the Magento backend manually. Reference the Magento API for more explanation on addTrack and using the SOAP API in general.
As for sendInfo() specifically, take a look at the source code from magento/app/code/core/Mage/Sales/Model/Order/Shipment/Api.php for help:
/**
* Send email with shipment data to customer
*
* #param string $shipmentIncrementId
* #param string $comment
* #return bool
*/
public function sendInfo($shipmentIncrementId, $comment = '')
{
/* #var $shipment Mage_Sales_Model_Order_Shipment */
$shipment = Mage::getModel('sales/order_shipment')->loadByIncrementId($shipmentIncrementId);
if (!$shipment->getId()) {
$this->_fault('not_exists');
}
try {
$shipment->sendEmail(true, $comment)
->setEmailSent(true)
->save();
$historyItem = Mage::getResourceModel('sales/order_status_history_collection')
->getUnnotifiedForInstance($shipment, Mage_Sales_Model_Order_Shipment::HISTORY_ENTITY_NAME);
if ($historyItem) {
$historyItem->setIsCustomerNotified(1);
$historyItem->save();
}
} catch (Mage_Core_Exception $e) {
$this->_fault('data_invalid', $e->getMessage());
}
return true;
}
If you want an email containing the tracking informationto get sent when some program e.g. eBridge calls the salesOrderShipmentAddTrack V2 API, you can also extend Mage_Sales_Model_Order_Shipment_Api
e.g.
class PKS_Sales_Model_Order_Shipment_Api extends Mage_Sales_Model_Order_Shipment_Api
public function addTrack
by adding the call to send the email in the try block e.g.
try {
$shipment->save();
$track->save();
$shipment->sendEmail(true, '')
->setEmailSent(true)
->save(); /* added email with null comment */
}
You also have to provide an extension to the SOAP V2 e.g.
class PKS_Sales_Model_Order_Shipment_Api_V2 extends PKS_Sales_Model_Order_Shipment_Api
even if it has no methods :)
(example has my app/code/local/PKS/Sales module, substitute your company name for PKS, apologies re formatting)
app/code/local/PKS/Sales/etc/config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<PKS_Sales>
<version>4.0.0.0</version>
<depends>
<Mage_Sales />
</depends>
</PKS_Sales>
</modules>
<global>
<models>
<sales>
<rewrite>
<order_shipment_api>PKS_Sales_Model_Order_Shipment_Api</order_shipment_api>
<order_shipment_api_v2>PKS_Sales_Model_Order_Shipment_Api_V2</order_shipment_api_v2>
</rewrite>
</sales>
</models>
</global>
</config>
It took more time figuring out how to write the required PKS/Sales/etc/api.xml
(example has my app/code/local/PKS module, substitute your company name for PKS)
<config>
<api>
<resources>
<sales_order_shipment translate="title" module="PKS_Sales">
<title>Modified Shipment API</title>
<model>sales/order_shipment_api</model>
<acl>sales/order/shipment</acl>
<methods>
<addTrack translate="title" module="PKS_Sales">
<title>Add new tracking number</title>
<acl>sales/order/shipment/track</acl>
</addTrack>
</methods>
<faults module="PKS_Sales">
<not_exists>
<code>100</code>
<message>Requested shipment does not exist.</message>
</not_exists>
<filters_invalid>
<code>101</code>
<message>Invalid filters given. Details in error message.</message>
</filters_invalid>
<data_invalid>
<code>102</code>
<message>Invalid data given. Details in error message.</message>
</data_invalid>
<order_not_exists>
<code>103</code>
<message>Requested order does not exist.</message>
</order_not_exists>
<track_not_exists>
<code>104</code>
<message>Requested tracking does not exist.</message>
</track_not_exists>
<track_not_deleted>
<code>105</code>
<message>Tracking not deleted. Details in error message.</message>
</track_not_deleted>
</faults>
</sales_order_shipment>
</resources>
<resources_alias>
<order>sales_order</order>
<order_shipment>sales_order_shipment</order_shipment>
</resources_alias>
<v2>
<resources_function_prefix>
<order>salesOrder</order>
<order_shipment>salesOrderShipment</order_shipment>
</resources_function_prefix>
</v2>
</acl>
</api>
</config>
Please note that with this approach, and having System > Configuration > Sales > Sales Emails > Order and Shipment emails enabled, your customer will get
- one email confirming a new order
- a second email for the shipment with no tracking number
- a third email for the shipment with the tracking number, from your API extension.
I've tried commenting out the Api.php create function's
$shipment->sendEmail($email, ($includeComment ? $comment : ''));
but that second email just keeps getting sent.
Related
Hi i have 2 payment method available in my magento2 store . One is cash on delivery and other is custom payment gateway . I install custom payment gateway extension and it is working fine .
Now i have some conditions if that condition is ok then only i need to make active that custom payment gateway extension .
My products have a product attribute called 'otherthancod' . If
'otherthancod' is active then only show the custom
payment gateway in checkout page. For that i write the following code .
$items = $cart->getItems();
$flag = 0;
$count=0;
foreach($items as $item){
$attribute1 = $item->getProduct()->getData('otherthancod');
if($attribute1){
$flag++;
$count++;
}else{
$flag--;
}
}
if($flag == $count){
$checkResult = $observer->getEvent()->getResult();
$checkResult->setData('is_available', true);
}else{
$checkResult = $observer->getEvent()->getResult();
$checkResult->setData('is_available', false);
}
Now i want to know where i need to put this code ? I don't want to create another extension for that .
Please help.
In my custom payment extension i have seen the following page
app/code/custompaymentgaetway/custom/Gateway/Config/config.php
class Config extends \Magento\Payment\Gateway\Config\Config{
}
can i add if condition before this class ? I think this class is activating payment gateway .
I can see that in frontend template of my payment gateway is
\view\frontend\web\template\custompaymentgaetway.html. Actually i want to hide this frontend once the condition is false .
Don't hesitate to create your own module in app/code/YourNamespace.
Basically you just need a registration.php file and an etc/module.xml:
https://devdocs.magento.com/videos/fundamentals/create-a-new-module/
Also see the following example showing how to declare the Observer for this event:
https://magento.stackexchange.com/a/188367/27460
You should not afraid of setup:upgrade command as it is OOTB Magento command it should work fine, and If something break then please fix it might be some permission issue on server side
First you will need to create events.xml file under app/code/Company/Module/etc/. Then write “payment_method_is_active” event in it. This is the event that hits on the checkout page for payment method availability.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="payment_method_is_active">
<observer name="custom_payment" instance="Company\Module\Observer\PaymentMethodAvailable" />
</event>
</config>
Now create PaymentMethodAvailable.php under Company/Module/Observer/ and write the following code in the file. I am disabling the check money order payment method, you can change the payment method code according to your need.
<?php
namespace Company\Module\Observer;
use Magento\Framework\Event\ObserverInterface;
class PaymentMethodAvailable implements ObserverInterface
{
/**
* payment_method_is_active event handler.
*
* #param \Magento\Framework\Event\Observer $observer
*/
public function execute(\Magento\Framework\Event\Observer $observer)
{
// you can replace "checkmo" with your required payment method code
if($observer->getEvent()->getMethodInstance()->getCode()=="checkmo"){
$checkResult = $observer->getEvent()->getResult();
$checkResult->setData('is_available', false); //this is disabling the payment method at checkout page
}
}
}
Now the payment method Check Money Order is disabled from checkout page .
For reference please check this link
We have several magento websites and some of them we would like to turn on the website restriction so only logged in customers can view it. This seems to work great except we have custom pages that we want the user to be able to access without having to login. And currently if we turn on access restriction it redirects all pages, except the login page and the password reset page, to the login page.
Does anyone know how to exclude other pages from being redirected to the login page? I think it would be a layout xml setting but I can't seem to figure it out or find anything on it.
Magento Enterprise version 1.12.02
Wow, this question is pretty valid, rather old, ranked high on on Google results, yet without a good answer. So here goes one.
The right way to exclude pages from restriction is by adding them to generic list of pages under Enterprise_WebsiteRestriction module config.
See app/code/core/Enterprise/WebsiteRestriction/etx/config.xml and config/frontend/enterprise/websiterestriction section in particular. Pages under full_action_names/generic are always accessible no matter what restriction level is set. Those in full_action_names/register are still accessible when restriction mode is set to "Login and Register" - i.e. they are intended for new registration to be possible.
The values under those sections are full actions names (i.e. <module>_<controller>_<action>), so e.g. to enable contact form for everyone, you need to add contacts_index_index to the generic list.
Note that editing files in core codepool is strongly discouranged, so to achieve this it's best to create your own module and add the configuration section.
It should look somewhat like this (remember to enable this module in app/etc/modules):
<?xml version="1.0"?>
<config>
<modules>
<Emki_WebsiteUnrestrict>
<version>0.1.0</version>
</Emki_WebsiteUnrestrict>
</modules>
<frontend>
<enterprise>
<websiterestriction>
<full_action_names>
<generic>
<contacts_index_index />
</generic>
</full_action_names>
</websiterestriction>
</enterprise>
</frontend>
</config>
Nick,There are several process for this function...
Step1:you can it from Controllers from using dispatch event.
/**
* Retrieve customer session model object
*
* #return Mage_Customer_Model_Session
*/
protected function _getSession()
{
return Mage::getSingleton('customer/session');
}
public function preDispatch()
{
// a brute-force protection here would be nice
parent::preDispatch();
if (!$this->getRequest()->isDispatched()) {
return;
}
$action = $this->getRequest()->getActionName();
/* put all action of this controllers for check ,if any actions of list is exit then redirect to login page*/
$openActions = array(
'index',
'post',
'autoy',
'confirmation'
);
$pattern = '/^(' . implode('|', $openActions) . ')/i';
if (!preg_match($pattern, $action)) {
if (!$this->_getSession()->authenticate($this)) {
$this->setFlag('', 'no-dispatch', true);
}
} else {
$this->_getSession()->setNoReferer(true);
}
}
/**
* Action postdispatch
*
* Remove No-referer flag from customer session after each action
*/
public function postDispatch()
{
parent::postDispatch();
$this->_getSession()->unsNoReferer(false);
}
Other thing is Using observer
The thing is, I want to send them manually via the API because in my country a invoice is a legal-bound order.
Is there a way to do that?
Thanks for your help!
Go to System->Configuration->Sales Emails and disable "Invoice".
I don't think the accepted answer is the best possible way to handle the problem. If you disable the automatic transactional emails completely from the backend, you also can't trigger them manually or use them in your custom module anymore. That means you have to create every transactional email you disabled from scratch if you need to send it at another point as magento standart, which also is a considerable maintenance effort in the aftermath.
The solution I came up with is to programmatically disable sending the email on invoice creation and leverage the default sender classes in a custom observer event. In our case we wanted to send the invoice email when shipment is created.
You do that by overriding \Magento\Sales\Model\InvoiceOrder. Locate the line
$this->notifierInterface->notify($order, $invoice, $comment);
And remove it.
If you want to trigger the email, you can still do that from anywhere you want using the standard "send" function from the InvoiceSender. In our case, we trigger the email from observer like this:
<?php
namespace Vendor\Module\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Email\Sender\InvoiceSender;
class SendInvoiceWithShipment implements ObserverInterface
{
protected $_invoiceSender;
public function __construct(
InvoiceSender $invoiceSender
) {
$this->_invoiceSender = $invoiceSender;
}
public function execute(\Magento\Framework\Event\Observer $observer)
{
$order = $observer->getShipment()->getOrder();
$invoices = $order->getInvoiceCollection();
foreach ($invoices as $invoice) {
// this is where the magic happens
$this->_invoiceSender->send($invoice);
}
}
}
Observer is triggered by event sales_order_shipment_save_after
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name='sales_order_shipment_save_after'>
<observer name='SendInvoiceWithShipment' instance='Vendor\Module\Observer\SendInvoiceWithShipment'
/>
</event>
</config>
You can do this for every transactional email.
I have a special case where users have to sign up for an account via the account sign up page (e.g. /customer/account/create/). Upon completion, and in the event that they have a product in the cart, I need to redirect them back to the checkout screen.
I currently have an observer in place that listens to the customer_register_success event. The observer upgrades the users account to a membership group via this code:
class Hatclub_MembershipHandler_Model_Observer {
// members group id
const GROUP_ID = 4;
// called when a customer registers for the site
public function registrationSuccess(Varien_Event_Observer $observer) {
// extract customer data from event
$customer = $observer->getCustomer();
// a cookie should have been set with the membership id
if (isset($_COOKIE['membership_account_id'])) {
$customer
->setGroupId(self::GROUP_ID)
->setRmsId($_COOKIE['membership_account_id']);
}
return $this;
}
}
Is there another event that I can listen to that is better suited for what I want to do? I have a redirect_to cookie that is available as well if that helps.
After much trial and error, I came across a solution. Since I'm already using the customer_register_success event and modifying user data with my observer, I had to use another event called customer_save_after and it worked like a charm.
config.xml event block
<customer_save_after>
<observers>
<customer_session_observer>
<class>hatclub_membership_handler/observer</class>
<method>customerSave</method>
<type>singleton</type>
</customer_session_observer>
</observers>
</customer_save_after>
observer.php method
public function customerSave() {
// set redirect url from cookie, default to null
$redirect_url = (isset($_COOKIE['redirect_url']))
? isset($_COOKIE['redirect_url']) : null;
// if a redirect url was specified
if (isset($redirect_url)) {
// remove cookie to prevent infinite loop
unset($_COOKIE['redirect_url']);
// redirect to provided url
Mage::app()->getResponse()
->setHeader('Location', $redirect_url)
->sendHeaders();
}
}
The redirect within an observer was a bit of a struggle, but I managed to accomplish it using
Mage::app()->getResponse()
->setHeader('Location', $redirect_url)
->sendHeaders();
Suggestions and criticism welcome.
I've made controller_front_init_routers event observer, which retrieves data from a REST service to construct a menu.
All was ok until I discovered that the observer generates errors in the backend (no way to save products for example) and also in the rest services.
I am struggling for any conclusions, So I raised some interrogations.
I tried to make a condition in order to trigger my Observer
methods in case we are in frontend only. But Magento considers that we are
always in frontend area.
(var_dump(Mage::app()->getStore()->isAdmin()) return always false
and the same with var_dump(Mage::getDesign()->getArea() ==
'adminhtml'))
Can anyone explain what's happened ?
Also one solution is to place the event observer in frontend
area in the config.xml and to load it with
Mage::app()->loadArea($this->getLayout()->getArea()); but where should I
place this piece of code ? in a new observer ? Is that the most
appropriate process ?
Is it a way to listen once an event then to pause the listener?
(once my menu is registered, I don't need to listen to the event any more)
Is the use of controller_front_init_routers event the best choice ?
Who has ever seen that kind of problem ?
I work on Magento ver. 1.12.0.2
Here the config.xml
<globals>
....
<events>
<controller_front_init_routers>
<observers>
<connector_services_observer>
<type>singleton</type>
<class>Connector_Services_Model_Observer</class>
<method>getEvent</method>
</connector_services_observer>
</observers>
</controller_front_init_routers>
</events>
</globals>
Here the function getEvent in my model observer
public function getEvent($observer){
//model which do post or get requests and return xml and menu
$_getRest = Mage::getModel('connector_services/RestRequest');
//the paths
$_menu_url = Mage::getStoreConfig('connector_service_section/connector_service_url/service_menu_url');
//put a store config
$path_nlist = 'veritas-pages-list.xml';
$_isAdmin = Mage::helper('connector_services');
$currentUrl=Mage::helper("core/url")->getCurrentUrl();
//the way I found to trigger methods only in frontend
//that's not very beautiful I know
$admin = preg_match("#/admin/#",$currentUrl);
$api = preg_match("#/api/#",$currentUrl);
//
if ( !$admin && ! $api ){
$_menuXml = $_getRest->postRequest($_menu_url);
if( $_menuXml )
{
$_menu = $_getRest->makeMenu($_menuXml);
Mage::register('menu',$_menu);
}
}
You should be able to pass a querystring to the rest service, similar to how you'd just type it out in the address bar. Magento will forward it on to the observer, and you can use it as a flag.
Add something like the following to your code:
const USE_FRONTEND = 'usefront';
public function getEvent($observer){
this->request = $observer->getEvent()->getData('front')->getRequest();
// If the constant is in the query string
if ($this->request->{self::USE_FRONTEND}) {
// Do code related to this request
die('Frontend flag detected');
}
}
Call to your site like this and pass the querystring
http://www.yourmagentosite.com/?usefront=true
I am not extremely familiar with Magento's new REST api, but I know it works in the browser. Maybe this explanation can help you out.