Update Item Product Options After Order Placed - magento

I'm working on an observer that needs to add (a) serial key(s) to each item in the cart once an order is placed.
I'm listening to the event sales_model_service_quote_submit_success right now.
I've been able to access the order, get a list of the items, iterate through them, and get the product options. My code fails either when I try to setProductOptions or save--I'm not sure which, maybe it's both.
Here is the relevant code:
// Get access to order information
$lastOrderId = Mage::getSingleton('checkout/session')->getLastOrderId();
$order = Mage::getModel('sales/order')->load($lastOrderId);
// Get the items from the order
$items = $order->getAllItems();
foreach ($items as $item)
{
// Pretend here is the call that fetches the serial keys for this item and stores them in $keyString
// If we actually received the keys in a string, store them with the item
if (!empty($keyString))
{
$productOptions = array();
if (count($item->getProductOptions()))
{
$productOptions = $item->getProductOptions();
}
$productOptions['keys'] = $keyString;
$item->setProductOptions($productOptions);
$item->save();
}
}
Any ideas what I have forgotten or done wrong? Thanks a bunch.

no such observer , at least i didn't find it from codebase, here's what you can use
Mage::dispatchEvent('sales_model_service_quote_submit_before', array('order'=>$order, 'quote'=>$quote));
Mage::dispatchEvent('sales_model_service_quote_submit_after', array('order'=>$order, 'quote'=>$quote));
and in your observer method
/**
*
* #param Varien_Event_Observer $observer
*
*/
public function setShippingDefaults(Varien_Event_Observer $observer) {
$order = $observer->getEvent()->getOrder();
}
the idea is that if you do it in before-action then you won't need to call save and if you do it in after-action then you do and if you do it in before action you might just end in endless loop if you are not careful.

Related

Order split with online transaction on checkout in magento 2.4 enterprise

