Magento Multi-store E-mail Templates - magento

I've got a multistore setup (shop A and B) with shared e-mail templates. In these templates, I reference custom variables that are differentiated over A and B.
Shop A ==\ /==> Custom Var (version A)
>==> E-mail Template X ==<
Shop B ==/ \==> Custom Var (version B)
This works pretty well, except for 1 issue: when I create an account through the admin for shop B, I cannot get the password reminder to be of shop B. Sending this user a new password will always send out as shop A.
Mind that the welcome mail that gets sent, is the correct one (B) but I guess only because you select the sending shop from the Create Account screen.
I do realize that the account is marked as having been created by Admin instead of Shop B as opposed to when one would have registered through Shop B. I can imagine this could contribute to the problem, but still, I'd really like to find a way to:
Create an account for Shop B through the admin
Have the password reminders sent in Shop B-style
Edit: the following question is related in the sense that is talks about associating a user with a shop from the admin: How can I change a customer store_id in Magento or set the "created_from" attribute when creating a new customer

It seems that by default, it's just impossible to create an account from the admin that's linked to a specific store. I did find a way though, by listening to adminhtml_customer_prepare_save and abusing the "send from" in the Account creation form.
Here's a module that I whipped up for it:
etc\config.xml:
<config>
<modules>
<Company_AccountPerStore>
<version>1.0</version>
</Company_AccountPerStore>
</modules>
<global>
<models>
<accountperstore>
<class>Company_AccountPerStore_Model</class>
</accountperstore>
</models>
</global>
<adminhtml>
<events>
<adminhtml_customer_prepare_save>
<observers>
<accountperstore_observer>
<class>Company_AccountPerStore_Model_Observer</class>
<method>customerPrepareSave</method>
</accountperstore_observer>
</observers>
</adminhtml_customer_prepare_save>
</events>
</adminhtml>
</config>
Model\Observer.php:
class Company_AccountPerStore_Model_Observer extends Varien_Object
{
public function customerPrepareSave($observer)
{
$customer = $observer->getEvent()->getCustomer();
if (!$customer->hasStoreId() && $customer->hasData('sendemail_store_id')) {
$customer->setStoreId($customer->getData('sendemail_store_id'));
}
}
}
Note that you won't be able to detect that the account was created with the admin anymore.

Related

mage registry key "_singleton/" already exists

