(prestashop) How can I detect if there's a catalog price rule bound to a category or some of its parents? - prestashop

I have catalog price rules for some categories. In frontend, in category.tpl, I have to notify if there are special prices bound to that specific category or some of its parents.
For now, I'm building a function on the controller that finds it with a query. I was wandering if there was some shortcut for doing this.

I wrote a function (a CategoryController method) to solve my problem and I share:
public function get_category_promo(){
//get the active country, since promo are also country-based
if($this->context->customer->isLogged()){
$current_country = Customer::getCurrentCountry($this->context->customer->id);
} else {
$current_country = $this->context->country->id;
}
$db = Db::getInstance();
$sql = '
SELECT
truncate(`reduction`,0) as reduction,
`reduction_type`,
`from`,
`to`,
`type` as category_type,
`value` as id_category,
`id_parent` as category_parent,
`level_depth` as depth
FROM
`'._DB_PREFIX_.'specific_price_rule_condition` rule_condition
INNER JOIN
`'._DB_PREFIX_.'specific_price_rule_condition_group` rule_group
on rule_condition.`id_specific_price_rule_condition_group` = rule_group.`id_specific_price_rule_condition_group`
INNER JOIN
`'._DB_PREFIX_.'specific_price_rule` price_rule
on price_rule.`id_specific_price_rule` = rule_group.`id_specific_price_rule`
INNER JOIN
`'._DB_PREFIX_.'category` category
on rule_condition.`value` = category.`id_category`
WHERE rule_condition.`type` = "category"';
$parents = $this->category->getParentsCategories();
array_shift($parents);//first is == this->category so I shift it out
$sql_aux = ' and (rule_condition.`value` = ' . $this->category->id_category;
foreach($parents as $parent){
$sql_aux .= ' or rule_condition.`value` = ' . $parent["id_category"];
}
$sql_aux .= ')';
$sql_aux .= ' and price_rule.`id_country` = ' . $current_country;
$sql_aux .= ' order by level_depth desc';
$sql .= $sql_aux;
$promo_data = $db->executeS($sql);
$promo_data = count($promo_data) > 0 ? $promo_data : null;
if(!$promo_data) return false;//stop
function validate_date($promo){
//if there are no dates
if((int)$promo['to'] == 0 && (int)$promo['from'] == 0) return true;
//if there is end promo date
if((int)$promo['to'] != 0){
$to = new DateTime($promo['to']);
//...and is still valid
if($to >= new DateTime('NOW')) return true;
}
}//end validate
//refine query results
$filtered = array_values(array_filter($promo_data,'validate_date'));
$promo_data = $filtered[0];//if there are more than one promo on the same category, the promo on the higher depth is used form the system, so I need only that promo
//promo without dates. Only refine to/from to better use in smarty
if((int)$promo_data['to'] == 0 && (int)$promo_data['from'] == 0){
$promo_data['to'] = 0;
$promo_data['from'] = 0;
$this->context->smarty->assign('promo_data', $promo_data);
return true;
}
if((int)$promo_data['to'] != 0){//promo ha send date
$to = new DateTime($promo_data['to']);
if($to >= new DateTime('NOW')){
$promo_data['to'] = $to->format('d-m-Y');
} else {
return false;//not a valid date
}
}
if((int)$promo_data['from'] != 0){
$from = new DateTime($promo_data['from']);
$promo_data['from'] = $from->format('d-m-Y');
} else {
$promo_data['from'] = 0;
}
$this->context->smarty->assign('promo_data', $promo_data);
}//end get_category_promo

Related

How to make a report parameter selection change another report parameter selection values while allowing multiselect for both of them?

