Prestashop 1.7 check available stock at final order step - prestashop

I had something working on Prestashop 1.6 to check cart quantities before the customer can buy. Here is my problem on Prestashop 1.7 :
If a customer put an item in his cart today, he comes back in 2 days and he is still logged in. The cart is still available while the product, in reality, became out of stock.
The customer can make the order and the quantity in my stock is -1. Since I upgraded to prestashop 1.7 it's a disaster, I have quantities at -5, -10...because if this non-checked scenario.
abstract class PaymentModule extends PaymentModuleCore
{
public function validateOrder($id_cart, $id_order_state, $amount_paid, $payment_method = 'Unknown',
$message = null, $extra_vars = array(), $currency_special = null, $dont_touch_amount = false,
$secure_key = false, Shop $shop = null)
{
if (!isset($this->context))
$this->context = Context::getContext();
$this->context->cart = new Cart($id_cart);
if (!$this->context->cart->checkQuantities()){
Tools::redirect(__PS_BASE_URI__.'order.php?step=0');
}
return parent::validateOrder($id_cart, $id_order_state, $amount_paid, $payment_method, $message,
$extra_vars, $currency_special, $dont_touch_amount, $secure_key, $shop);
}
}

Actually the best solution is to use this addons : https://addons.prestashop.com/en/stock-supplier-management/21707-temporary-product-reservation-lonely-stock.html
Prestashop handles really bad cart stocks.
Anyway if you want to do it yourself and check stock available it's pretty simple :
<?php
$cart = $this->context->cart;
$cart_products = $cart->getProducts();
if (!empty($cart_products)) {
$db = Db::getInstance();
foreach ($cart_products as $key => $cart_product) {
$real_quantity = StockAvailable::getQuantityAvailableByProduct($cart_product['id_product'], $cart_product['id_product_attribute']);
if ( (int) $real_quantity < (int) $cart_product['quantity'] ) {
// If negative
$real_quantity = (int) $real_quantity < 0 ? 0 : $real_quantity;
$sql = '
UPDATE `'._DB_PREFIX_.'cart_product`
SET quantity = '.(int) $real_quantity.',`date_add` = NOW()
WHERE `id_product` = '.(int) $cart_product['id_product'].
(!empty($cart_product['id_product_attribute']) ? ' AND `id_product_attribute` = '.(int) $cart_product['id_product_attribute'] : '').'
AND `id_cart` = '.(int) $cart->id;
$db->execute($sql);
}
}
// Garbage collector
$db->execute('DELETE FROM '._DB_PREFIX_.'cart_product WHERE quantity < 1 ');
}

Related

payment module in prestashop 1.6.1.x doesn't send total price with shipment

