I've been struggling with this for a while, should be pretty straight forward but...
I'm trying to apply coupon to order using magento cart.coupon.add api.
The product is Virtual
here is my code ( and I've tried everything i could find on google before I came here ):
protected function _applyCoupon($quoteId, $couponCode, $store = null)
{
$coupon = Mage::getModel('salesrule/coupon');
$coupon->loadByCode($couponCode);
Mage::log('_applyCoupon('.$couponCode.')');
$quote = $this->_getQuote($quoteId, $store);
if (!$quote->getItemsCount()) {
// $this->_fault('quote_is_empty');
}
$oldCouponCode = $quote->getCouponCode();
if (!strlen($couponCode) && !strlen($oldCouponCode)) {
return false;
}
try {
//$quote->getShippingAddress()->setCollectShippingRates(true);
$quote->setCouponCode($couponCode);
$quote->setTotalsCollectedFlag(false)->collectTotals();
$quote->collectTotals();
$quote->save();
Mage::getModel("checkout/session")->setData("coupon_code",$couponCode);
Mage::getModel('checkout/cart')->getQuote()->setCouponCode($couponCode)->save();
Mage::getModel('checkout/cart')->getQuote()->collectTotals();
Mage::getModel('checkout/cart')->getQuote()->save();
Mage::log("_applyCoupon : Set coupon to quote:".$quote->getCouponCode());
} catch (Exception $e) {
$this->_fault("cannot_apply_coupon_code", $e->getMessage());
}
Mage::log('3');
if ($couponCode) {
Mage::log("Coupon applied");
if (!$couponCode == $quote->getCouponCode()) {
Mage::log('3.2');
$this->_fault('coupon_code_is_not_valid');
}
}
return true;
}
I've also tried applying coupon to address:
protected function applyDiscountToAddress($address,$quote)
{
Mage::log('applyDiscountToProduct ...');
$coupon = Mage::getModel('salesrule/coupon');
Mage::log("checkoutprocess: checkout/session:".Mage::getModel("checkout/session")->getData("coupon_code"));
$coupon->loadByCode(Mage::getModel("checkout/session")->getData("coupon_code"));
$rule = Mage::getModel('salesrule/rule');
$rule->load($coupon->getRuleId());
$discountamount = $rule->getDiscountAmount();
$dbldiscount = 0.00 + $discountamount;
$grandTotal = Mage::getModel('checkout/cart')->getQuote()->getGrandTotal();
$subTotal = Mage::getModel('checkout/cart')->getQuote()->getSubtotal();
Mage::log('applyDiscountToProduct $grandTotal:'.$grandTotal);
Mage::log('applyDiscountToProduct $subTotal:'.$subTotal);
$gTotal = $grandTotal - $dbldiscount;
$address->setDiscountAmount($dbldiscount)
->setBaseDiscountAmount($dbldiscount)
->setGrandTotal($gTotal)
->setBaseGrandTotal($gTotal);
$grandTotal = $address->getGrandTotal();
$baseGrandTotal = $address->getBaseGrandTotal();
Mage::log('applyDiscountToProduct Address:$grandTotal:'.$grandTotal);
Mage::log('applyDiscountToProduct Address:$baseGrandTotal:'.$baseGrandTotal);
$totals = array_sum($address->getAllTotalAmounts());
$baseTotals = array_sum($address->getAllBaseTotalAmounts());
$address->setGrandTotal($grandTotal+$totals);
$address->setBaseGrandTotal($baseGrandTotal+$baseTotals);
}
the coupon is valid but after the order being placed in the Magento admin I see that the discount amount = 0.0 and the user was charged full amount to his credit card.
Anyone....? Help...?
Finally found an answer
I needed to call setCouponCode() before adding any items to quote.
$quote= Mage::getModel('sales/quote')->setCouponCode($couponCode);
Related
I am generating orders from a custom web service called from a mobile app. However when the order is finally generated, I get "Original Price" as Rs0.00 . This is creating a problem since we have third party integrations for invoicing and Shipping.
However in the normal course of action when order is generated from website, the Original Price is correct.
I inspected the sales_flat_order_item table for both these orders, the original_price is indeed 0.00 in the first order, Hence I must be missing something in my code which is not setting the original_price for the orderItem.
Here is the code where the quote is converted to order.
public function placeorder($custid, $Jproduct, $store, $address, $couponCode, $is_create_quote, $transid, $payment_code, $shipping_code, $currency, $message)
{
$res = array();
$quantity_error = array();
try {
$quote_data = $this->prepareQuote($custid, $Jproduct, $store, $address, $shipping_code, $couponCode, $currency, 1, 0);
if ($quote_data["status"] == "error") {
return $quote_data;
}
$quote = Mage::getModel('sales/quote')->load($quote_data['quote_id']);
$quote = $this->setQuoteGiftMessage($quote, $message, $custid);
$quote = $this->setQuotePayment($quote, $payment_code, $transid);
$convertQuote = Mage::getSingleton('sales/convert_quote');
try {
$order = $convertQuote->addressToOrder($quote->getShippingAddress());
}
catch (Exception $Exc) {
echo $Exc->getMessage();
}
$items = $quote->getAllItems();
foreach ($items as $item) {
$orderItem = $convertQuote->itemToOrderItem($item);
$order->addItem($orderItem);
}
try {
$decode_address = json_decode(base64_decode($address));
$order->setCustomer_email($decode_address->billing->email);
$order->setCustomerFirstname($decode_address->billing->firstname)->setCustomerLastname($decode_address->billing->lastname);
}
catch (Exception $e) {
}
$order->setBillingAddress($convertQuote->addressToOrderAddress($quote->getBillingAddress()));
$order->setShippingAddress($convertQuote->addressToOrderAddress($quote->getShippingAddress()));
$order->setPayment($convertQuote->paymentToOrderPayment($quote->getPayment()));
$order->save();
$quantity_error = $this->updateQuantityAfterOrder($Jproduct);
$res["status"] = 1;
$res["id"] = $order->getId();
$res["orderid"] = $order->getIncrementId();
$res["transid"] = $order->getPayment()->getTransactionId();
$res["shipping_method"] = $shipping_code;
$res["payment_method"] = $payment_code;
$res["quantity_error"] = $quantity_error;
$order->addStatusHistoryComment("Order was placed using Mobile App")->setIsVisibleOnFront(false)->setIsCustomerNotified(false);
if ($res["orderid"] > 0 && ($payment_code == "cashondelivery" || $payment_code == "banktransfer" || $payment_code == "free")) {
$this->ws_sendorderemail($res["orderid"]);
$order->setState(Mage_Sales_Model_Order::STATE_NEW, true)->save();
$res["order_status"] = "PENDING_PAYMENT";
} else {
$order->setState(Mage_Sales_Model_Order::STATE_PENDING_PAYMENT, true)->save();
$res["order_status"] = "PENDING_PAYMENT";
}
}catch (Exception $except) {
$res["status"] = 0;
$res["shipping_method"] = $shipping_code;
$res["payment_method"] = $payment_code;
}
return $res;
}
It would be very helpful if someone points out what I missed. I will edit if any other info is required.
Missing original_price indicates that you didn't run collectTotals() on the quote yet, try the following:
$quote->collectTotals()->save();
$order->save();
After struggling attempting to fix a bundle price whilst remaining VAT calculations, my next attempt to resolve the issue is to apply a discount to the bundle to reduce it to the fixed amount.
<?php
class XXX_Fixedbundlediscount_Model_Observer
{
public function setDiscount($observer)
{
Mage::log('settingDiscount');
$quote = $observer->getEvent()->getQuote();
$quoteid = $quote->getId();
if ($quoteid) {
foreach ($quote->getAllItems() as $item) {
$product = $item->getProduct();
$fixed_price_attribute = (float)Mage::getResourceModel('catalog/product')->getAttributeRawValue($product->getId(), 'giftset_fixed_price', Mage::app()->getStore()->getStoreId());
if ($product->getTypeId() !== Mage_Catalog_Model_Product_Type::TYPE_BUNDLE || !$fixed_price_attribute) {
continue;
}
$lineTotal = (float)$item->getPriceInclTax();
$item->setBaseDiscountAmount(0);
$item->setDiscountAmount(0);
if ($lineTotal > $fixed_price_attribute) {
$item->setDiscountAmount(-($lineTotal - $fixed_price_attribute));
$item->setDiscountDescription('Gift Set');
$item->setBaseDiscountAmount(-($lineTotal - $fixed_price_attribute))->save();
Mage::log('xxxx');
}
Mage::log($fixed_price_attribute);
Mage::log($lineTotal);
Mage::log(($fixed_price_attribute > $lineTotal) ? 'Yes' : 'No');
}
}
}
}
I've set up a custom attribute on the Bundled products which specifies the fixed cost. As you can see from the above, the idea is to detect this, calculate the cost of the bundle and add the difference of that between the attribute value as a discount.
Unfortunately it's not adding any discounts what so ever...
Can any one suggest anything that may be of any use?
Thanks
Gav
I managed to find a way in the end:
<?php
class XXX_Fixedbundlediscount_Model_Observer
{
public function setDiscount($observer)
{
$quote = $observer->getEvent()->getQuote();
$quoteid = $quote->getId();
$discountAmount = 0;
if ($quoteid) {
foreach ($quote->getAllItems() as $item) {
$product = $item->getProduct();
$fixed_price_attribute = (float)Mage::getResourceModel('catalog/product')->getAttributeRawValue($product->getId(), 'giftset_fixed_price', Mage::app()->getStore()->getStoreId());
if ($product->getTypeId() !== Mage_Catalog_Model_Product_Type::TYPE_BUNDLE || !$fixed_price_attribute) {
continue;
}
if ($item->getPriceInclTax() > $fixed_price_attribute) {
$discountAmount += $item->getQty() * ($item->getPriceInclTax() - $fixed_price_attribute);
}
}
if ($discountAmount > 0) {
$total = $quote->getBaseSubtotal();
$quote->setSubtotal(0);
$quote->setBaseSubtotal(0);
$quote->setSubtotalWithDiscount(0);
$quote->setBaseSubtotalWithDiscount(0);
$quote->setGrandTotal(0);
$quote->setBaseGrandTotal(0);
$canAddItems = $quote->isVirtual() ? ('billing') : ('shipping');
foreach ($quote->getAllAddresses() as $address) {
$address->setSubtotal(0);
$address->setBaseSubtotal(0);
$address->setGrandTotal(0);
$address->setBaseGrandTotal(0);
$address->collectTotals();
$quote->setSubtotal((float)$quote->getSubtotal() + $address->getSubtotal());
$quote->setBaseSubtotal((float)$quote->getBaseSubtotal() + $address->getBaseSubtotal());
$quote->setSubtotalWithDiscount(
(float)$quote->getSubtotalWithDiscount() + $address->getSubtotalWithDiscount()
);
$quote->setBaseSubtotalWithDiscount(
(float)$quote->getBaseSubtotalWithDiscount() + $address->getBaseSubtotalWithDiscount()
);
$quote->setGrandTotal((float)$quote->getGrandTotal() + $address->getGrandTotal());
$quote->setBaseGrandTotal((float)$quote->getBaseGrandTotal() + $address->getBaseGrandTotal());
$quote->save();
$quote->setGrandTotal($quote->getGrandTotal() - ($discountAmount / 2))
->setBaseGrandTotal($quote->getBaseGrandTotal() - $discountAmount)
->setSubtotalWithDiscount($quote->getSubtotalWithDiscount() - $discountAmount)
->setBaseSubtotalWithDiscount($quote->getBaseSubtotalWithDiscount() - $discountAmount)
->save();
if ($address->getAddressType() == $canAddItems) {
$address->setSubtotalWithDiscount((float)$address->getSubtotalWithDiscount() - $discountAmount);
$address->setGrandTotal((float)$address->getGrandTotal() - $discountAmount);
$address->setBaseSubtotalWithDiscount((float)$address->getBaseSubtotalWithDiscount() - $discountAmount);
$address->setBaseGrandTotal((float)$address->getBaseGrandTotal() - $discountAmount);
if ($address->getDiscountDescription()) {
$address->setDiscountAmount(-($address->getDiscountAmount() - $discountAmount));
$address->setDiscountDescription($address->getDiscountDescription() . ', Gift Sets');
$address->setBaseDiscountAmount(-($address->getBaseDiscountAmount() - $discountAmount));
} else {
$address->setDiscountAmount(-($discountAmount));
$address->setDiscountDescription('Gift Sets');
$address->setBaseDiscountAmount(-($discountAmount));
}
$address->save();
}
}
foreach ($quote->getAllItems() as $item) {
$product = $item->getProduct();
$fixed_price_attribute = (float)Mage::getResourceModel('catalog/product')->getAttributeRawValue($product->getId(), 'giftset_fixed_price', Mage::app()->getStore()->getStoreId());
if ($product->getTypeId() !== Mage_Catalog_Model_Product_Type::TYPE_BUNDLE || !$fixed_price_attribute) {
continue;
}
$lineDiscount = 0;
if ($item->getPriceInclTax() > $fixed_price_attribute) {
$lineDiscount = $item->getQty() * ($item->getPriceInclTax() - $fixed_price_attribute);
}
$item->setDiscountAmount($lineDiscount);
$item->setBaseDiscountAmount($lineDiscount)->save();
}
}
}
}
}
The above simply allows the bundled product to remain dynamic whilst having a fixed price. If the dynamic value is more than the fixed price, it will add a discount to reduce it back to the fixed price.
I'll update this shortly with code that excludes extra products added to the bundle.
i am tyring to add categories programmatically for german and english languages.
website-ID: 1
website store-ID: 1
view-ID english, code "en" => 1
view-ID german, code "de" => 2
$category['general']['path'] = $v['cat_name_de'];
$category['general']['name'] = $v['cat_name_de'];
$category['general']['meta_title'] = "";
$category['general']['meta_description'] = "";
$category['general']['is_active'] = 1;
$category['general']['url_key'] = urlencode($v['cat_name_de']);
$category['general']['display_mode'] = "PRODUCTS";
$category['general']['is_anchor'] = 0;
$category['category']['parent'] = $v['magento_cat_id'];
$storeId = 0; // default
$magento_subcat_id = createCategory($category, $storeId);
...
function createCategory($data, $storeId) {
echo "Erzeuge '{$data['general']['name']}' - Elternkategorie:
[{$data['category']['parent']}] ...";
$category = Mage::getModel('catalog/category');
$category->setStoreId($storeId);
if (is_array($data)) {
$category->addData($data['general']);
if (!$category->getId()) {
$parentId = $data['category']['parent'];
if (!$parentId) {
if ($storeId) {
$parentId = Mage::app()->getStore($storeId)->getRootCategoryId();
} else {
$parentId = Mage_Catalog_Model_Category::TREE_ROOT_ID;
}
}
$parentCategory = Mage::getModel('catalog/category')->load($parentId);
$category->setPath($parentCategory->getPath());
}
if ($useDefaults = $data['use_default']) {
foreach ($useDefaults as $attributeCode) {
$category->setData($attributeCode, null);
}
}
$category->setAttributeSetId($category->getDefaultAttributeSetId());
if (isset($data['category_products']) &&
!$category->getProductsReadonly()) {
$products = array();
parse_str($data['category_products'], $products);
$category->setPostedProducts($products);
}
try {
$category->save();
return $category->getId();
} catch (Exception $e) {
return FALSE;
}
}
}
What i am puzzling with at the moment is setting view-ID individually for passing the $category['general']['name'] for each language.
I tried to set each language string with a foreach setting english/german texts. But this did not work out, because i somehow mixed up setting the view correctly.
To make it short:
Creating categories and subcategories like this does work very good, but i do not get it to work to pass english/german categoty titles.
Thanks guys
For all of you who are struggeling with this situation i made a workaound like this:
I added all those values to the affected database tables directly. This is for sure a quick and dirty way, but it does the job.
For all in need of some ideas here is the (undocumented) snipped belonging to my initial post.
// Get values for next language insert
$entity_type_id = $category
->getResource()
->getTypeId();
$entity_id = $category->getEntityId();
$read_entity = Mage::getSingleton('core/resource')
->getConnection('core_write');
$sql_entity = "SELECT * FROM eav_attribute WHERE
`attribute_code` = 'name' AND
`entity_type_id` = " . $entity_type_id . ";";
$row_entity = $read_entity
->fetchRow($sql_entity);
$attribute_id = $row_entity['attribute_id'];
// prepare value for name field in different langauge
$val = Mage::getSingleton('core/resource')
->getConnection('default_write')
->quote($v['en']);
$sql_insert = "INSERT INTO `catalog_category_entity_varchar`
(`entity_type_id`,`attribute_id`,`store_id`,`entity_id`,`value`)
VALUES ($entity_type_id,$attribute_id,$storeId,$entity_id,$val);";
$read_entity->query($sql_insert);
I hope this helps someone out who is in need of this.
I have an order that looks like this:
I want to create a shipment for the following on this order (not for everything):
2 x VCF001
1 x SBA364
To achieve this, I found the following info on the shipment api: http://bit.ly/17rpuwj
Based on this documentation of the API, I came up with the following code. When I tried to execute it, I got the following error Cannot create an empty shipment:
$order_number = '300000002';
$order = Mage::getModel('sales/order')->loadByIncrementId($order_number);
$allocations = array(
array('sku' => 'VCF001', 'allocated_qty' => 2),
array('sku' => 'SBA364', 'allocated_qty' => 1)
);
function GetOrderItemId($order_items, $sku) {
foreach ($order_items as $order_item) {
if ($order_item->getSku() == $sku) {
return $order_item->getItemId();
}
}
return 0;
}
function CreateShipment($order, $allocations)
{
// Get Order Items
$order_items = $order->getItemsCollection();
// Parepare Item Qtys For Shipment
$item_qtys = array();
foreach ($allocations as $allocation) {
$item_qtys[] = array(GetOrderItemId($order_items,
$allocation['sku']) => $allocation['allocated_qty']);
}
// Anticipate Errors
try
{
// Create Shipment
$shipment = new Mage_Sales_Model_Order_Shipment_Api();
$shipmentId = $shipment->create($order->getIncrementId(),
$item_qtys, 'Pick #123 Sent to Warehouse');
// Done
echo 'Shipment Created - ID #'. $shipmentId;
}
catch (Exception $e)
{
print_r($e);
}
}
CreateShipment($order, $allocations);
Any idea why this isn't working as intended? See the full exception log here : http://pastebin.com/raw.php?i=GMN4WCAE
Try it like this instead :
protected function _createShipment($order, $qtysForProducts)
{
$shipment = $order->prepareShipment($qtysForProducts);
if ($shipment) {
$shipment->register();
$shipment->sendEmail(true)
->setEmailSent(true)
->save();
$order->setIsInProcess(true);
$transactionSave = Mage::getModel('core/resource_transaction')
->addObject($shipment)
->addObject($shipment->getOrder())
->save();
}
return $shipment;
}
How can i create partial create memo? i.e. if total order value is £50 and I want to create a credit memo of £10. I am using the below code to create credit memo:
function createCreditMemo($orderId) {
$order = Mage::getModel('sales/order')->load($orderId, 'increment_id');
if (!$order->getId()) {
print_r('order_not_exists');
}
if (!$order->canCreditmemo()) {
Mage::log('cannot_create_creditmemo', null, 'product.txt');
print_r('cannot_create_creditmemo');
}
$data = array();
$refundToStoreCreditAmount = '10.22';
$service = Mage::getModel('sales/service_order', $order);
$creditmemo = $service->prepareCreditmemo($data);
// refund to Store Credit
if ($refundToStoreCreditAmount) {
// check if refund to Store Credit is available
if ($order->getCustomerIsGuest()) {
print_r('cannot_refund_to_storecredit');
}
$refundToStoreCreditAmount = max(
0,
min($creditmemo->getBaseCustomerBalanceReturnMax(), $refundToStoreCreditAmount)
);
if ($refundToStoreCreditAmount) {
$refundToStoreCreditAmount = $creditmemo->getStore()->roundPrice($refundToStoreCreditAmount);
$creditmemo->setBaseCustomerBalanceTotalRefunded($refundToStoreCreditAmount);
$refundToStoreCreditAmount = $creditmemo->getStore()->roundPrice(
$refundToStoreCreditAmount*$order->getStoreToOrderRate()
);
// this field can be used by customer balance observer
$creditmemo->setBsCustomerBalTotalRefunded($refundToStoreCreditAmount);
// setting flag to make actual refund to customer balance after credit memo save
$creditmemo->setCustomerBalanceRefundFlag(true);
print_r($refundToStoreCreditAmount.'<br/>');
//die('2');
}
}
//Mage::log($creditmemo, null, 'product.txt');
$creditmemo->setPaymentRefundDisallowed(true);
$creditmemo->register();
$orderCreditMemoStatusCode = Mage_Sales_Model_Order::STATE_CLOSED;
$orderCreditMemoStatusComment = 'Order Refunded.';
$saveTransaction = Mage::getModel('core/resource_transaction')->addObject ($creditmemo )->addObject ( $order )->save ();
$order->addStatusToHistory ( $orderCreditMemoStatusCode, $orderCreditMemoStatusComment, true );
$notifyCustomer = true;
$comment = 'testing refund';
$includeComment = true;
$creditmemo->setEmailSent(true);
// add comment to creditmemo
if (!empty($comment)) {
$creditmemo->addComment($comment, $notifyCustomer);
}
try {
Mage::getModel('core/resource_transaction')
->addObject($creditmemo)
->addObject($order)
->save();
// send email notification
$creditmemo->sendEmail($notifyCustomer, ($includeComment ? $comment : ''));
} catch (Mage_Core_Exception $e) {
print_r('data_invalid', $e->getMessage());
}
echo $creditmemo->getIncrementId();
}
In the below code, even if I set the $refundToStoreCreditAmount = 10; it still refunds the full amount. Any idea how to refund partially?