I know there are lot of posts with this problem, but I guess each of them is with different roots of it (at least from what I checked - nothing helped me).
I am trying to fire the event upon click on a button from the user, but I get the upper mentioned exception in a browser alert Mage registry key "_singleton/" already exists.
The part of the config.xml:
.....
<models>
<packagecustomernumber>
<class>Package_CustomerNumber_Model</class>
</packagecustomernumber>
</models>
</global>
<frontend>
<events>
<checkout_type_onepage_save_order>
<observers>
<type>singleton</type>
<class>packageName/customernumber/observer</class>
<method>setCustomerNumber</method>
</observers>
</checkout_type_onepage_save_order>
</events>
</frontend>
And the class itself:
class Package_CustomerNumber_Model_Observer
{
public function setCutomerNumber($observer)
{
die('setCutomerNumber');
}
}
The button which should fire the even it checking out/saving the order, so the event should be correct.
Any suggestions ?
The first thing that pops out is this
<class>packageName/customernumber/observer</class>
That's invalid. This is the node where you're telling Magento what class to use for your observer. As such, the <class/> node should be either the full PHP class name of your observer
<class>Package_CustomerNumber_Model_Observer</class>
Or a class aliases for the model
<class>packagecustomernumber/observer</class>
Also, before running your observer, it helps to make sure you can instantiate your model class. Try running the following code in a Magento loaded environment (script, controller action, phtml template, etc.)
$model = new Package_CustomerNumber_Model_Observer;
var_dump(get_class($model));
$model = Mage::getModel('packagecustomernumber/observer');
var_dump(get_class($model));
If you can't instantiate the class, then Magento won't be able to either (and it's easier to test this first before running through some steps to trigger your observer).
Yes, the "packageName/customernumber/observer" is the source of the problem.
while this class reference is completely incorrect in its structure, the problem actually comes up when your class reference does not match up with your global/models/modulename definition. even when the reference "looks" correct.
The config :
<config>
<global>
<models>
<mymodule>
<class>My_Module_Model</class>
</mymodule>
</models>
<events>
<some_event_tag>
<observers>
<my_event_observer_method>
<class>my_module/observer</class>
<method>myEventObserverMethod</method>
</my_event_observer_method>
</observers>
</some_event_tag>
</events>
</global>
</config>
Will have the same result because "my_module/observer" is not found, since the "my_module" class group node is not configured. The correct use for this sample would have been "mymodule/observer".
So if you run across this error, re-read your config.xml.
Make sure that your config.xml models section contains
<!-- This says that string 'company_module' corresponds to Company_Module_Model pseudo-namespace in getModel() and getSingleton() calls. -->
<company_module>
<class>Company_Module_Model</class>
</company_module>
Otherwise you won't be able to make new model instance.

Magento newsletter_subscriber change_status_at

I've been looking for a while at other threads, several have been close to what I need but not quite.
When a guest or customer sign up for our newsletter the field change_status_at populates with a timestamp.
However, if the guest or customer unsubscribe, we would like the the change_status_at field to pickup the current timestamp.
This is important to use because we do not use the newsletter "feature" of Magento 1.7.0.2 CE. Rather we export the newletter_subscriber list to a company to send an email.
Thank you,
Dan
I just ran into this too. A customer wanted to know the date when a subscribe/unsubscribe action occurred. Looking around the internets I've found a few others who have run into this and some claim that it used to work in very old version of Magento.
I think what happened is that the field definition for change_status_at used to be setup to auto update as a default timestamp (i.e. ON UPDATE CURRENT_TIMESTAMP), but that was lost in some update. So there's no Magento code that writes to the field, because MySql is supposed to magically maintain it.
You could try updating the table definition to add the ON UPDATE CURRENT_TIMESTAMP back in (but I'm not in favor changing default model tables) or adding another field to be the default timestamp field.
Or a better solution would be to create a module with an observer to just add the date in when the subscription changes. Here's that might look like (warning - this code is just an example, there may be some syntax errors due to expunging my module's info) -
app/code/local/Myco/MyMod/etc/config.xml
<?xml version="1.0"?>
<config>
<modules>
<Myco_MyMod>
<version>1.0.0</version>
</Myco_MyMod>>
</modules>
<global>
<models>
<myco>
<class>Myco_MyMod_Model</class>
</myco>
</models>
<events>
<newsletter_subscriber_save_before>
<observers>
<mycomymod_observer_subscriber>
<type>singleton</type>
<class>Myco_MyMod_Model_Observer</class>
<method>setUpdateDate</method>
</mycomymod_observer_subscriber>
</observers>
</newsletter_subscriber_save_before>
</events>
</global>
</config>
===========
in app/code/local/Myco/MyMod/Modules/Observer.php
<?php
class Myco_MyMod_Model_Observer
{
public function setUpdateDate(Varien_Event_Observer $observer) {
$subscriber = $observer->getSubscriber();
$subscriber['change_status_at'] = (date("Y-m-d H:i:s", time()));
}
}

How to get All Credit card Details entered by Customer till now in Magento?

I have enabled "CC saved" method from Magento Admin Panel. I want list of all credit card details entered by Customer in drop down on checkout page. Can anybody help me out.
This is not a solution that you use "roll your own" for. There are transaction processing companies that have card wallet features that don't require you to save the credit card details yourself, which will be safer and more secure than this line of thinking.
The laws around storing credit card data are stringent, and from the sounds of it, this line of thinking will break several of them, exposing customers to a significant risk of credit card fraud.
I was going to find a solution for completeness, but couldn't bring myself to it.
By chance I did exactly this for a client last week. My solution was to create a descendant of Mage_Sales_Model_Mysql4_Order_Payment_Collection by supplying new classes, enabled with this config:
<config>
<global>
<models>
<mymodule>
<class>My_Module_Model</class>
<resourceModel>mymodule_eav</resourceModel>
</mymodule>
<mymodule_eav>
<class>My_Module_Model_Resource</class>
<entities>
<payment><table>sales_flat_order_payment</table></payment>
</entities>
</mymodule_eav>
</models>
</global>
</config>
app/code/local/My/Module/Model/Payment.php:
class My_Module_Model_Payment extends Mage_Sales_Model_Order_Payment
{
protected function _construct()
{
$this->_init('mymodule/payment'); // name of single resource model
}
}
app/code/local/My/Module/Model/Resource/Payment.php:
class My_Module_Model_Resource_Payment extends Mage_Sales_Model_Mysql4_Order_Payment
{} // no more is needed
app/code/local/My/Module/Model/Resource/Payment/Collection.php:
class My_Module_Model_Resource_Payment_Collection extends Mage_Sales_Model_Mysql4_Order_Payment_Collection
{
protected function _construct()
{
$this->_init('mymodule/payment'); // model alias
}
public function addSavedFilter($customerId)
{
$this->join('sales/order', '`main_table`.`parent_id`=`sales/order`.`entity_id`', '');
$this->addAttributeToFilter('customer_id', $customerId)
->addAttributeToFilter('cc_number_enc', array('neq'=>''))
->getSelect()->group(array('cc_exp_year', 'cc_exp_month', 'cc_owner', 'cc_last4', 'cc_type'));
return $this;
}
}
I could then get a list like this:
$payments = Mage::getModel('mymodule/payment')->getCollection()->addSavedFilter($customer->getId());
P.S.
As Joseph points out storing credit card details has certain legal considerations. In this particular case my client was aware of all that and were prepared to take on the responsibility.
Another alternative to consider is using the payment gateway to store credit cards instead of directly on the server and use their API to process order payments. A well known one that comes to mind is Authorize.Net CIM (Customer Information Manager). This will offload many of the potential legality issues and is easier for your site to pass the PCI Compliance scan and rules. There are some payment method extensions for Magento that will help with this.

Magento router: How can I catch parameters in all URLs?

Think of a small and basic affiliate system. I want an URL like
www.myshop.com/mynewproduct.html?afid=123
Every time afid is found in the URL, a method should be called (basically to save "afid" in the session and when the customer buys stuff, I want to track it).
You don't need a router for this. You'll want to setup an event listener that fires for every page load, and then access the variables in the request collection. The controller_front_init_routers event should do.
So, setup your module's config with the following
<global>
<events>
<controller_front_init_routers>
<observers>
<packagename_modulename_observer>
<type>singleton</type>
<class>Packagename_Modulename_Model_Observer</class>
<method>interceptMethod</method>
</packagename_modulename_observer>
</observers>
</controller_front_init_routers>
</events>
</global>
And then create the following class
app/code/local/Packagename/Modulename/Model/Observer.php
class Packagename_Modulename_Model_Observer {
public function interceptMethod($observer) {
$request = $observer->getEvent()->getData('front')->getRequest();
$afid = $request->afid;
//do whatever you want with your variable here
}
}
The interceptMethod can be named whatever you want.
I know this is a very old answer, but it is valid to mention we shouldn't use the controller_front_init_routers event if we intend to store those parameters in session, which is the scenario for the original question. For example, if you instantiate customer/session at this point you won't be able to perform a customer login anymore. Alan pointed this himself in http://alanstorm.com/magento_sessions_early. BTW, thanks Alan for this great article.

Magento Email Templates

How can I set different email templates for Customer Order Confirm email and Admin copy of the same.
I need to add some extra content for the Admin email copy.
Thanks
I am assuming that you are currently using the "copy" feature to send the admin email. Let me know if that's not the case. Because the same email is currently being sent to multiple recipients, it would be difficult to change the content for each recipient. You could send multiple emails with a little bit of code, though, which would allow you to use a different email template for each. This could be achieved by creating a new class:
class MyModule_Model_Sales_Order extends Mage_Sales_Model_Order {
/**
* Sending email with order data
*
* #return Mage_Sales_Model_Order
*/
public function sendNewOrderEmail() {
parent::sendNewOrderEmail();
/**
* Your admin email sending code here. Copy it out of the sendNewOrderEmail
* function in Sales_Order.
*/
return $this;
}
}
And then telling Magento to override the core class inside your module config:
<config>
<global>
<models>
<mymodule>
<class>MyModule_Model</class>
</mymodule>
<sales>
<rewrite>
<order>MyModule_Model_Sales_Order</order>
</rewrite>
</sales>
</models>
</global>
</config>
You'll need to create the template you want and be sure that your overridden model uses that template instead.
Change tag catalog to sales and add namespace with class name, Eg: if namespace is Custom then add Custom_MyModule_Model and Custom_MyModule_Model_Sales_Order, so the resulting XML file would be:
<config>
<global>
<models>
<mymodule>
<class>Custom_MyModule_Model</class>
</mymodule>
<sales>
<rewrite>
<order>Custom_MyModule_Model_Sales_Order</order>
</rewrite>
</sales>
</models>
</global>
</config>
you can check and edit the HTML files in app > locale > en_US > templates > email
Please keep in mind that you are overruling the Sales and not the Catalog.
So to prevent searching why the above code is not working use "sales" instead of "catalog" in the config.xml.
This extension does it: http://codecanyon.net/item/send-new-order-email-to-admin/3198802
Totally worth the $10 given all the time I wasted trying to get the other answer here working. I had just finished making my own custom module for the contact form, but the other solutions here didn't work.

Resources