i'd like that when my product quantity is equal to 1, the product is out of stock ( and not when quantity is equal to 0 ).
Is it possible ?
How ?
In PrestaShop 1.6 (tested and confirmed working in v1.6.0.14) you can accomplish this by the following method, where the website will always show an available stock quantity that is the real quantity minue one. If you have 6 in stock this modification will change your website to report the stock as 5 to your customers, when you have only 1 stock available, customers will see the product with 0 quantity and marked as out of stock.
Copy the file /classes/stock/StockAvailable.php to /override/classes/stock/StockAvailable.php.
Edit the file /override/classes/stock/StockAvailable.php as follows.
At lines 357-380 is the function AvailableStock::getQuantityAvailableByProduct() that will normally read like this (formatting may vary slightly):
public static function getQuantityAvailableByProduct( $id_product,
$id_product_attribute = null, $id_shop = null ) {
if( $id_product_attribute === null ) $id_product_attribute = 0;
$skey = 'StockAvailable::getQuantityAvailableByProduct_' . (int)$id_product . '-' .
(int)$id_product_attribute . '-' . (int)$id_shop;
if( !Cache::isStored( $key )) {
$query = new DbQuery();
$query->select( 'SUM(quantity)' );
$query->from( 'stock_available' );
if( $id_product !== null ) $query->where( 'id_product = ' . (int)$id_product );
$query->where( 'id_product_attribute = ' . (int)$id_product_attribute );
$query = StockAvailable::addSqlShopRestriction( $query, $id_shop );
Cache::store( $key, (int)Db::getInstance(_PS_USE_SQL_SLAVE_)->getValue( $query ));
}
return Cache::retrieve( $key );
}
Replace the last line of the function beginning with the keyword return to the following:
$iStockQty = Cache::retrieve( $key );
if( $iStockQty > 0 ) $iStockQty--;
return $iStockQty;
Delete the file /cache/class_index.php so that Prestashop automatically re-creates this file taking into account the new override file.
I also found the method self::removeProductFromStockAvailable($id_product) in StockAvailable.php to do some similar stuff.
My default controller function
public function actionAddNewCategories() {
$model = new AddNewCategory();
$model->category_name=strip_tags(trim($_POST['categoryname']));
$model->category_description=strip_tags(trim($_POST['categorydescription']));
$model->save(false);
$category_list=Invoice::getCategoryName();
$test="";
$test = ' <option value="">Select Category</option>';
foreach($category_list as $value ){
$test .= "<option >{$value['category_name']}</option>";
}
echo $test;
}
Model function
public function getCategoryName() {
$id = Yii::app()->db->createCommand()
->select('category_name')
->from('add_new_category c')
->queryAll();
return $id;
}
You can add unique rule into your AddNewCategory model like below:
array('fieldName','unique','className'=>__CLASS__,'attributeName'=>'columnName','allowEmpty'=>FALSE)
By now, You have a rule, which denies to insert new record with existing value.
Another alternative way would be using exist() like below:
$exist=AddNewCategory::model()->exist(array('columnName'=>'VALUE'));
Which $exist variable holds a Boolean value which means whether a record exists with entered condition or not.
http://www.yiiframework.com/doc/api/1.1/CUniqueValidator
http://www.yiiframework.com/doc/api/1.1/CActiveRecord#exists-detail
have an issue updating magento product from frontend using a module that its function is for customers to create their own products and have the admin approve before enabled(this part is working).
the problem is when a customer tries to updated their admin approved product (as before approval, product states that newly created product is pending, but they can still update the data/attributes created during the product create function, the same attributes that are not updating using the controller)
first of all i have a controller with the action to update the approved/pending customer product
public function editPostAction() {
$id = $this->getRequest()->getParam('productid');
if ( $id !== false ) {
list($data, $errors) = $this->validatePost();
if ( !empty($errors) ) {
foreach ($errors as $message) {
$this->_getSession()->addError($message);
}
$this->_redirect('customer/products/edit/', array(
'id' => $id
));
} else {
$customerId = $this->_getSession()->getCustomer()->getid();
$product = Mage::getResourceModel('customerpartner/customerpartner_product_collection')
->addAttributeToSelect('*')
->addAttributeToFilter('customer_id', $customerId)
->addAttributeToFilter('entity_id', $id)
->load()
->getFirstItem();
$product->setName($this->getRequest()->getParam('name'));
$product->setSku($this->getRequest()->getParam('sku'));
$product->setDescription($this->getRequest()->getParam('description'));
$product->setShortDescription($this->getRequest()->getParam('short_description'));
$product->setPrice($this->getRequest()->getParam('price'));
$product->setWeight($this->getRequest()->getParam('weight'));
$product->setStock($this->getRequest()->getParam('stock'));
$product->save();
if ( isset($_FILES) && count($_FILES) > 0 ) {
foreach($_FILES as $image ) {
if ( $image['tmp_name'] != '' ) {
if ( ( $error = $this->uploadImage($image, $id) ) !== true ) {
$errors[] = $error;
}
}
}
}
if ( empty($errors) ) {
$this->_getSession()->addSuccess($this->__('Your product was successfully updated'));
} else {
$this->_getSession()->addError('Product info was saved but was imposible to save the image');
foreach ($errors as $message) {
$this->_getSession()->addError($message);
}
}
$this->_redirect('customer/products/');
}
}
}
as well as a form that on submit is supposed to update the product attributes and images but the page reloads on submit and shows successful saved message but the attributes are not updated and going back to the edit form (for each product created) for that product the values in the update form have the values of the update we just submitted, bet yet the products attributes are not updated in the catalog either (they remain the same values as entered in the create new process)
don't no if to continue to figure out what is going wrong or just move to either use api or direct sql to get the job done.
see this post Magento 1.7: Non-system product attribute not saving in PHP script the problem maybe different but the solution can be found in that post
updated to a new action to call in .phtml see below as it seems to be updating the product data as needed, still wanting to improve..
called in form using /frontendname/editApprovedPost/
public function editApprovedPostAction() {
$id = $this->getRequest()->getParam('productid');
$idproduct = $this->getRequest()->getParam('product_id');
if ( $id !== false ) {
list($data, $errors) = $this->validatePost();
if ( !empty($errors) ) {
foreach ($errors as $message) {
$this->_getSession()->addError($message);
}
$this->_redirect('customer/products/edit/', array(
'id' => $id
));
} else {
- now added more php code to action (in this order) after the } else {...
require_once 'app/Mage.php';
then add admin store for frontend product updates...
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
then get the customer id...
$customerId = Mage::getSingleton('customer/session')->getCustomerId();
then use the forms product Id to get the Product Id to update...
$product = Mage::getModel('catalog/product')->load("".$idproduct."");
then use setName() to update/save the attribute value grabbed from the forms input value...
$product->setName($this->getRequest()->getParam('name'));//use whatever attributes need (only text and text area tested so far)
then save/update product data with...
$product->save();
then add to run through errors...
if ( empty($errors) ) {
$this->_getSession()->addSuccess($this->__('Your product was successfully updated'));
} else {
$this->_getSession()->addError('Product info was saved but was imposible to save the image');
foreach ($errors as $message) {
$this->_getSession()->addError($message);
}
}
$this->_redirect('customer/products/');
}
}
}
then with a form to submit in frontend with customer logged in and customer group config
custom form only visible to Customer Group 2 (default is Wholesale)
form below....
sorry cant paste form to much work to paste the code here, any way using the
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
which i have read in some posts is that to update product in frontend you need to be "admin", which this seems to do just fine. As Noted before, the script was not updating and it was because it is trying to save to a different models data when the data to be updated was an actual product (that has been approved and created using the different models data) and it was updating using
$product = Mage::getResourceModel('customerpartner/customerpartner_product_collection')
would be good to here anyone else's comments
hope this helps someone because was think time to close this build.
Is there a simple way of adding custom fields to a model? Say I have a table "user" with 3 fields: id, name and surname. I want this:
$user = User::model()->findByPk(1);
$echo $user->fullName; // echoes name and surname
Please note: I want this custom field to be added via sql, smth like
$c = new CDbCriteria();
$c->select = 'CONCAT("user".name, "user".surname) as fullName';
$user = User::model()->find($c);
Problem is that fullName property is not set.
UPD:
here is the code for a little bit trickier problem -- custom field from another table. This is how it's done:
$model = Application::model();
$model->getMetaData()->columns = array_merge($model->getMetaData()->columns, array('fullName' => 'CONCAT("u".name, "u".surname)'));
$c = new CDbCriteria();
$c->select = 'CONCAT("u".name, "u".surname) as fullName';
$c->join = ' left join "user" "u" on "t".responsible_manager_id = "u".id';
$model->getDbCriteria()->mergeWith($c);
foreach ($model->findAll() as $o) {
echo '<pre>';
print_r($o->fullName);
echo '</pre>';
}
You can add a function to the User class:
public function getFullName() { return $this->name.' '.$this->surname; }
This will return the full name as if it were an attribute from the database. This is much easier than adding a calculated column to the SQL.
In model
public function getMetaData(){
$data = parent::getMetaData();
$data->columns['fullName'] = array('name' => 'fullName');
return $data;
}
Thus not recommended
I've got a client database with a large range of stock items, which are being uploaded to Magento as simple products.
Now I need to group them up and assign them to configurable products with their size and colour being their configurable attributes.
The Magento API has a Product_Link class, with a promising looking method: catalogue-product-link.assign (link), but I can't for the life of me figure out what arguments I need to make it work with configurable products, providing this is how assign was meant to be used.
Well the notes here helped me get this running. So I thought I'd share with you the code to add a simple product to an existing Configurable Product.
This code assumes the simple product is a valid one to add, I'm not sure what would happen if it wasn't.
private function _attachProductToConfigurable( $_childProduct, $_configurableProduct ) {
$loader = Mage::getResourceModel( 'catalog/product_type_configurable' )->load( $_configurableProduct );
$ids = $_configurableProduct->getTypeInstance()->getUsedProductIds();
$newids = array();
foreach ( $ids as $id ) {
$newids[$id] = 1;
}
$newids[$_childProduct->getId()] = 1;
$loader->saveProducts( $_configurableProduct->getId(), array_keys( $newids ) );
}
The code from the accepted answer by Scimon does not work anymore in recent versions of magento (at least in 1.7). But fortunately, you need just a small fix to get it working again:
private function _attachProductToConfigurable( $_childProduct, $_configurableProduct ) {
$loader = Mage::getResourceModel( 'catalog/product_type_configurable' )->load( $_configurableProduct, $_configurableProduct->getId() );
$ids = $_configurableProduct->getTypeInstance()->getUsedProductIds();
$newids = array();
foreach ( $ids as $id ) {
$newids[$id] = 1;
}
$newids[$_childProduct->getId()] = 1;
//$loader->saveProducts( $_configurableProduct->getid(), array_keys( $newids ) );
$loader->saveProducts( $_configurableProduct, array_keys( $newids ) );
}
I'm working on doing this right now.
So far I've found these items helpful as references:
http://snippi.net/magento-programmatically-add-configurable-product-color-api
http://www.omnisubsole.com/blog/2009/07/01/configurable-products-in-magento.html
http://www.magentocommerce.com/boards/viewthread/6941/P30/
I'll post my code so far, and hopefully update it once it works..
// Set 'item_size' as the super attribute # choose your own attribute!
// this is the 'choose-able' field that differenciates products
$super_attributes=array( Mage::getModel('eav/entity_attribute')
->loadByCode('catalog_product','item_size')
->getData('attribute_id')
);
$product_collection=Mage::getModel('catalog/product')->getCollection();
// Fetch configurable orders
$product_collection->addFieldToFilter('type_id',Array('eq'=>"configurable"));
#$product_collection->addFieldToFilter('sku',Array('eq'=>"ASMCL000002"));
$product_collection->addAttributeToSelect('*');
$count=0;
foreach($product_collection as $product) {
$sku = $product->getSku();
echo "SKU: $sku\n";
$simple_children_collection = Mage::getModel('catalog/product')->getCollection();
$simple_children_collection->addAttributeToSelect('*');
$simple_children_collection->addFieldToFilter('sku',Array('like'=>$sku . "-%"));
echo "children: ";
foreach($simple_children_collection as $child) {
$child_sku = $child->getSku();
echo "$child_sku ";
#visiblity should be 'nowhere'
}
echo "\n";
if (!$product->getTypeInstance()->getUsedProductAttributeIds()) {
# This is a new product without the Configurable Attribue Ids set
$product->getTypeInstance()
->setUsedProductAttributeIds( $super_attributes );
//$product->setConfigurableAttributesData(array($_attributeData));
$product->setCanSaveConfigurableAttributes(true); # Not sure if this is needed.
$product->setConfigurableProductsData(''); # Use this to add child products.
}
$count++;
try {
$product->save();
$productId = $product->getId();
echo $product->getId() . ", $sku updated\n";
}
catch (Exception $e){
echo "$sku not added\n";
echo "exception:$e";
}
}
echo "\nCount is $count\n";
Okay, this uses 'item_size' as the attribute that differentiates the "simple" products. Also, this assumes that the "configurable" parent SKU is the root of the child SKU. For example, ABC001 is the parent while ABC001-SMALL and ABC001-LARGE are the simple children.
Hope that helps someone.
I this is an un-educated guess, but I think what your asking for can't be done with the existing API. You will have to write your own or just got directly to the DB.
Here is the hack-y way that I did this straight with PHP. There are three related tables. I was using color and size as my attributes.
My parent products (configurable) don't actually exist in my catalog. They are essentially model level and then the products are the SKU level.
So LIKE 'parentproductsku%' works out for the children.
$query1 = "SELECT * FROM mage_catalog_product_entity WHERE type_id= 'configurable'";
//Find the parent id
$statusMessage = "Ok, found a product with a confgurable attribute";
$result1 = $this->runQuery($query1, "query1", $statusMessage);
while ($row1 = mysql_fetch_assoc($result1)) { //entering the first loop where products are configurable
$this->parentId = $row1['entity_id'];
$this->parentSku = $row1['sku'];
echo "The SKU was $this->parentSku" . "<br />";
//insert these into the link table for association
$query2 = "SELECT * FROM mage_catalog_product_entity WHERE type_id= 'simple' AND sku LIKE '" . $this->parentSku . "%';";
// find the child ids that belong to the parent
$statusMessage = "Found some children for $this->parentSku";
$result2 = $this->runQuery($query2, "query2", $statusMessage);
while ($row2 = mysql_fetch_assoc($result2)) {//entering the second loop where SKU is like model sku
$this->childId = $row2['entity_id'];
$this->childSku = $row2['sku'];
echo "Now we're working with a child SKU $this->childSku" . "<br />";
//"REPLACE INTO catalog_product_super_attribute SET product_id='".$product->entity_id."', attribute_id='".$attribute->attribute_id."', position='".$position."'";
$query3 = "REPLACE INTO mage_catalog_product_super_attribute (product_id, attribute_id, position) VALUES ('" . $this->childId . "', '76', '0');";
$message3 = "Inserted attribute for color for ID $this->childId SKU $this->childSku";
$result3 = $this->runQuery($query3, "query3", $message3);
$query4 = "REPLACE INTO mage_catalog_product_super_attribute_label (product_super_attribute_id, store_id, use_default, value) VALUES (LAST_REPLACE_ID(), '0', '0', 'Color');";
$message4 = "Inserted attribute for Color SKU $this->childSku ID was $this->db->insert_id";
$result4 = $this->runQuery($query4, "query4", $message4);
$query5 = "REPLACE INTO mage_catalog_product_super_attribute (product_id, attribute_id, position) VALUES ('" . $this->childId . "', '529', '0');";
$message5 = "Inserted attribute for Product Size SKU $this->childSku";
$result5= $this->runQuery($query5, "query5", $message5);
$query6 = "REPLACE INTO mage_catalog_product_super_attribute_label (product_super_attribute_id, store_id, use_default, value) VALUES (LAST_REPLACE_ID(), '0', '0', 'Size');";
$message6 = "Inserted attribute for Size SKU $this->childSku ID was $this->db->insert_id";
$result6 = $this->runQuery($query6, "query6", $message6);
$query7 = "REPLACE INTO mage_catalog_product_super_link (product_id, parent_id) VALUES ('" . $this->childId . "', '" . $this->parentId . "');";
$message7 = "Inserted $this->childId and $this->parentId into the link table";
$result7 = $this->runQuery($query7, "query7", $message7);
$query8 = "REPLACE INTO mage_catalog_product_relation (parent_id, child_id) VALUES ('" . $this->parentId . "', '" . $this->childId . "');";
$message8 = "Inserted $this->childId and $this->parentId into the link table";
$result8 = $this->runQuery($query8, "query8", $message8);
} //end while row 2 the child ID
} //end while row 1 the parent id
Surprisingly, this works, if all your simple products share the same price:
$childProducts = $configurable->getTypeInstance(true)->getUsedProductIds($configurable);
// Don't add this product if it's already there
if(!in_array($child->getId(), $childProducts)) {
$childProducts[] = $child->getId();
}
$existingIds = $configurable->getTypeInstance(true)->getUsedProductAttributeIds($configurable);
$newAttributes = array();
foreach($configurable->getTypeInstance(true)->getSetAttributes($configurable) as $attribute) {
if(!in_array($attribute->getId(), $existingIds) && $configurable->getTypeInstance(true)->canUseAttribute($attribute)
&& $child->getAttributeText($attribute->getAttributeCode())) {
// Init configurable attribute
$configurableAtt = Mage::getModel('catalog/product_type_configurable_attribute')
->setProductAttribute($attribute);
// Add new attribute to array
$newAttributes[] = array(
'id' => $configurableAtt->getId(),
'label' => $configurableAtt->getLabel(),
'position' => $attribute->getPosition(),
'values' => $configurableAtt->getPrices() ? $configurable->getPrices() : array(),
'attribute_id' => $attribute->getId(),
'attribute_code' => $attribute->getAttributeCode(),
'frontend_label' => $attribute->getFrontend()->getLabel(),
);
}
}
if(!empty($newAttributes)) {
$configurable->setCanSaveConfigurableAttributes(true);
$configurable->setConfigurableAttributesData($newAttributes);
}
$configurable->setConfigurableProductsData(array_flip($childProducts));
$configurable->save();
#aeno's solution did not work for me, so I refined it a bit. This has been tested using a product instantiated via the Mage::getModel( 'catalog/product' )->load() method.
private function _attachProductToConfigurable( $childProduct, $configurableProduct )
{
$childIds = $configurableProduct->getTypeInstance()->getUsedProductIds();
$childIds[] = $childProduct->getId();
$childIds = array_unique( $childIds );
Mage::getResourceModel( 'catalog/product_type_configurable' )
->saveProducts( $configurableProduct, $childIds );
}