I'm working on a Prestashop 1.5.x website, where I need to add a custom price-calculation rule for a specific product.
My goal is to add 10 dollars per order, but PS adds extra cost by product quantity, so if you order 20 products it ask you 200$ instead of 10...
I need to override the calculating process in /classes/Product.php, with something like:
if (product_id = 44) { $price = $price + 10; }
else { $price = $price }
Have you got any idea?
You have to create an override of the Product class in prestashop. To do so, create a new file in override/classes called Product.php and put this code in it :
<?php
class Product extends ProductCore
{
// Here we will put every method or property override
}
In this class, you will copy / paste the static method priceCalculation (it's at line 2567 of original Product.php file for me). When done, just add these lines at the end of the method, just before the self::$_prices[$cache_id] = $price; :
if ($id_product == 44 && Context::getContext()->customer->isLogged()) {
$customer = Context::getContext()->customer;
$nbTimesBoughtThisProduct = (int) Db::getInstance()->getValue('
SELECT COUNT(*)
FROM `' . _DB_PREFIX_ . 'product` p
JOIN `' . _DB_PREFIX_ . 'order_detail` od
ON p.`id_product` = od.`product_id`
JOIN `' . _DB_PREFIX_ . 'orders` o
ON od.`id_order` = o.`id_order`
WHERE o.`id_customer` = ' . $customer->id . '
AND p.`id_product` = ' . $id_product . '
');
$price += $nbTimesBoughtThisProduct * 10;
}
I did not have the time to test these, but I think that's the way to do what you want to do.
priceCalculation is the method called each time Prestashop needs a product's price. By putting this code at the end of this method, we modify the returned price.
The code first checks if the customer is logged (we can't get orders from him if he is not). If so, a query retrieves the amount of times this customer bought this product in the past. This number is multiplied by ten, and the value is added to the price.
EDIT: If, as Cyril Tourist said, you want to also count the current cart, get this new code (still not tested, but should work) :
if ($id_product == 44 && Context::getContext()->customer->isLogged()) {
$customer = Context::getContext()->customer;
$nbTimesBoughtThisProduct = (int) Db::getInstance()->getValue('
SELECT COUNT(*)
FROM `' . _DB_PREFIX_ . 'product` p
JOIN `' . _DB_PREFIX_ . 'order_detail` od
ON p.`id_product` = od.`product_id`
JOIN `' . _DB_PREFIX_ . 'orders` o
ON od.`id_order` = o.`id_order`
WHERE o.`id_customer` = ' . $customer->id . '
AND p.`id_product` = ' . $id_product . '
');
$productsInCart = Context::getContext()->cart->getProducts();
foreach ($productsInCart as $productInCart) {
if ($productInCart['id_product'] == 44) {
$nbTimesBoughtThisProduct++;
}
}
$price += $nbTimesBoughtThisProduct * 10;
}
Also, I advice you to store the "44" product ID in a constant, configuration variable, or anything, but not keeping it in the code like that. I did that just for the example.
Related
The issue is Output on related products it shows on every related product box same total counted number in OpenCart. So, it see just main product_id. So, must indicate related product_id somehow. I tried many many ways but it don`t work for me. Can someone help me to fix this?
There is function on modules:
public function getUnitsSold($product_id) {
$query = $this->db->query("SELECT SUM(op.quantity) AS total FROM `" . DB_PREFIX . "order_product` op LEFT JOIN `" . DB_PREFIX . "order` o ON (op.order_id = o.order_id) WHERE o.order_status_id = '5' AND op.product_id = '" . (int)$product_id . "'");
if ($query->row) {
return $query->row['total'];
} else {
return FALSE;
}
}
And this is simply template output.
<?php if ($tproducts) { ?>
<?php foreach ($tproducts as $product) { ?>
<?php if ($product['units_sold']) { ?>
<?php echo $text_units_sold; ?> <?php echo $product['units_sold']; ?>
<?php } ?>
<?php } ?>
<?php } ?>
In controller where is related product array is a possibility to indicate maybe this function query and then make right output.
something like that units_sold?
$data['tproducts'][] = array(
'product_id' => $result['product_id'],
'units_sold' => $this->db->query("SELECT SUM(op.quantity) AS total FROM `" . DB_PREFIX . "order_product` op LEFT JOIN `" . DB_PREFIX . "order` o ON (op.order_id = o.order_id) WHERE o.order_status_id = '5' AND op.product_id = '" . (int)$product_id . "'"),
);
or maybe
$data['tproducts'][] = array(
'product_id' => $result['product_id'],
'units_sold' => $this->model_catalog_product->getUnitsSold($this->request->get['product_id']),
);
You'll want to store the amount of sold items on the item itself.
Increment the number as if it is a reverse stock.
So, I'd just ALTER TABLE oc_product ADD amount_sold INT(8) NULL DEFAULT NULL;
Then, just display that simple column value. The storage of the orders must be modified and becomes a little bit slower (in the order of .001 - .0001s).
This way the store will keep performing well in the future, even when your order item table has become huge.
Let the storage handler be the working horse. Take a look at ModelCheckoutOrder::addOrderHistory(), when they subtract the quantity, you must add your quantity :)
"UPDATE " . DB_PREFIX . "product SET /*.... opencart default shit */, amount_sold = (amount_sold + " . (int)$order_product['quantity'] . ")
I am trying to do one change in opencart source code (1.5.6 version), but unfortunately without success.
We have 5 languages on website with descriptions.
I want to show english description for the products on all version of languages on web site (on the page: ?route=product/product&product_id=X)
All other content will be on selected language but only description need be on english (no matter what language is selected).
I am searching for solution, and only logical for me is that this changes in code need to be make on product.php file under catalog/controller/product dir.
But can anybody give to me advice where to change the line of code where open cart select language id? In database english is language_id = 1
Maybe I can do this
remove this line on product.tpl
<div id="tab-description" class="tab-content"><?php echo $description; ?></div>
and put something like this, but how select description in this case
<div id="tab-description" class="tab-content"><?php SELECT DESCRIPTION FROM DATABASE WHERE LANGUAGE_ID =1 ?></div>
Mainly you need to modify your Model, Controller and View files:
Open you product model file from here : catalog/model/catalog/product.php
Add following code before line no 64 or public function getProducts($data = array()) {
public function getProductEngDesc($product_id) {
$query = $this->db->query("SELECT DISTINCT * FROM " . DB_PREFIX . "product p LEFT JOIN " . DB_PREFIX . "product_description pd ON (p.product_id = pd.product_id) LEFT JOIN " . DB_PREFIX . "product_to_store p2s ON (p.product_id = p2s.product_id) WHERE p.product_id = '" . (int)$product_id . "' AND pd.language_id = '1' AND p.status = '1' AND p.date_available <= NOW() AND p2s.store_id = '" . (int)$this->config->get('config_store_id') . "'");
if ($query->num_rows) {
return array(
'description' => $query->row['description'],
);
} else {
return false;
}
}
Open you product Controller file from here : catalog/controller/product/product.php
Add following line after $product_info = $this->model_catalog_product->getProduct($product_id);
$product_eng_desc = $this->model_catalog_product->getProductEngDesc($product_id);
Add following line after $this->data['description'] = html_entity_decode($product_info['description'], ENT_QUOTES, 'UTF-8');
$this->data['eng_description'] = html_entity_decode($product_eng_desc['description'], ENT_QUOTES, 'UTF-8');
Now open your Product template file and replace below line
<div id="tab-description" class="tab-content"><?php echo $description; ?></div>
With
<div id="tab-description" class="tab-content"><?php echo $eng_description; ?></div>
Things get lost or broken, so basically, what I want to do is to compare actual inventory database against zencart store stock and update zencart stock if it is greater than the real inventory database.
I have a table 'products' in my first database (zencart) and second database (inventory), like this:
products_id products_model products_quantity
zencart.products :
products_id : 3 | products_model : J293-04 | products_quantity : 4 |
[...]
inventory.products :
products_id : 15 | products_model : J293-04 | products_quantity : 1 |
[...]
The inventory.products database is the actual count of the products available.
When the zencart stock query function tests if goods are available,
I want it to compare stock quantity with the inventory database.
If the zencart stock quantity is > than inventory stock, then
update zencart stock quantity to = inventory stock.
The zencart stock functions use zencart.products.product_id
so the zencart.products.products_model has to be used to compare the 2 databases.
This is what I have come up replace the zencart stock count function. It seems to be overly complex, and I am not sure if a inventory global $db2; needs to be put in.
function zen_get_products_stock($products_id) {
global $db;
$products_id = zen_get_prid($products_id);
$stock_query = "select products_quantity
from " . TABLE_PRODUCTS . "
where products_id = '" . (int)$products_id . "'";
$stock_model_query = "select products_model
from " . TABLE_PRODUCTS . "
where products_id = '" . (int)$products_id . "'";
$inv_stock_query = "select products_quantity
from inventory.products.products_model
where zencart.products.products_model =
inventory.products.products_model
if $stock_query > $inv_stock_query
{
$stock_query = $inv_stock_query
UPDATE
inventory.products,
zencart.products
SET
zencart.products.products_quantity = inventory.products.products_quantity
WHERE
zencart.products.products_model = inventory.products.model
}
$stock_values = $db->Execute($stock_query);
return $stock_values->fields['products_quantity'];
}
Original code from zencart/includes/functions/functions_lookups.com
/**
* Return a product's stock count.
*
* #param int The product id of the product who's stock we want
*/
function zen_get_products_stock($products_id) {
global $db;
$products_id = zen_get_prid($products_id);
$stock_query = "select products_quantity
from " . TABLE_PRODUCTS . "
where products_id = '" . (int)$products_id . "'";
$stock_values = $db->Execute($stock_query);
return $stock_values->fields['products_quantity'];
}
/**
* Check if the required stock is available.
*
* If insufficent stock is available return an out of stock message
*
* #param int The product id of the product whos's stock is to be checked
* #param int Is this amount of stock available
*
* #TODO naughty html in a function
*/
function zen_check_stock($products_id, $products_quantity) {
$stock_left = zen_get_products_stock($products_id) - $products_quantity;
$out_of_stock = '';
if ($stock_left < 0) {
$out_of_stock = '<span class="markProductOutOfStock">' . STOCK_MARK_PRODUCT_OUT_OF_STOCK . '</span>';
}
return $out_of_stock;
}
Thank you for your help.
I'm looking for something more advanced than viewtopic.php?t=7080
I need to hide (or disable) products that are out of stock AND are in a specific category.
This problem arose because I have a category called "Clearance" and I want clearance items to automatically disable after selling out, as we'll never get more in stock. At the same time I want products in all other categories to continue to display after being out of stock.
Please help.
Mike
I would do this inside the confirm method of the order model. This is where quantities for products are updated once a sale is confirmed.
Open:
catalog/model/checkout/order.php
Within the confirm() method you'll find a line like:
foreach ($order_product_query->rows as $order_product) {
You could create a new method that would return the existing quantity for the given product, subtract the sold amount and check to see if the new quantity is 0, plus it would check if the product is attached to your given category.
If so, then set the product status to disabled if you don't want it to show at all, or set the stock_status to Out of Stock if you just want to show that it's sold out.
// check quantity and categories
private function checkQuantity ($product_id) {
$return = array();
// first check if product is attached to your specified category and add boolean to array
$categories = $this->db->query ("SELECT category_id FROM " . DB_PREFIX . "product_to_category WHERE product_id = '" . (int)$product_id . "'");
if (in_array($your_clearance_category_id, $categories->rows)):
$return['check'] = true;
else:
$return['check'] = false;
endif;
// get your pre-sale quantity and add to array
$quantity = $this->db->query ("SELECT quantity FROM " . DB_PREFIX . "product WHERE product_id = '" . (int)$product_id . "'");
$return['quantity'] = $quantity->row['quantity'];
return $return;
}
Then add this just after the opening of your foreach ($order_product_query->rows as $order_product) { structure:
$checks = $this->checkQuantity($order_product['product_id']);
if ($checks['check']):
if (((int)$check['quantity'] - (int)$order_product['quantity']) <= 0):
$this->db->query ("UPDATE " . DB_PREFIX . "product SET status = '0' WHERE product_id = '" . (int)$order_product['product_id'] . "'");
endif;
endif;
Haven't tested but it should work with or without a couple tweaks.
I am trying to find out how I could manually add the product model to each title in the category product listing in zencart, rather than having the model number appear in its own column.
It appears that any instance of using:
$listing->fields['products_model']
in the "product_listing.php" file will only work when the parameters in the admin are sent to true. This is fine, however I then get two instances of the product model. One with the variable in it's own column (which I don't want), and one wherever else I put the variable.
Here is the section I am referring to, you will notice that a case is set up for the model to have its own column, however I want to place it before the title instead under the product list name case and eliminate the column.
for ($col=0, $n=sizeof($column_list); $col<$n; $col++) {
$lc_align = '';
switch ($column_list[$col]) {
case 'PRODUCT_LIST_MODEL':
$lc_align = '';
$lc_text = $listing->fields['products_model'];
break;
case 'PRODUCT_LIST_NAME':
$lc_align = '';
$lc_text = '<h3 class="itemTitle">' . $listing->fields['products_name'] . '</h3>
Is there a way I can reference the model number and place in the title for each row while bypassing the parameter set in the administration?
Any help would be greatly appreciated.
I would just append the product model number ($listing->fields['products_model']) to the product title:
case 'PRODUCT_LIST_NAME':
$lc_align = '';
$lc_text = '<h3 class="itemTitle">' . $listing->fields['products_name'] . '[' . $listing->fields['products_model'] . ']' . '</h3><div class="listingDescription">' . zen_trunc_string(zen_clean_html(stripslashes(zen_get_products_description($listing->fields['products_id'], $_SESSION['languages_id']))), PRODUCT_LIST_DESCRIPTION) . '</div>';
break;
Remember to then turn off the Model Number column in the Zen Cart Admin.
I haven't tested this in Zen Cart - hope it helps.
After some time, I have figured out the answer to my own question. It was actually much easier than I thought. Hopefully, this will help others who may have wanted to do the same thing.
Go to "includes/index_filters/default_filter.php:
From what I can tell, this is where all the queries are made for the "Product Listing" module to display lists for both a specific category or manufacturer. By default, the current queries do not include "p.products_model". Simply add the column identifier in each query (4 total locations: category, category:all, manufacturer, manufacturer:all).
Example:
// We are asked to show only specific category
$listing_sql = "select " . $select_column_list . " p.products_id, p.products_model, p.products_type, p.master_categories_id, p.manufacturers_id, p.products_price, p.products_tax_class_id, pd.products_description, IF(s.status = 1, s.specials_new_products_price, NULL) as specials_new_products_price, IF(s.status = 1, s.specials_new_products_price, p.products_price) as final_price, p.products_sort_order, p.product_is_call, p.product_is_always_free_shipping, p.products_qty_box_status
from " . TABLE_PRODUCTS . " p left join " . TABLE_SPECIALS . " s on p.products_id = s.products_id, " .
TABLE_PRODUCTS_DESCRIPTION . " pd, " .
TABLE_MANUFACTURERS . " m, " .
TABLE_PRODUCTS_TO_CATEGORIES . " p2c
where p.products_status = 1
and p.manufacturers_id = m.manufacturers_id
and m.manufacturers_id = '" . (int)$_GET['filter_id'] . "'
and p.products_id = p2c.products_id
and pd.products_id = p2c.products_id
and pd.language_id = '" . (int)$_SESSION['languages_id'] . "'
and p2c.categories_id = '" . (int)$current_category_id . "'" .
$alpha_sort;
}
From there, you will be able to call the product model code "$listing->fields['products_model']" anywhere you would like, without the parameter in the admin back-end interrupting it from showing. In my case, it I included it directly under the product name in the "product_listing.php" file in modules.
Example:
$lc_text = '<h3 class="itemTitle">' . $listing->fields['products_name'] . '</h3><div id=\"listing_model\">Item Code: ' .$listing->fields['products_model'] . '</div>
Hope this helps someone. Why they made you originally have a whole column for the product model is beyond me, as both "Listing New" and "Listing All" both show it in the same column as the title and description.