Tuesday, April 12, 2011

Adding 'on the fly' form validation

Here is how you can add 'on the fly' validation for forms in Magento. I mean to validate the forms not only on submit but after editing a field.
Put the following line in a js file included in all the pages or in the <head> tag
Validation.defaultOptions.immediate = true; 

If you also want the classes of the validation to be added not only for the input but fur it's parent also add the following line in the same place as the one above.
Validation.defaultOptions.addClassNameToContainer = true;

Hope it helps

Wednesday, November 24, 2010

How to add tier prices in the product list

Inside the app/design/frontend/{interface}/{theme}/template/catalog/product/list.phtml add this inside the foreach loops for grid and/or list
<?php $this->setProduct(Mage::getModel('catalog/product')->setStoreId(Mage::app()->getStore()->getId())->load($_product->getId()))?>
<?php echo $this->getTierPriceHtml() ?> 

It does not look nice for grid but it can be fixed by css.

Tuesday, August 3, 2010

How to delete orders from Magento

This is a response for http://www.magentocommerce.com/boards/viewthread/197549/
Problem: How to delete (test) orders from Magento.
Possible solution:

Here is a free but 'quick and dirty' way to do it.
Create a file on the same level as index.php. Let's call it orders.php
<?php
$mageFilename = 'app/Mage.php';
require_once $mageFilename;
Varien_Profiler::enable();
Mage::setIsDeveloperMode(true);
ini_set('display_errors', 1);
umask(0);
Mage::app('default');
Mage::register('isSecureArea', 1);
//until here you gained access to the Magento models. The rest is custom code

$orderId = 156;//put here the id of the order you want to delete. THE ONE FROM THE DATABASE NOT THE INCREMENT_ID