In my Prestashop store, I have a payment module that sends the total amount to an external API:
/** CART ITEMS */
$products = $cart->getProducts();
$CartItems = [];
$api_currency_id = (int)Currency::getIdByIsoCode($currency_iso);
foreach($products as $product) {
if ($this->context->currency->id != $api_currency_id) {
$price = Tools::convertPrice((float)$product['price_wt'], $this->context->currency, false);
$price = Tools::convertPrice((float)$price, $api_currency_id);
} else {
$price = (float)$product['price_wt'];
}
$CartItem['Amount'] = Tools::ps_round($price) ;
$CartItem['Currency'] = $currency_iso;
$CartItem['Name'] = $product['name'];
$CartItem['Description'] = strip_tags($product['description_short']);
$CartItem['Quantity'] = (int)$product['cart_quantity'];
$CartItem['IsTaxFree'] = $product['price'] != $product['price_wt'];
The problem is when the purchase includes the shipping price, the amount that sends to the API is without the shipping .
what do I need to fix in the code, so that the $CartItem['Amount'] will include the shipping cost?
The shipping price is a different amount. With a proper flag, you can get all the prices you need using Cart::getOrderTotal(). For instance:
$cart->getOrderTotal(true, Cart::ONLY_SHIPPING)
It will return you the amount of shipping for a given shopping cart.

Get product qty using product id Prestashop

I am not able to get qty using product id.
public function hookActionObjectProductUpdateAfter(array $params)
{
$vendproduct = $params['object'];
$lang_id = (int) Configuration::get('PS_LANG_DEFAULT');
$product = new Product($vendproduct->id, false, $lang_id);
print_r($product->quantity); exit;
}
You can get quantity by
$quantity = StockAvailable:: getQuantityAvailableByProduct($id_product = null, $id_product_attribute = null, $id_shop = null);

Sku not working in magento product update API

$result = $client->call($session, 'catalog_product.update', array('123', array(
'name' => 'Product333222'
)
)
);
Here '123' is the Sku of product. Sku is not working here in update Api.
If i give Product ID in place of Sku it is working fine.
So what is the Issue behind that.
If anyone Knows please let me know.
Thanks.
Magento is a bit dull here.
Long story short:
If you are using a numeric value without specifying an identification type its assuming you are doing your works on a product id. If you where to insert "abc" as a value (not numeric) it will be treated as if it were a SKU.
Best way to solve this is to use an identification type (in your case "SKU") in your api call.
Please see this for more info on using the identification type. http://www.magentocommerce.com/api/soap/catalog/catalogProduct/catalog_product.update.html
Or see: Magento 1.5, numeric SKUs and productIdentifierType
Short story long:
The following function gets called trough the api
app/code/core/Mage/Catalog/Model/Api/Resource.php
protected function _getProduct($productId, $store = null, $identifierType = null)
{
$product = Mage::helper('catalog/product')->getProduct($productId, $this->_getStoreId($store), $identifierType);
if (is_null($product->getId())) {
$this->_fault('product_not_exists');
}
return $product;
}
As you can see that function is calling the following function in the product helper:
public function getProduct($productId, $store, $identifierType = null) {
$loadByIdOnFalse = false;
if ($identifierType == null) {
if (is_string($productId) && !preg_match("/^[+-]?[1-9][0-9]*$|^0$/", $productId)) {
$identifierType = 'sku';
$loadByIdOnFalse = true;
} else {
$identifierType = 'id';
}
}
/** #var $product Mage_Catalog_Model_Product */
$product = Mage::getModel('catalog/product');
if ($store !== null) {
$product->setStoreId($store);
}
if ($identifierType == 'sku') {
$idBySku = $product->getIdBySku($productId);
if ($idBySku) {
$productId = $idBySku;
}
if ($loadByIdOnFalse) {
$identifierType = 'id';
}
}
if ($identifierType == 'id' && is_numeric($productId)) {
$productId = !is_float($productId) ? (int) $productId : 0;
$product->load($productId);
}
return $product;
}
Without specifying an $identifierType here and using a sku like '123' the thrid line is going to do a preg match with will result in true. Thus using its else function threating it as an ID in stead of sku.
In the end:
So, do your call like:
$result = $client->call($session, 'catalog_product.update', array('123', array(
'name' => 'Product333222'
), null, 'sku'));

Creating/Retrieving a Cart programmatically

For a module that I'm writing I need to retrieve a cart for a certain user (not necessary registered) after that a link is called and some data are passed.
My idea was to receive back a previously id passed that can help me to identify a certain cart.
My big problem is that I've search a lot for code cart creation into prestashop. Finally I've found something into
/* Cart already exists */
if ((int)$this->context->cookie->id_cart)
{
$cart = new Cart($this->context->cookie->id_cart);
if ($cart->OrderExists())
{
unset($this->context->cookie->id_cart, $cart, $this->context->cookie->checkedTOS);
$this->context->cookie->check_cgv = false;
}
/* Delete product of cart, if user can't make an order from his country */
elseif (intval(Configuration::get('PS_GEOLOCATION_ENABLED')) &&
!in_array(strtoupper($this->context->cookie->iso_code_country), explode(';', Configuration::get('PS_ALLOWED_COUNTRIES'))) &&
$cart->nbProducts() && intval(Configuration::get('PS_GEOLOCATION_NA_BEHAVIOR')) != -1 &&
!FrontController::isInWhitelistForGeolocation() &&
!in_array($_SERVER['SERVER_NAME'], array('localhost', '127.0.0.1')))
unset($this->context->cookie->id_cart, $cart);
// update cart values
elseif ($this->context->cookie->id_customer != $cart->id_customer || $this->context->cookie->id_lang != $cart->id_lang || $currency->id != $cart->id_currency)
{
if ($this->context->cookie->id_customer)
$cart->id_customer = (int)($this->context->cookie->id_customer);
$cart->id_lang = (int)($this->context->cookie->id_lang);
$cart->id_currency = (int)$currency->id;
$cart->update();
}
/* Select an address if not set */
if (isset($cart) && (!isset($cart->id_address_delivery) || $cart->id_address_delivery == 0 ||
!isset($cart->id_address_invoice) || $cart->id_address_invoice == 0) && $this->context->cookie->id_customer)
{
$to_update = false;
if (!isset($cart->id_address_delivery) || $cart->id_address_delivery == 0)
{
$to_update = true;
$cart->id_address_delivery = (int)Address::getFirstCustomerAddressId($cart->id_customer);
}
if (!isset($cart->id_address_invoice) || $cart->id_address_invoice == 0)
{
$to_update = true;
$cart->id_address_invoice = (int)Address::getFirstCustomerAddressId($cart->id_customer);
}
if ($to_update)
$cart->update();
}
}
if (!isset($cart) || !$cart->id)
{
$cart = new Cart();
$cart->id_lang = (int)($this->context->cookie->id_lang);
$cart->id_currency = (int)($this->context->cookie->id_currency);
$cart->id_guest = (int)($this->context->cookie->id_guest);
$cart->id_shop_group = (int)$this->context->shop->id_shop_group;
$cart->id_shop = $this->context->shop->id;
if ($this->context->cookie->id_customer)
{
$cart->id_customer = (int)($this->context->cookie->id_customer);
$cart->id_address_delivery = (int)(Address::getFirstCustomerAddressId($cart->id_customer));
$cart->id_address_invoice = $cart->id_address_delivery;
}
else
{
$cart->id_address_delivery = 0;
$cart->id_address_invoice = 0;
}
// Needed if the merchant want to give a free product to every visitors
$this->context->cart = $cart;
CartRule::autoAddToCart($this->context);
}
that is contained into FrontController.php (that seems to be called in every page). So, to me, a cart should always be present during a "user session".
But - yes, there's a but - when I try to retrieve a cart (in that way, into controller of my module)
$context=Context::getContext();
$id_cart=$context->cart->id;
$id_cart isn't there, so cart seems to miss. So I'm a little bit confused.
What's goin' on here? Someone could give me some pointers?
PS.:
I've tried to replicate that function (only the else part) but it doesn't work
You can force cart generation when the user isn't logged in and there is no product in the cart:
$context = Context::getContext();
if (!$context->cart->id) {
$context->cart->add();
$context->cookie->id_cart = $context->cart->id;
}
$id_cart = $context->cart->id;
Take a look at the processChangeProductInCart method in controllers/front/CartController.php
I'm using Prestashop 1.6, and #yenshiraks answer did not work for me. I cannot use $context->cart->add();, because $context->cartis null.
This is what worked in my case:
$context = Context::getContext();
$cart_id = null
if($context->cookie->id_cart) {
$cart = new Cart($context->cookie->id_cart);
$cart_id = $cart->id; // just in case the cookie contains an invalid cart_id
}
if(empty($cart_id)) {
$cart = new Cart();
$cart->id_lang = (int)$context->cookie->id_lang;
$cart->id_currency = (int)$context->cookie->id_currency;
$cart->id_guest = (int)$context->cookie->id_guest;
$cart->id_shop_group = (int)$context->shop->id_shop_group;
$cart->id_shop = $context->shop->id;
$cart->add();
$cart_id = $cart->id;
}
$context->cookie->id_cart = $cart_id;
To answer the question at the end: Even though a cart is always generated in the FrontController, it is not saved to the database, therefore the id is null.
If you are in a context, where the FrontController is instantiated (any page of the frontend, $context->cart->add(); will suffice to save an empty cart to the database.
If on the other hand, you are in a script, that is called directly (like prestashop/modules/my_module/script.php), you have to use the above code.

How do i write update number of download bought function in controller of magento

i want to update number_of_download_bougt. So How will SQL be in a controllers?
i wrote:
public function updatedownloadAction($db_magento, $id, $numberdownload)
{
// $id = $this->getRequest()->getParam('id', 0);
$db_magento = Mage::getModel('downloadable/link_purchased_item')->load($id);
$db_magento->query("UPDATE downloadable_link_purchased_item d
SET d.number_of_downloads_bought = '$numberdownload'
WHERE d.item_id = '$id'");
}
but it's error
Use the setNumberOfDownloadsUsed function to set the number purchased.
$id = $this->getRequest()->getParam('id', 0);
$linkPurchasedItem = Mage::getModel('downloadable/link_purchased_item')->load($id, 'link_hash');
$linkPurchasedItem->setNumberOfDownloadsUsed($linkPurchasedItem->getNumberOfDownloadsUsed()+1);
if ($linkPurchasedItem->getNumberOfDownloadsBought() != 0 &&
!($linkPurchasedItem->getNumberOfDownloadsBought() - $linkPurchasedItem->getNumberOfDownloadsUsed())) {
$linkPurchasedItem->setStatus(Mage_Downloadable_Model_Link_Purchased_Item::LINK_STATUS_EXPIRED);
}
$linkPurchasedItem->save();