Lets say I have a table with a list of names, such as "a, b, c" and each name has several other values assigned (some of the other values can be assigned to several/all of names values, example below).
Table example:
( names - other ):
a - aa
a - ab
a - ac
b - ab
b - bb
b - cb
c - ac
c - bc
c - cc
How do I make in birt so that I could select names in one parameter box and get corresponding other values to select for another parameter? I know it's possible to do that with cascading parameter, but that doesn't allow to have multiselect for the first parameter, which in our example would be names values.
Found a solution partly here: https://forums.opentext.com/forums/developer/discussion/61283/allow-multi-value-for-cascading-parameter
and here (thanks google translate ^^): https://www.developpez.net/forums/d1402270/logiciels/solutions-d-entreprise/business-intelligence/birt/birt-4-3-1-multi-value-cascade-parameters/
Steps to do:
change"\birt\webcontent\birt\ajax\ui\dialog\BirtParameterDialog.js" file contents (there is a file to download for replacement in one of the links, but in case it goes dead I'm sharing 2 snippets from it where the changes occur:
__refresh_cascade_select : function( element )
{
var matrix = new Array( );
var m = 0;
for( var i = 0; i < this.__cascadingParameter.length; i++ )
{
for( var j = 0; j < this.__cascadingParameter[i].length; j++ )
{
var paramName = this.__cascadingParameter[i][j].name;
if( paramName == this.__isnull )
paramName = this.__cascadingParameter[i][j].value;
if( paramName == element.id.substr( 0, element.id.length - 10 ) )
{
//CHANGE//
//The two way work (String(selectedValue) or Array(SelectedValueArray))
var l = 0;
var isFirstElement = true;
var selectedValue = "";
var selectedValueArray = new Array();
for(var test = 0; test<element.options.length;test++){
if(element.options[test].selected){
if(isFirstElement){
isFirstElement = false;
selectedValue = element.options[test].value;
}
else{
selectedValue = selectedValue+","+element.options[test].value;
}
selectedValueArray[l] = element.options[test].value;
l++;
}
}
//CHANGE//
var tempText = element.options[element.selectedIndex].text;
var tempValue = element.options[element.selectedIndex].value;
// Null Value Parameter
if ( tempValue == Constants.nullValue )
{
this.__cascadingParameter[i][j].name = this.__isnull;
this.__cascadingParameter[i][j].value = paramName;
}
else if( tempValue == '' )
{
if( tempText == "" )
{
var target = element;
target = target.parentNode;
var oInputs = target.getElementsByTagName( "input" );
if( oInputs.length >0 && oInputs[1].value != Constants.TYPE_STRING )
{
// Only String parameter allows blank value
alert( birtUtility.formatMessage( Constants.error.parameterNotAllowBlank, paramName ) );
this.__clearSubCascadingParameter( this.__cascadingParameter[i], j );
return;
}
else
{
// Blank Value
this.__cascadingParameter[i][j].name = paramName;
this.__cascadingParameter[i][j].value = tempValue;
}
}
else
{
// Blank Value
this.__cascadingParameter[i][j].name = paramName;
this.__cascadingParameter[i][j].value = tempValue;
}
}
else
{
this.__cascadingParameter[i][j].name = paramName;
//CHANGE//
//The two way work (String(selectedValue) or Array(SelectedValueArray))
this.__cascadingParameter[i][j].value = selectedValueArray;
//CHANGE//
}
for( var m = 0; m <= j; m++ )
{
if( !matrix[m] )
{
matrix[m] = {};
}
matrix[m].name = this.__cascadingParameter[i][m].name;
matrix[m].value = this.__cascadingParameter[i][m].value;
}
this.__pendingCascadingCalls++;
birtEventDispatcher.broadcastEvent( birtEvent.__E_CASCADING_PARAMETER, matrix );
}
}
}
},
and this:
// exist select control and input text/password
// compare the parent div offsetTop
if( oFirstITC.parentNode && oFirstST.parentNode )
{
// Bugzilla 265615: need to use cumulative offset for special cases
// where one element is inside a group container
var offsetITC = Position.cumulativeOffset( oFirstITC );
var offsetST = Position.cumulativeOffset( oFirstST );
// compare y-offset first, then x-offset to determine the visual order
if( ( offsetITC[1] > offsetST[1] ) || ( offsetITC[1] == offsetST[1] && offsetITC[0] > offsetST[0] ) )
{
oFirstST.focus( );
}
else
{
oFirstITC.focus( );
}
}
After .js is changed cascading parameters can have multiselect on all levels.
Example:
1st DataSet "DS_country" query:
select CLASSICMODELS.OFFICES.COUNTRY
from CLASSICMODELS.OFFICES
2nd DataSet "DS_office" query:
select CLASSICMODELS.OFFICES.OFFICECODE
from CLASSICMODELS.OFFICES
where CLASSICMODELS.OFFICES.COUNTRY IN ('DS_country')
After datasets are created we can make cascading report parameters CRP_country and CRP_office (keep in mind that UI doesn't let you to choose "Allow multiple values" on the upper levels, but we can change that in property editor after the parameters are made by going to advanced tab and changing "Scalar parameter type" property value to "Multi Value")
The only thing left is to pick lower level cascading parameters (CRP_office in our example) and go to script tab and add this on "beforeOpen":
// Do that if your selections are in String type.
var stringArray = params["CRP_country"].value.toString().split(",");
var result = "";
for(var i =0 ; i < stringArray.length ; i++){
if(i==0){
result = "'"+stringArray[i]+"'";
}
else{
result = result+",'"+stringArray[i]+"'";
}
}
// For String
this.queryText = this.queryText.replace("'DS_country'", result);
//For integer (the first part is useless)
//this.queryText = this.queryText.replace("'DS_country'",
params["CRP_country"].value);

DBContext Query returns null

Codes inside Weekly returns a value. But in Monthly it returns null.
I tried to run the query in my SQL Server Management Studio and it returns the correct result. But in my code, it returns null.
I tried to breakpoint and the item.JobId is = 2.
Here is my codes.
if (item.Frequency == "Weekly")
{
if (item.StartDate.DayOfWeek.ToString() == DateTime.Now.DayOfWeek.ToString())
{
var jobname = _context.Jobs.Where(x => x.Id == item.JobId).FirstOrDefault();
if (ModelState.IsValid)
{
jobs.Id = 0;
jobs.Job = jobname.Job;
jobs.ClientName = jobname.ClientName;
jobs.ClientId = jobname.ClientId;
jobs.JobRate = jobname.JobRate;
jobs.Status = "Active";
}
}
}
else if (item.Frequency == "Monthly")
{
if (item.StartDate.Day.ToString() == DateTime.Now.Day.ToString())
{
var jobname = _context.Jobs.Where(x => x.Id == item.JobId).FirstOrDefault();
var a = jobname.Job;
if (ModelState.IsValid)
{
jobs.Id = 0;
jobs.Job = jobname.Job;
jobs.ClientName = jobname.ClientName;
jobs.ClientId = jobname.ClientId;
jobs.JobRate = jobname.JobRate;
jobs.Status = "Active";
}
}
}
And here is my SQL Query
SELECT TOP (200) Id, Job, ClientName, ClientId, JobRate, Status
FROM Jobs
WHERE (Id = 2)
It should return some value in it.
Both inner expressions of the ifs(ones controlling item.StartDate) are identical, so your problem lies in these controls. Can you make sure your code evaluates true for below condition?
if (item.StartDate.Day.ToString() == DateTime.Now.Day.ToString())

Calculate the price based on height and width

I need to make a module that allow the user to enter width and height , and then it should calculate the price based on the height and width .
thanks in advance
let's say that you want to do it on the product ID N° 1 from the product page :
first add two inputs fields in your product.tpl ( or in your module.tpl called in product.tpl)
<input name="height"><br>
<input name="width">
now you need to get it in your module.php to change the price dynamicly :
$id_product = 1;
$newPrice = 0;
$height = Tools::getValue('height');
$width = Tools::getValue('width');
//Price rules (change with your rules)
if($height > 10 && $width > 10) $newPrice = 10;
$specific_price = new SpecificPrice();
$specific_price->price = $newPrice;
$specific_price->id_cart = (int) $this->context->cart->id;
$specific_price->id_shop = 0;
$specific_price->id_shop_group = 0;
$specific_price->id_currency = 0;
$specific_price->id_country = 0;
$specific_price->id_group = 0;
$specific_price->id_customer = (int) $this->context->customer->id;
$specific_price->id_product = (int)$id_product;
$specific_price->id_product_attribute = 0;
$specific_price->from_quantity = 1;
$specific_price->reduction = 0;
$specific_price->reduction_type = 'amount';
$specific_price->from = '0000-00-00 00:00:00';
$specific_price->to = '0000-00-00 00:00:00';
$specific_price->add();
and if you want add it to cart
$cart->id_lang = (int)($this->context->cookie->id_lang);
$cart->id_currency = (int)($this->context->cookie->id_currency);
$cart->updateQty($qte, $id_product);
and after order is done you put the normal price, so override the orderConfirmation controller in override/controllers/front/OrderConfirmationController.php:
class OrderConfirmationController extends OrderConfirmationControllerCore
{
public $ssl = true;
public $php_self = 'order-confirmation';
public $id_cart;
public $id_module;
public $id_order;
public $reference;
public $secure_key;
public function getPriceBack($id_product){
$normalPrice = Product::getPriceStatic($id_product);
$specific_price = new SpecificPrice();
$specific_price->price = $normalPrice;
$specific_price->id_cart = (int) $this->context->cart->id;
$specific_price->id_shop = 0;
$specific_price->id_shop_group = 0;
$specific_price->id_currency = 0;
$specific_price->id_country = 0;
$specific_price->id_group = 0;
$specific_price->id_customer = (int) $this->context->customer->id;
$specific_price->id_product = (int)$id_product;
$specific_price->id_product_attribute = 0;
$specific_price->from_quantity = 1;
$specific_price->reduction = 0;
$specific_price->reduction_type = 'amount';
$specific_price->from = '0000-00-00 00:00:00';
$specific_price->to = '0000-00-00 00:00:00';
$specific_price->add();
}
public function init()
{
parent::init();
$this->id_cart = (int)(Tools::getValue('id_cart', 0));
$is_guest = false;
/* get normal price back */
$id_product = 1;
self::getPriceBack($id_product);
/* check if the cart has been made by a Guest customer, for redirect link */
if (Cart::isGuestCartByCartId($this->id_cart)) {
$is_guest = true;
$redirectLink = 'index.php?controller=guest-tracking';
} else {
$redirectLink = 'index.php?controller=history';
}
$this->id_module = (int)(Tools::getValue('id_module', 0));
$this->id_order = Order::getOrderByCartId((int)($this->id_cart));
$this->secure_key = Tools::getValue('key', false);
$order = new Order((int)($this->id_order));
if ($is_guest) {
$customer = new Customer((int)$order->id_customer);
$redirectLink .= '&id_order='.$order->reference.'&email='.urlencode($customer->email);
}
if (!$this->id_order || !$this->id_module || !$this->secure_key || empty($this->secure_key)) {
Tools::redirect($redirectLink.(Tools::isSubmit('slowvalidation') ? '&slowvalidation' : ''));
}
$this->reference = $order->reference;
if (!Validate::isLoadedObject($order) || $order->id_customer != $this->context->customer->id || $this->secure_key != $order->secure_key) {
Tools::redirect($redirectLink);
}
$module = Module::getInstanceById((int)($this->id_module));
if ($order->module != $module->name) {
Tools::redirect($redirectLink);
}
}
}
after the order is done you have to empty the cart with id_product and delete rule in dbb :
$this->context->cart->deleteProduct(1);
Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'specific_price WHERE id_customer='.(int)$this->context->customer->id);
it s just an example and can not use without variables tests and maybe put constante for your product id concerned, hope it helps