I'm facing one issue while splitting the order on checkout. I followed these code mentioned in the link:-
https://magento.stackexchange.com/questions/196669/magento-2-split-order-for-every-item
and
https://github.com/magestat/magento2-split-order
Both solution is working with offline payment like check/mo, Cash on delivery, po number etc. But its not working with credit card details. Always getting error regarding credit card details.
I'm putting some more information through code:-
I am stuck at a point to distribute order and assign payment method into it.
there are two scenario i'm getting:
if i assign payment method checkmo,Cash on delivery then order is splitted and everything is working fine with this.
But i need to order products using credit card and when i assign payment method code(credit card payment method is 'nmi_directpost') and also assign card details into quote and placed and order then its showing me error differently, Some time its shows credit card details is not valid, sometime page is redirected to cart page without any log/exception. Here is bunch of code i'm trying to do:-
public function aroundPlaceOrder(QuoteManagement $subject, callable $proceed, $cartId, $payment = null)
{
$currentQuote = $this->quoteRepository->getActive($cartId);
// Separate all items in quote into new quotes.
$quotes = $this->quoteHandler->normalizeQuotes($currentQuote);
if (empty($quotes)) {
return $result = array_values([($proceed($cartId, $payment))]);
}
// Collect list of data addresses.
$addresses = $this->quoteHandler->collectAddressesData($currentQuote);
/** #var \Magento\Sales\Api\Data\OrderInterface[] $orders */
$orders = [];
$orderIds = [];
foreach ($quotes as $items) {
/** #var \Magento\Quote\Model\Quote $split */
$split = $this->quoteFactory->create();
// Set all customer definition data.
$this->quoteHandler->setCustomerData($currentQuote, $split);
$this->toSaveQuote($split);
// Map quote items.
foreach ($items as $item) {
// Add item by item.
$item->setId(null);
$split->addItem($item);
}
\Magento\Framework\App\ObjectManager::getInstance()->get('Psr\Log\LoggerInterface')->info('new quote 1st :-'. print_r($split->getData(),true));
$this->quoteHandler->populateQuote($quotes, $split, $items, $addresses, $payment);
// $split->getPayment()->setMethod('nmi_directpost');
// if ($payment) {
// $split->getPayment()->setQuote($split);
// $data = $payment->getData();
// $paymentDetails = $paymentCardDetails = '';
// $postData = file_get_contents("php://input");//Get all param
// $postData = (array)json_decode($postData);//Decode all json param
// foreach ($postData as $key => $value) {
// if ($key == 'paymentMethod') { //Get paymentMethod details
// $paymentDetails = (array)$value;
// foreach ($paymentDetails as $key1 => $paymentValue) {
// if ($key1 == 'additional_data') { //get paymentMethod Details like card details
// $paymentCardDetails = (array)$paymentValue;
// }
// }
// }
// }
// $split->setMethod('checkmo');
\Magento\Framework\App\ObjectManager::getInstance()->get('Psr\Log\LoggerInterface')->info('Paynet :-');
// $payment = $quotes->getPayment();
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$cart = $objectManager->get('\Magento\Checkout\Model\Cart');
$quote = $cart->getQuote();
$paymentMethod = $quote->getPayment()->getMethod();
$payment = $this->checkoutSession->getQuote()->getData();
\Magento\Framework\App\ObjectManager::getInstance()->get('Psr\Log\LoggerInterface')->info('second Paynet :-');
\Magento\Framework\App\ObjectManager::getInstance()->get('Psr\Log\LoggerInterface')->info('new quote :-'. print_r($paymentMethod,true));
// $split->setPayment($payment);
// $split->getPayment()->importData(array(
// 'method' =>'nmi_directpost',
// 'cc_type' =>'VI',
// 'cc_number' =>'4111111111111111',
// 'cc_exp_year' =>'2025',
// 'cc_exp_month'=>'10',
// ));
// }
// \Magento\Framework\App\ObjectManager::getInstance()->get('Psr\Log\LoggerInterface')->info('original quote :-'. print_r($quotes->getData(),true));
\Magento\Framework\App\ObjectManager::getInstance()->get('Psr\Log\LoggerInterface')->info('new quote :-'. print_r($split->getData(),true));
// \Magento\Framework\App\ObjectManager::getInstance()->get('Psr\Log\LoggerInterface')->info('new quote :-'. print_r($payment->getData(),true));
// Dispatch event as Magento standard once per each quote split.
$this->eventManager->dispatch(
'checkout_submit_before',
['quote' => $split]
);
$this->toSaveQuote($split);
$order = $subject->submit($split);
$orders[] = $order;
$orderIds[$order->getId()] = $order->getIncrementId();
if (null == $order) {
throw new LocalizedException(__('Please try to place the order again.'));
}
}
$currentQuote->setIsActive(false);
$this->toSaveQuote($currentQuote);
$this->quoteHandler->defineSessions($split, $order, $orderIds);
$this->eventManager->dispatch(
'checkout_submit_all_after',
['orders' => $orders, 'quote' => $currentQuote]
);
return $this->getOrderKeys($orderIds);
}
Please suggest how can we achieve order splitting with credit card payment.
Splitting payment across multiple credit cards like this is referred to as 'partial authorization'. (Note: This is a very different thing from 'partial invoicing' or 'partial capturing', terms you'll also see thrown around.)
Magento's default Authorize.Net gateway includes partial authorization functionality, you just have to enable it in the gateway settings. This works with both Community and Enterprise Edition. See official documentation on the setup and workflow here.
To my knowledge, this is the only payment method that supports it.
Note that the customer does not get to choose how much to charge to each card. Rather, if the card they enter does not have sufficient funds, they will be prompted to enter another one.

get check out cart attributes using magento

I was successfully getting the all the cart details(i.e total items, items in the cart, etc).
But i want only specific attributes from cart.
suppose i want to get store_id attribute, how can i achieve.
public function info($quoteId, $store = null)
{
$quote = $this->_getQuote($quoteId, $store);
$result_attr = $this->_getAttributes($quote, 'quote');
//this is giving error
$result_attr->getAttributeText('store_id');
}
if you are getting object in your $quote
you can get with below code
if($quote )
$store = $quote->getStoreId();
hope this will sure help you.

Custom Tier Price not working in checkout page magento

I have developed a custom module to meet my project requirements using Alan Storms tutorial for creating modules in magento.
I had the requirement of changing the price attribute dynamically on frontend based on a livefeed. Everysecond the feed is updated so every time the page refreshes a new price must be displayed for each product on the site.
I have override the product module and the price modules for this purpose. The issue is with tier pricing. When tier pricing comes into place I need to calculate the tier-price based on the live price.
For this also I managed to change using the price_type class override.
Now whenever an item is added to cart the tier-pricing was not working for that I wrote event_trigger ie an Observer which updates the tier_pricing on the event "checkout_cart_save_before" and here's my code
class My_Custom_Model_Observer extends Varien_Event_Observer
{
public function __construct()
{
}
public function updateCartBasedOnLiveFeed($observer)
{
foreach ($observer->getCart()->getQuote()->getAllVisibleItems() as $item /* #var $item Mage_Sales_Model_Quote_Item */)
{
$tierPrices = array();
$tierPrices = $item->getProduct()->getTierPrice();
$itemPrice = $item->getProduct()->getPrice();
$i=0;
foreach($tierPrices as $key => $tierPrice)
{
if(!is_numeric($key))
{
$updatedTierPrice = $itemPrice - ($itemPrice * ($tierPrice['price']/100));
$tierPrices[$key]['price'] = $updatedTierPrice;
$tierPrices[$key]['website_price'] = $updatedTierPrice;
}
else
{
if($tierPrice['price'] > 0)
{
$updatedTierPrice = $itemPrice - ($itemPrice * ($tierPrice['price']/100));
$tierPrice['price'] = $updatedTierPrice;
$tierPrice['website_price'] = $updatedTierPrice;
$tierPrices[$i] = $tierPrice;
$i++;
}
}
}
$item->getProduct()->setData('tier_price',$tierPrices);
}
}
}
The above code works excellently in cart page. But when it comes to checkout page. It works for a single item and when tier-pricing comes into play it does apply cart prices.
Please help me with this.
I also tried using other events along with the above event.
Event: sales_quote_save_before
public function updateQuoteLive($observer)
{
$tierPrices = array();
$quote_item = $observer->getEvent()->getQuote;
$itemPrice = $quote_item->getProduct()->getPrice();
$tierPrices = $quote_item->getProduct()->getTierPrice();
$tierPricesSize = sizeof($tierPrices);
for($i=0;$i<$tierPricesSize;$i++)
{
$updatedTierPrice = $itemPrice - ($itemPrice * ($tierPrices[$i]['price']/100));
$tierPrices[$i]['price'] = $updatedTierPrice;
$tierPrices[$i]['website_price'] = $updatedTierPrice;
}
$quote_item->getProduct()->setData('tier_price',$tierPrices);
}
When I tried to print the getQuote() function available in Quote.php I find that the tier prices there are not the ones which I updated using the first event. So I think I need to update the price before saving the quote. Please any one help me and show the correct direction.
Please help me with this I am missing some important step. Any help is greatly appreciated.
Thanks in advance.
It might be better off "saving" the new price in to the database when you update.
Try something along the lines of:
$product = $observer->getProduct();
$procuct->setPrice($updatedPrice);
This way when it comes to checkout it will be pulling in the correct price from the database (and avoids the headache of correcting it "mid-flight"
i realized such a project like you. I have no sales_quote_save_before Observer. I only use the checkout_cart_save_before. Based on the session the price will be setted.
I realized that like this way:
public function updatePrice( $observer )
{
try {
$cart = $observer->getCart();
$items = $cart->getItems();
foreach($items as $item)
{
$item->setCustomPrice($price);
$item->setOriginalCustomPrice($price);
}
} catch ( Exception $e )
{
Mage::log( "checkout_cart_save_before: " . $e->getMessage() );
}
}
I calcute the tierprices on the fly and with this Observer. All prices will be set up correct in the qoute.
Maybe you should try this way.
Regards boti
At last figured out the issue and got the solution.
The problem was that in cart page or checkout page when the getTierPrice() function is called, which is present in /app/code/core/Mage/Catalog/Product.php. It takes one parameter named $qty which is by default null. This function in turn calls the function getTierPrice which is present in /app/code/core/Mage/Type/Price.php file which takes two parameters $qty and $productObject. By default $qty is null and when it is null the function returns an array of tier_prices. But when the $qty value is passed then the function returns a single for that particular quantity.
So, I wrote my own custom function which calculates the tier prices based no my requirements like
I overridden both the core files with my custom module following Alan Storm's tutorials.
I've extended Mage_Catalog_Model_Product with My_CustomModule_Model_Product class and
then
Mage_Catalog_Model_Product_Type_Price with My_CustomModule_Model_Price
And then in /app/code/local/My/Custommodule/Model/Product.php
I added my custom code like
public function getTierPrice($qty=null)
{
if($qty)
{
return $this->getPriceModel()->getCustomTierPrice($qty, $this);
}
return $this->getPriceModel()->getTierPrice($qty, $this);
}
Then in /app/code/local/My/Custommodule/Model/Price.php
<?php
public function getCustomTierPrice($qty = null, $product)
{
$allGroups = Mage_Customer_Model_Group::CUST_GROUP_ALL;
$prices = $product->getData('tier_price');
if (is_null($prices)) {
$attribute = $product->getResource()->getAttribute('tier_price');
if ($attribute) {
$attribute->getBackend()->afterLoad($product);
$prices = $product->getData('tier_price');
}
}
foreach($prices as $key => $customPrices)
{
if($prices[$key]['price'] < 1)
{
$prices[$key]['price'] = abs($product->getPrice() - ($productPrice * ($customPrices['price']/100)));
$prices[$key]['website_price'] = $prices[$key]['price'];
}
}
}
which retured a customized value when $qty is passed and voila it worked.
I just posed this answer so that any one else who has similar requirement may get benefited with this.
?>

Magento - Add to Cart Error

I'm getting this error after add/remove item to/from cart. Add to Cart button does ajax call to add item to the cart. This kind of json string which will be used in top cart:
I'm stuck. Can you tell me where should I start from to debug?
the "Add to Cart" button does not work asynchrounisly in Magento's default behaviour. That means that you installed/developped a module to do this. For us to help you, we need to know what is it.
Anyways, this looks like a Zend_Dump or maybe a die: make a search in your files for these strings and see what comes up
Use Netbeans and Xdebug.
http://wiki.netbeans.org/HowToConfigureXDebug
Place a breakpoint on the /app/code/core/Mage/Sales/Model/Quote.php inside this function:
/**
* Adding catalog product object data to quote
*
* #param Mage_Catalog_Model_Product $product
* #return Mage_Sales_Model_Quote_Item
*/
protected function _addCatalogProduct(Mage_Catalog_Model_Product $product, $qty = 1)
{
$newItem = false;
$item = $this->getItemByProduct($product);
if (!$item) {
$item = Mage::getModel('sales/quote_item');
$item->setQuote($this);
if (Mage::app()->getStore()->isAdmin()) {
$item->setStoreId($this->getStore()->getId());
}
else {
$item->setStoreId(Mage::app()->getStore()->getId());
}
$newItem = true;
}
/**
* We can't modify existing child items
*/
if ($item->getId() && $product->getParentProductId()) {
return $item;
}
$item->setOptions($product->getCustomOptions())
->setProduct($product);
// Add only item that is not in quote already (there can be other new or already saved item
if ($newItem) {
$this->addItem($item);
}
return $item;
}
HTH

How to update record with OneToMany relationship in Doctrine 2?

I have a user entity and I'm trying to update it from a UserService. The problem comes when I try to update a property which is set as an array collection.
/**
*
* #param \Doctring\Common\Collections\Collection $property
* #OneToMany(targetEntity="Countries",mappedBy="user", cascade={"persist", "remove"})
*/
private $countries;
I'm not sure if I'm supposed to somehow delete all the $countries before I set them back or if there's a way to select which ones to delete and set up the different ones....this is what my updateUser method looks so far:
public function updateUser($user) {
$entity = $this->getUser($user['id']);
if (!$entity)
throw new Exception('Error saving user!');
$countries = $this->getCountriesArray($user); //countries already set in the db
$tempCountries = array();
$delete = array();
foreach ($countries as $country) {
$found = false;
if (in_array($country, $user['countries'])) {
$tempCountries[] = $country;
} else {
$delete[] = $country;
}
}
$tempCountries = array_unique(array_merge( //combine the countries from the db we want to leave
$tempCountries, //with those the user selected
$user['countries']));
...
//here I need something to remove the countries in $delete...right?
...
$entity->setEmail($user['email']);
$entity->setResponsable($user['responsable']);
$entity->setPassword($this->createPass($user['password']));
$entity->setUrl($user['url']);
$entity->setRole($user['role']);
$modelCountries = array();
foreach($tempCountries as $c) {
$p = new Countries();
$p->setCountryName($c);
$p->setUser($entity);
$modelCountries[] = $p;
}
$entity->setCountries($modelCountries);
$this->em->persist($entity);
$this->em->flush();
}
please stackOverflow... give me a hand making sense out of this.
It actually depends on your use case.
As you said, you can either delete all countries before to set the new ones, or compute the delta, and update only the needed ones.
What countries are you providing to your service? The delta? Or full list?
Do you have performance constraints? If yes, what is the cost of a DELETE statements vs SELECT then UPDATE?
Compute delta, then UPDATE can be tricky and difficult, in most case, you better want to just DELETE all and INSERT.
For my current and personal choice I am using DQL to DELETE all owing side rows for the mapped entity and then inserting the new one.
class Rule
{
/**
* #OneToMany(targetEntity="Tier", mappedBy="rule", cascade={"persist", "remove"})
* #JoinColumn(name="ruleId", referencedColumnName="ruleId")
* #var Tier[]
*/
public $tiers;
So when I am passing new Tier in my call I am simply Deleting the all Tiers for that Role from the Rule Mapper
public function resetTiers($ruleId)
{
$modelClass = "Tier";
$q = $this->doctrineEntityManager->createQuery("Delete from $modelClass m where m.rule =" . $ruleId);
$numDeleted = $q->execute();
return $numDeleted;
}
and then calling the usual
$this->doctrineEntityManager->persist($rule);
$this->doctrineEntityManager->flush();
Hope that helps

Resources