$order = Mage::getModel('sales/order')->load($orderId);
$invoices = $order->getInvoiceCollection();
foreach ($invoices as $invoice){
$invoice->delete();
}
$creditnotes = $order->getCreditmemosCollection();
foreach ($creditnotes as $creditnote){
$creditnote->delete();
}
$shipments = $order->getShipmentsCollection();
foreach ($shipments as $shipment){
$shipment->delete();
}
$order->delete();
?>
Save and call it in your browser (http://my-magento.root/orders.php)
This will delete an order with the id you specify.
If you want to delete more of them just create a for (foreach) loop that will execute this code for each order you want to delete.

For example, if all your test orders are consecutive up to a number you can do this:
//let's say that the orders with ids less than 200 are test orders
$collection = Mage::getModel('sales/order')->getCollection()->addAttributeToFilter(array(array('attribute'=>'entity_id', 'lt'=>200)));
foreach ($collection as $o){
$orderId = $o->getId();
//here goes the code above starting below $orderId = 156.
}

I've used it in several occasions and it worked, but please back-up before using it. I will not be responsible if something goes wrong :)

Wednesday, June 9, 2010

Adding related products, up-sells and cross-sells programmaticly

Problem:
Adding related products, up-sells and cross-sells programmaticly.
http://www.magentocommerce.com/boards/viewthread/195440/

Possible solution:

In order to set related, up-sells and crosssells you need to do the following
Let's assume that the current product is $_product (that you got by doing Mage::getModel('catalog/product')->load(SOME ID))
//for related
$_product->setRelatedLinkData($param);
//for up-sells
$_product->setUpSellLinkData($param);
//for crosssells
$_product->setCrossSellLinkData($param);
$param is an array with the following structure
$param = array(
$associatedProductId=>array(
'position'=>$associatedProductPosition
)
)

Here is an example. Let's say you want to add products with ids 101 and 102 as related products to $_product on positions 3 and 5.
You should do something like this:

$param = array(
101=>array(
'position'=>3
),
102=>array(
'position'=>5
)
);
$_product->setRelatedLinkData($param);
//here ... some other product operations and in the end
$_product->save();

It works the same for up-sells and crosssels.

Thursday, May 20, 2010

how to find methods in Magento

Problem: http://www.magentocommerce.com/boards/viewthread/193422/
Here is how I've learned.
Let's say you have in a phtml file this line of code.
$object->method();
($object can be anything including $this).
And you don't know what type is $object.
put this line above the method call.
echo get_class($object);
Clear the cache (contents of var/cache) and reload the page. You should see the name of the class. For example Mage_Catalog_Model_Category. now all you have to do is to find the class.
Because Magento is based on Zend Framework it uses the the same convention for class naming.
So class Abc_Def_Ghi will be in the file Abc/Def/Ghi.php (for the example above the file is Mage/Catalog/Model/Category.php)
All the classes are placed in app/code/core or app/code/local/ or app/code/community or lib/ and after that follow the path like explained above.

If you don't find a method in that specific class look in one of the parents. Check the class that the current class extends.

You will not find all the methods in Magento. For example some of the methods that start with get, set, has, uns is possible that the methods are missing.
For this take a look in Varien_Object class (that is the base class for most of the objects) lib/Varien/Object.php file (remember the convention above?) and look in the 'magic' method __call().

Monday, May 17, 2010

Import products update

This is an update for http://marius-strajeru.blogspot.com/2009/11/import-simple-products-for-barrmy.html

Starting version 1.4 you can only perform product updates 'programmatic' only if Magento thinks you are in the admin panel.

If you try to do this from a non-admin page make sure you put this

Mage::app()->setCurrentStore(Mage::getModel('core/store')->load(Mage_Core_Model_App::ADMIN_STORE_ID));
before you call
$product->save();

Thursday, May 6, 2010

How to add product skus (or payment method) to the orders grid

[UPDATE] The solution provided here works only for Magento 1.4.0.0 or lower. Starting Magento 1.4.0.1 the order tables are not EAV anymore. I think (but I'm not sure) the logic is still OK but you have to try it by adding a new column to sales_flat_order and sales_flat_order_grid tables instead of adding a new attribute. [/UPDATE] I've tried in different ways to add articles skus (or payment method) to the orders grid in admin. I've tried 'left joining' different tables, performing different actions after applying _prepareCollection() method. All of them worked in some way, but the website started working slow when it reached 10000-12000 orders. And when I say slow I mean over 2 minutes to render the orders grid in admin. This was because of the slow queries which also resulted in slow loading of the frontend pages.
I found an easier solution, it's somehow redundant but it works and it's pretty fast.
I added a new varchar attribute for the order object.
EDIT: This only works in versions < 1.4
INSERT INTO `eav_attribute` (`attribute_id`, `entity_type_id`, `attribute_code`, `attribute_model`, `backend_model`, `backend_type`, `backend_table`, `frontend_model`, `frontend_input`, `frontend_label`, `frontend_class`, `source_model`, `is_required`, `is_user_defined`, `default_value`, `is_unique`, `note`) VALUES
(NULL, 11, 'product_skus', NULL, NULL, 'varchar', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, 0, '');

Clear the cache (contents of the folder var/cache)
Override the Mage_Sales_Model_Order class and in the _beforeSave() method add this:
if (!$this->getProductSkus()) {
$skus = array();
foreach ($this->getAllItems() as $item) {
$skus[] = $item->getSku();
}
$this->setProductSkus(implode(",", $skus));
}
This will add a string with all the skus separated by comma to the order model.
Now all you have to do is to show the string in the orders grid.
Override the Mage_Adminhtml_Block_Sales_Order_Grid block and in the _prepareColumns() method add a new column like this:
$this->addColumn('product_skus', array(
'header' => Mage::helper('sales')->__('Products'),
'index' => 'product_skus',
));
Added it where ever you want.
If you don't know how to overwrite a model or a class this is a good tutorial:
http://magedev.com/2009/06/03/magento-overriding-model-block-or-helper/

The only problem is that these changes will take effect only for the orders you make from now on.

You can add the skus for every order like this.
Create a new php file, let's call it orders.php and place it in the root of the application (same level as index.php) with this code:
<?php
$mageFilename = 'app/Mage.php';

require_once $mageFilename;

Varien_Profiler::enable();

Mage::setIsDeveloperMode(true);

ini_set('display_errors', 1);

umask(0);
Mage::app('default');
Mage::register('isSecureArea', 1);

$orders = Mage::getModel('sales/order')->getCollection()->addAttributeToSelect("*");
foreach ($orders as $order){
$order->save();
}
?>

and call it in the browser (http://yourwebsite.com/orders.php)

If you have many orders it will take some time to run but eventually ... you get my point.

If you want to do this with the payment method the system is the same. Add a new attribute:
INSERT INTO `eav_attribute` (`attribute_id`, `entity_type_id`, `attribute_code`, `attribute_model`, `backend_model`, `backend_type`, `backend_table`, `frontend_model`, `frontend_input`, `frontend_label`, `frontend_class`, `source_model`, `is_required`, `is_user_defined`, `default_value`, `is_unique`, `note`) VALUES
(NULL, 11, 'order_payment_method', NULL, NULL, 'varchar', NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, 0, '');
Clear the cache.
In the _beforeSave() method add this:
if (!$this->getOrderPaymentMethod()) {
$this->setOrderPaymentMethod($this->getPayment()->getMethodInstance()->getTitle()); //if you want the payment method's name (Check / money order)
//$this->setOrderPaymentMethod($this->getPayment()->getMethod());//if you want the payment method's code (checkmo)
}

In the Mage_Adminhtml_Block_Sales_Order_Grid block _prepareColumns() method add this:
$this->addColumn('order_payment_method', array(
'header' => Mage::helper('sales')->__('Payment'),
'index' => 'order_payment_method',
));

Same as for the product skus, this will now work for previous orders, but you can run the same script as above to update all the old orders.

That's it. Let me know how it turns out.

Friday, April 30, 2010

Add new variable to order update e-mail

possible solution:

You need to override the Order model (app/code/core/Mage/Sales/Model/Order.php) and for the sendOrderUpdateEmail() method
you need to change this section
$mailTemplate->setDesignConfig(array('area'=>'frontend', 'store' => $this->getStoreId()))
->sendTransactional(
$template,
Mage::getStoreConfig(self::XML_PATH_UPDATE_EMAIL_IDENTITY, $this->getStoreId()),
$recipient['email'],
$recipient['name'],
array(
'order'     => $this,
'billing'   => $this->getBillingAddress(),
'comment'   => $comment
)
);
to this:
$mailTemplate->setDesignConfig(array('area'=>'frontend', 'store' => $this->getStoreId()))
->sendTransactional(
$template,
Mage::getStoreConfig(self::XML_PATH_UPDATE_EMAIL_IDENTITY, $this->getStoreId()),
$recipient['email'],
$recipient['name'],
array(
'order'     => $this,
'billing'   => $this->getBillingAddress(),
'comment'   => $comment,
'your_var_name'=>'SOMETHNG'
)
);

In the e-mail template you can show the new var like this

{{var your_var_name}}

Thursday, April 29, 2010

How to add a block above the 3 columns in 3 columns layout

Problem: Adding on home page a block above the 3 columns
http://www.magentocommerce.com/boards/viewthread/180617/

Possible solution:
Here is how i did it.
Edit app/design/frontend/{interface}/{theme}/layout/page.xml
In the <default> tag add this
<block type="core/text_list" name="top" as="top" translate="label">
<label>Top Row</label>
</block>
Now go to admin panel: Cms->Pages and edit the homepage.
I've created a new phtml file in which I placed the content I want on top
Let's say it's app/design/frontend/{interface}/{theme}/template/callouts/top_callout.phtml
In the design tab for 'Layout Update XML' I put this:

<reference name="top">
<block type="core/template" name="top.callout" template="callouts/top_callout.phtml" />
</reference>
You can put every block you want here. I just used this as an example. Just put it inside
<reference name="top">
....
</reference>
Now edit app/design/frontend/{interface}/{theme}/template/page/3columns.phtml
and add this line
<?php echo $this->getChildHtml('top') ?>
right under
<?php echo $this->getChildHtml('header') ?>
Save all files you edited and clear the contents of 'var/cache/'

I attached an image to see how it looks like.



View full size image

Thursday, April 22, 2010

How to change invoice status in Admin panel

Problem: When creating Invoice directly from Order in admin the invoice automatically gets marked as Paid. how can it be created with the status 'Pending' and marked as paid later. http://www.magentocommerce.com/boards/viewthread/54391/

Possible solution:
In order to create a 'pending' invoice, the payment method you use for the order must allow you to.
For that you need to add in the payment method's model this member:
protected $_canCapture = true;
You can add it right under
protected $_code  = 'PAYMENT CODE HERE';
For example if you want to do this for Check/money order you need to add the line above in app/code/core/Mage/Payment/Model/Method/Checkmo.php

After this when you create an invoice you will have an option near the save button to set the invoice status. (see picture attached). To create a pending invoice select 'Not capture'.
If you want to mark it later as paid edit the invoice and between the buttons on to one called 'Capture' (see picture). Well you get the idea....