ignore where statement if parameter is null in doctrine

Hi i have a problem in QueryByilder in Doctrine. i wrote a Query that has 2 parameter and they affect in where statement. i want to ignore where statement if the related parameter was null. for example if $play = 3 and $theater = null the query must return all tickets with play 3 and whatever theater
this is my code:
public function getAllSearchedTickets($play,$teater){
return $this->getEntityManager()->createQuery('
select s from mtadminBundle:ReserveLocation s
join s.reserve a
join a.sance b
where a.acceptCode != 0
and b.play = :play
and b.teater = :teater')
->setParameters(array('play'=>$play,'teater'=>$teater))->getResult();
}
thank you.
You should use the QueryBuilder for this, to do it more efficiently, I'll show you how you do yours and then the same with the QueryBuilder as example:
Yours:
public function getAllSearchedTickets($play,$teater){
$query = 'select s from mtadminBundle:ReserveLocation s'.
'join s.reserve a'.
'join a.sance b'.
'where a.acceptCode != 0');
$paramArray = array();
if( $play ) {
$query .= ' and b.play = :play';
$paramArray['play'] = $play;
}
if( $teater ) {
$query .= ' and b.teater = :teater';
$paramArray['teater '] = $teater;
}
return $this->getEntityManager()->createQuery($query)
->setParameters($paramArray)->getResult();
}
QueryBuilder:
public function getAllSearchedTickets($play,$teater){
$queryBuilder = $this->getEntityManager()->createQueryBuilder();
$queryBuilder->select('s')
->from('mtadminBundle:ReserveLocation', 's')
->join('s.reserve', 'a')
->join('a.sance', 'b')
->where('a.acceptCode != 0');
if( $play ) {
$queryBuilder->andWhere('b.play = :play');
$queryBuilder->setParameter('play', $play);
}
if( $teater ) {
$queryBuilder->andWhere('b.teater = :teater');
$queryBuilder->setParameter('teater', $teater);
}
return $queryBuilder->getResult();
}

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.