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
Related
I have a magento store with two currency, and my items in cart have a dynamic price.
I succesfully calculate my quote_item price, with an observer and setCustomPrice and setOriginalCustom price
$quote_item->setCustomPrice($price);
$quote_item->setOriginalCustomPrice($price);
And my observer:
<sales_quote_add_item>
But i have a problem, when i change the currency of my store, the subtotal is not update.
How to handle multiple-currency and custom quote item price ?
handle it through observer
< sales_quote_item_set_product>
$baseCurrencyCode = Mage::app()->getStore()->getBaseCurrencyCode();
$currentCurrencyCode = Mage::app()->getStore()->getCurrentCurrencyCode();
if($currentCurrencyCode!=$baseCurrencyCode)
$price= Mage::helper('directory')->currencyConvert($baseprice, $baseCurrencyCode, $currentCurrencyCode);
else
$price = $baseprice;
$item->setPrice($baseprice);
$item->setRowTotal($item->getQty() * $price);
I've had this very same issue over the last week. Using the ->setOriginalCustomPrice method is fine for a single currency site, but with currency switching, its rigidness means you need to update the cart items and list price everytime the currency is switched, which is very inefficient in my opinion.
I came up with a more elegant solution. Create a module and within the model section of your config add this;
<models>
<catalog>
<rewrite>
<product>PixieMedia_CustomPrice_Model_Product</product>
</rewrite>
</catalog>
</models>
Counter intuitively, the main ->getFinalPrice function is in the product model and not the price model.
Now create your new Product.php model in /app/code/local/Namespace/Module/Model/Product.php
class PixieMedia_CustomPrice_Model_Product extends Mage_Catalog_Model_Product {
public function getFinalPrice($qty=null)
// REWRITTEN FUNCTION TO RETURN THE SPECIAL PRICE AND ENSURE CURRENCY CONVERSION
{
$qBreak = false;
$customPrice = Mage::Helper('pixiemedia_customprice')->getCustomerPrice($this);
if($qty) {
$qBreak = $this->getQtyBreakPrice($this->getSku(),$qty,$customPrice[0]);
}
if($qBreak) { return $qBreak; } else { return $customPrice[0]; }
}
}
On the particular project I was working on, the client is using multiple price lists for customer specific pricing, the scope of which would make Magento horrendously slow to index pricing. So we've loaded all the data to a custom table and perform a lookup to return the customer's correct price or qty break.
It's dead easy to hook your own logic in and return what ever price you wish. This fully supports currency conversion, so no need to fiddle around re-converting prices.
Hope this helps someone out. Enjoy :)
You probably miss a call to:
$quote->collectTotals()->save();
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());
}
}
}
I have created attributes that save directly to a quote item in my checkout_cart_product_add_after Observer method will not persist the value as it seems to be reverted after the Observer exits.
See code samples below:
config.xml (snippet):
<checkout_cart_product_add_after>
<observers>
<module>
<type>model</type>
<class>NativeRemedies_OrderGroove_Model_Observer</class>
<method>productAddAfter</method>
</module>
</observers>
</checkout_cart_product_add_after>
Observer.php (snippet):
public function handleOrderGroove()
{
foreach($this->og->products as $_product){
if($_product->every>0){
foreach($this->_quote->getAllVisibleItems() as $_item){
//if sku is in the active list of recurring products selected add quote item id to array
if($_item->getSku()==$_product->id){
Mage::helper('nrordergroove')->setRecurringItem($_item->getItemId());
$_item->setOrdergrooveActive(true)->save();
$_item->getProduct()->setPrice(2);
$_item->setCustomPrice(2);
$_item->setOriginalCustomPrice(2);
$_item->getProduct()->setIsSuperMode(true);
}
}
} // else, do nothing
}
The $_item object in this example isn't providing the facility to retain the attribute as set - even when calling ->save().
Thank in advance for your help. I have seen all of the tutorials about setting custom prices and attributes - nothing seems to remedy the situation!
Edit 1
I'm starting to feel like this is a bug in 1.6+. I have seen much discussion on various boards about this working in >=1.4.
Edit 2
Just to be absolutely clear, the issue here is that the Custom Pricing attribute is being overwritten effectively by the Product model or the collectTotals methods. I need a workaround.
As it happens my working code here did, in fact, work. An extension conflict with Amasty's Special Promotions was causing the custom pricing to be unset. This is tested as working with the following Magento versions:
1.5 Community
1.6.1 Community
1.11.1.1 Enterprise
Here is the answer to your question, yes this is in the newer version of Magento 1.5+:
When checking out items get converted from a quote to a order at which point your attribute are being lost.
You would need to add something similar to this observer in order for your attributes to retain at checkout:
<sales_convert_quote_item_to_order_item>
<observers>
<extra_options>
<type>model</type>
<class>extra_options/observer</class>
<method>salesConvertQuoteItemToOrderItem</method>
</extra_options>
</observers>
</sales_convert_quote_item_to_order_item>
Here we move the option from the quote item to the order item.
public function salesConvertQuoteItemToOrderItem(Varien_Event_Observer $observer)
{
$quoteItem = $observer->getItem();
if ($additionalOptions = $quoteItem->getOptionByCode('additional_options')) {
$orderItem = $observer->getOrderItem();
$options = $orderItem->getProductOptions();
$options['additional_options'] = unserialize($additionalOptions->getValue());
$orderItem->setProductOptions($options);
}
}
Take a look here for more details:
Magento - Quote/order product item attribute based on user input
Hello i need to use magento to sell a product that will have custom price rules.
the rule will depend by the quantity of this product sold.
I know that magento can make special rule, if a customer by ++ quantity of this product, but me i need to apply different rule and i cant find the way.
For example, product bought from customers first 100 times, price is :100$
product bought 200-500 times, price is 400$
500-1000times, product is 800$
1000 - > product is fixed price 1000$
is it possible for magento to do this?
Thank you
You can do this by creating a module that uses the checkout_cart_product_add_after and checkout_cart_update_items_after observers.
Then inside your observer class you can put the following function in to set the price:
public function yourAddToCartFunction($observer) {
if ($p = $observer->getQuoteItem()->getParentItem()) {
$discount_amount = $your_discount_logic;
$p->setCustomPrice($discount_amount)->setOriginalCustomPrice($discount_amount); #configs
} else {
$p = $observer->getQuoteItem();
$discount_amount = $your_discount_logic;
$p->setCustomPrice($discount_amount)->setOriginalCustomPrice($discount_amount); #simple products
}
}
You will more than likely need a different function for the cart update observer call. The one above is for the cart_add_after event. The function for the cart update is nearly identical except you have to go through the cart object to get the logic.
public function yourCartUpdateFunction($observer) {
$cart = $observer->cart;
$quote = $cart->getQuote();
foreach($quote->getAllVisibleItems() as $item) {
if ($p = $item->getParentItem()) {
$discount_amount = $your_discount_logic;
$p->setCustomPrice($discount_amount)->setOriginalCustomPrice($discount_amount); #configs
} else {
$discount_amount = $your_discount_logic;
$item->setCustomPrice($discount_amount)->setOriginalCustomPrice($discount_amount); #simple products
}
}
}
The reason you would want the second function is if the logic needs to happen again if the cart is updated. If the price is going to stick forever no matter what happens once it is in the cart, you can probably exclude the second function.
MASSIVE EDIT:
Ok this is how I would do this. I am assuming you are starting with a basic, functional module.
The first thing you want to do is setup your observers in your modules config.xml file. In this case you are going to use be using the checkout_cart_product_add_after and checkout_cart_update_items_after observers. You will set that up by adding the following to your config.xml file:
<config>
...
<global>
...
<events>
<checkout_cart_product_add_after>
<observers>
<call_this_something_unique>
<type>singleton</type>
<class>Ocaff_Custompricing_Model_Cart_Observer</class>
<method>calculateAddToCart</method>
</call_this_something_unique>
</observers>
</checkout_cart_product_add_after>
<checkout_cart_update_items_after>
<observers>
<call_this_something_unique_2>
<type>singleton</type>
<class>Ocaff_Custompricing_Model_Cart_Observer</class>
<method>calculateCartUpdate</method>
</call_this_something_unique_2>
</observers>
</checkout_cart_update_items_after>
</events>
...
</global>
...
</config>
Basically what this does is it tells Magento that when the checkout_cart_product_add_after event is triggered, run the calculateAddToCart method of the Ocaff_Custompricing_Model_Cart_Observer class and also to run the calculateCartUpdate when the checkout_cart_update_items_after event is triggered.
Ok now that you have your configuration put together you can create your model. In the case of this example the class would be located in the /app/code/local/Ocaff/Custompricing/Model/Cart/Observer.php file (however you will put the class that matches your module)
Ok now in your Observer.php file you are going to put the following code:
<?php
class Ocaff_Custompricing_Model_Cart_Observer {
public function calculateAddToCart($observer) {
if ($p = $observer->getQuoteItem()->getParentItem()) {
$discount_amount = Mage::helper('custompricing')->calculatePrice($p);
$p->setCustomPrice($discount_amount)->setOriginalCustomPrice($discount_amount); #configs
} else {
$p = $observer->getQuoteItem();
$discount_amount = Mage::helper('custompricing')->calculatePrice($p);
$p->setCustomPrice($discount_amount)->setOriginalCustomPrice($discount_amount); #simple products
}
}
public function calculateCartUpdate($observer) {
$cart = $observer->cart;
$quote = $cart->getQuote();
foreach($quote->getAllVisibleItems() as $item) {
if ($p = $item->getParentItem()) {
$discount_amount = Mage::helper('custompricing')->calculatePrice($prpoduct);
$p->setCustomPrice($discount_amount)->setOriginalCustomPrice($discount_amount); #configs
} else {
$discount_amount = Mage::helper('custompricing')->calculatePrice($p);
$item->setCustomPrice($discount_amount)->setOriginalCustomPrice($discount_amount); #simple products
}
}
}
}
Ok a few notes here. both functions take a single paramater that I have called observer (and pretty much everywhere else calls it that too). This is a single variable that holds all the information that Magento passes along to all the functions that handle the event. In the case of the calculateAddToCart function we are only grabbing the Quote Item (which is confusing since in checkout with Magento there are quite a few different objects that kind of all represent the same thing if you are looking at it from 50,000 feet). What this function does is takes the quote item and determines if the product that was added to cart is a simple or configurable product and takes that appropiate action.
The calculateCartUpdate function basically does the exact same thing, but it has to instead iterate through all the products in the cart since in Magento you update the entire cart all at once, not every line item.
In both functions you will notice that I am updating the Custom Price and the Original Custom Price field. I am not 100% sure why you have to update both (and really why ask questions if it works :))
Ok now you may or may not notice that I have done something a little less than conventional for figuring out the $discount_amount variable. I am calling a helper function to get the price that the product should be set to. I put this into a helper because you may want to access that logic elsewhere in the system and I generally prefer to put that kind of logic into a Helper (in fact I am pretty sure you are going to want to use this on the product and category pages since you are modifying the way pricing is working for some products and customers get mad when prices change up unexpectadly).
Ok now onto the helper function. Since I am not 100% sure what logic you really want in the end, we are just going to keep this function simple and have it return $2. This will make everything that goes into the cart $2 no matter what its price actually is. This is going to be where you figure out your logic for pricing.
<?php
class Ocaff_Custompricing_Helper_Data {
public function calculatePrice($product=null) {
// Your custom logic will go here. Whatever number you come up with and return will be the price that the item is set to
return 2;
}
}
For the most part this should get you about 90% of where you want to be. Try this out and post questions and I'll do my best to help you.
Another Edit: Activating Helpers in config.xml
Put this code inside your config.xml file right after your observers block detailed above. This will tell Magento the location of the helper files for your module.
<helpers>
<custompricing>
<class>Ocaff_Custompricing_Helper</class>
</custompricing>
</helpers>
This is a tier prices feature, you don't need to create a catalog price rule. Check in the product edit page when you manage your products in the backend, the tab Prices and see the option "Tier Prices"
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!