Merge pull request #4623 from IonAgorria/dynamic-price-changes

Dynamic price corretions/changes
This commit is contained in:
Laurent Destailleur 2016-02-16 15:11:02 +01:00
commit e0a0817123
11 changed files with 161 additions and 107 deletions

View File

@ -116,11 +116,11 @@ class box_produits extends ModeleBoxes
'text' => $objp->label,
);
if (empty($objp->fk_price_expression)) {
if (empty($conf->dynamicprices->enabled) || empty($objp->fk_price_expression)) {
$price_base_type=$langs->trans($objp->price_base_type);
$price=($objp->price_base_type == 'HT')?price($objp->price):$price=price($objp->price_ttc);
}
else //Parse the dinamic price
else //Parse the dynamic price
{
$productstatic->fetch($objp->rowid, '', '', 1);
$priceparser = new PriceParser($this->db);

View File

@ -123,12 +123,12 @@ class box_produits_alerte_stock extends ModeleBoxes
'text' => $objp->label,
);
if (empty($objp->fk_price_expression))
if (empty($conf->dynamicprices->enabled) || empty($objp->fk_price_expression))
{
$price_base_type=$langs->trans($objp->price_base_type);
$price=($objp->price_base_type == 'HT')?price($objp->price):$price=price($objp->price_ttc);
}
else //Parse the dinamic price
else //Parse the dynamic price
{
$productstatic->fetch($objp->rowid, '', '', 1);
$priceparser = new PriceParser($this->db);

View File

@ -1795,7 +1795,7 @@ class Form
}
else
{
if (!empty($objp->fk_price_expression)) {
if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_price_expression)) {
$price_product = new Product($this->db);
$price_product->fetch($objp->rowid, '', '', 1);
$priceparser = new PriceParser($this->db);
@ -2089,7 +2089,7 @@ class Form
$sql = "SELECT p.rowid, p.label, p.ref, p.price, p.duration,";
$sql.= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice,";
$sql.= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
$sql.= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, pfp.fk_soc, s.nom as name";
$sql.= " FROM ".MAIN_DB_PREFIX."product as p";
$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
if ($socid) $sql.= " AND pfp.fk_soc = ".$socid;
@ -2173,9 +2173,15 @@ class Form
{
$outqty=$objp->quantity;
$outdiscount=$objp->remise_percent;
if (!empty($objp->fk_supplier_price_expression)) {
if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) {
$prod_supplier = new ProductFournisseur($this->db);
$prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
$prod_supplier->id = $objp->fk_product;
$prod_supplier->fourn_qty = $objp->quantity;
$prod_supplier->fourn_tva_tx = $objp->tva_tx;
$prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
$priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProductSupplier($objp->fk_product, $objp->fk_supplier_price_expression, $objp->quantity, $objp->tva_tx);
$price_result = $priceparser->parseProductSupplier($prod_supplier);
if ($price_result >= 0) {
$objp->fprice = $price_result;
if ($objp->quantity >= 1)
@ -2312,9 +2318,15 @@ class Form
}
$opt.= '>'.$objp->name.' - '.$objp->ref_fourn.' - ';
if (!empty($objp->fk_supplier_price_expression)) {
if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_supplier_price_expression)) {
$prod_supplier = new ProductFournisseur($this->db);
$prod_supplier->product_fourn_price_id = $objp->idprodfournprice;
$prod_supplier->id = $productid;
$prod_supplier->fourn_qty = $objp->quantity;
$prod_supplier->fourn_tva_tx = $objp->tva_tx;
$prod_supplier->fk_supplier_price_expression = $objp->fk_supplier_price_expression;
$priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProductSupplier($objp->fk_product, $objp->fk_supplier_price_expression, $objp->quantity, $objp->tva_tx);
$price_result = $priceparser->parseProductSupplier($prod_supplier);
if ($price_result >= 0) {
$objp->fprice = $price_result;
if ($objp->quantity >= 1)

View File

@ -355,6 +355,7 @@ class ProductFournisseur extends Product
*/
function fetch_product_fournisseur_price($rowid, $ignore_expression = 0)
{
global $conf;
$sql = "SELECT pfp.rowid, pfp.price, pfp.quantity, pfp.unitprice, pfp.remise_percent, pfp.remise, pfp.tva_tx, pfp.fk_availability,";
$sql.= " pfp.fk_soc, pfp.ref_fourn, pfp.fk_product, pfp.charges, pfp.unitcharges, pfp.fk_supplier_price_expression, pfp.delivery_time_days"; // , pfp.recuperableonly as fourn_tva_npr"; FIXME this field not exist in llx_product_fournisseur_price
$sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp";
@ -473,9 +474,9 @@ class ProductFournisseur extends Product
$prodfourn->fourn_tva_npr = $record["info_bits"];
$prodfourn->fk_supplier_price_expression = $record["fk_supplier_price_expression"];
if (!empty($prodfourn->fk_supplier_price_expression)) {
if (!empty($conf->dynamicprices->enabled) && !empty($prodfourn->fk_supplier_price_expression)) {
$priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProductSupplier($prodid, $prodfourn->fk_supplier_price_expression, $prodfourn->fourn_qty, $prodfourn->fourn_tva_tx);
$price_result = $priceparser->parseProductSupplier($prodfourn);
if ($price_result >= 0) {
$prodfourn->fourn_price = $price_result;
$prodfourn->fourn_unitprice = null; //force recalculation of unitprice, as probably the price changed...
@ -572,9 +573,15 @@ class ProductFournisseur extends Product
{
$fourn_price = $record["price"];
$fourn_unitprice = $record["unitprice"];
if (!empty($record["fk_supplier_price_expression"])) {
if (!empty($conf->dynamicprices->enabled) && !empty($record["fk_supplier_price_expression"])) {
$prod_supplier = new ProductFournisseur($this->db);
$prod_supplier->product_fourn_price_id = $record["product_fourn_price_id"];
$prod_supplier->id = $prodid;
$prod_supplier->fourn_qty = $record["quantity"];
$prod_supplier->fourn_tva_tx = $record["tva_tx"];
$prod_supplier->fk_supplier_price_expression = $record["fk_supplier_price_expression"];
$priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProductSupplier($prodid, $record["fk_supplier_price_expression"], $record["quantity"], $record["tva_tx"]);
$price_result = $priceparser->parseProductSupplier($prod_supplier);
if ($price_result >= 0) {
$fourn_price = price2num($price_result,'MU');
if ($record["quantity"] != 0)

View File

@ -296,6 +296,8 @@ ComposedProduct=Sub-product
MinSupplierPrice=Minimum supplier price
DynamicPriceConfiguration=Dynamic price configuration
DynamicPriceDesc=On product card, with this module enabled, you should be able to set mathematic functions to calculate Customer or Supplier prices. Such function can use all mathematic operators, some constants and variables. You can set here the variables you want to be able and if the variable need an automatic update, the external URL to use to ask Dolibarr to update automaticaly the value.
AddVariable=Add Variable
AddUpdater=Add Updater
GlobalVariables=Global variables
VariableToUpdate=Variable to update
GlobalVariableUpdaters=Global variable updaters

View File

@ -173,6 +173,16 @@ if ($action != 'create_updater' && $action != 'edit_updater') {
print '</tr>';
}
print '</table>';
if (empty($action))
{
//Action Buttons
print '<div class="tabsAction">';
print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?action=create_variable">'.$langs->trans("AddVariable").'</a>';
print '</div>';
//Separator is only need for updaters table is showed after buttons
print '<br><br>';
}
}
//Global variable editor
@ -253,6 +263,14 @@ if ($action != 'create_variable' && $action != 'edit_variable') {
print '</tr>';
}
print '</table>';
if (empty($action))
{
//Action Buttons
print '<div class="tabsAction">';
print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?action=create_updater">'.$langs->trans("AddUpdater").'</a>';
print '</div>';
}
}
//Updater editor
@ -321,11 +339,6 @@ if ($action == 'create_updater' || $action == 'edit_updater') {
print '<input type="submit" class="button" name="cancel" id="cancel" value="'.$langs->trans("Cancel").'">';
print '</div>';
print '</form>';
} else {
//Action Buttons
print '<div class="tabsAction">';
print '<a class="butAction" href="'.$_SERVER["PHP_SELF"].'?action=create_updater">'.$langs->trans("Add").'</a>';
print '</div>';
}
llxFooter();

View File

@ -1278,6 +1278,7 @@ class Product extends CommonObject
*/
function get_buyprice($prodfournprice,$qty,$product_id=0,$fourn_ref=0)
{
global $conf;
$result = 0;
// We do select by searching with qty and prodfournprice
@ -1294,11 +1295,17 @@ class Product extends CommonObject
$obj = $this->db->fetch_object($resql);
if ($obj && $obj->quantity > 0) // If found
{
if (!empty($obj->fk_supplier_price_expression))
if (!empty($conf->dynamicprices->enabled) && !empty($obj->fk_supplier_price_expression))
{
require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
$priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProductSupplier($obj->fk_product, $obj->fk_supplier_price_expression, $obj->quantity, $obj->tva_tx);
$prod_supplier = new ProductFournisseur($this->db);
$prod_supplier->product_fourn_price_id = $obj->rowid;
$prod_supplier->id = $obj->fk_product;
$prod_supplier->fourn_qty = $obj->quantity;
$prod_supplier->fourn_tva_tx = $obj->tva_tx;
$prod_supplier->fk_supplier_price_expression = $obj->fk_supplier_price_expression;
$priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProductSupplier($prod_supplier);
if ($price_result >= 0) {
$obj->price = $price_result;
}
@ -1329,11 +1336,17 @@ class Product extends CommonObject
$obj = $this->db->fetch_object($resql);
if ($obj && $obj->quantity > 0) // If found
{
if (!empty($obj->fk_supplier_price_expression))
if (!empty($conf->dynamicprices->enabled) && !empty($obj->fk_supplier_price_expression))
{
require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
$priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProductSupplier($obj->fk_product, $obj->fk_supplier_price_expression, $obj->quantity, $obj->tva_tx);
$prod_supplier = new ProductFournisseur($this->db);
$prod_supplier->product_fourn_price_id = $obj->rowid;
$prod_supplier->id = $obj->fk_product;
$prod_supplier->fourn_qty = $obj->quantity;
$prod_supplier->fourn_tva_tx = $obj->tva_tx;
$prod_supplier->fk_supplier_price_expression = $obj->fk_supplier_price_expression;
$priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProductSupplier($prod_supplier);
if ($result >= 0) {
$obj->price = $price_result;
}
@ -1785,7 +1798,7 @@ class Product extends CommonObject
}
}
if (!empty($this->fk_price_expression) && empty($ignore_expression))
if (!empty($conf->dynamicprices->enabled) && !empty($this->fk_price_expression) && empty($ignore_expression))
{
require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php';
$priceparser = new PriceParser($this->db);

View File

@ -131,6 +131,15 @@ class PriceParser
public function parseExpression($product, $expression, $values)
{
global $user;
//Check if empty
$expression = trim($expression);
if (empty($expression))
{
$this->error = array(20, null);
return -2;
}
//Accessible product values by expressions
$values = array_merge($values, array(
"tva_tx" => $product->tva_tx,
@ -169,14 +178,6 @@ class PriceParser
$values["global_".$entry->code] = $entry->value;
}
//Check if empty
$expression = trim($expression);
if (empty($expression))
{
$this->error = array(20, null);
return -2;
}
//Prepare the lib, parameters and values
$em = new EvalMath();
$em->suppress_errors = true; //Don't print errors on page
@ -187,7 +188,8 @@ class PriceParser
$expression = str_replace("\n", $this->separator_chr, $expression);
foreach ($values as $key => $value)
{
$expression = str_replace($this->special_chr.$key.$this->special_chr, "$value", $expression);
if ($value === null) $value = "NULL";
$expression = str_replace($this->special_chr.$key.$this->special_chr, strval($value), $expression);
}
$expressions = explode($this->separator_chr, $expression);
$expressions = array_slice($expressions, 0, $this->limit);
@ -221,40 +223,11 @@ class PriceParser
}
/**
* Calculates product price based on product id and string expression
*
* @param Product $product The Product object to get information
* @param string $expression The expression to parse
* @param array $extra_values Any aditional values for expression
* @return int > 0 if OK, < 1 if KO
*/
public function parseProductExpression($product, $expression, $extra_values = array())
{
//Get the supplier min
$productFournisseur = new ProductFournisseur($this->db);
$supplier_min_price = $productFournisseur->find_min_price_product_fournisseur($product->id);
//Accessible values by expressions
$extra_values = array_merge($extra_values, array(
"supplier_min_price" => $supplier_min_price,
));
//Parse the expression and return the price, if not error occurred check if price is higher than min
$result = $this->parseExpression($product, $expression, $extra_values);
if (empty($this->error)) {
if ($result < $product->price_min) {
$result = $product->price_min;
}
}
return $result;
}
/**
* Calculates product price based on product id and expression id
* Calculates product price based on product id and associated expression
*
* @param Product $product The Product object to get information
* @param array $extra_values Any aditional values for expression
* @return int > 0 if OK, < 1 if KO
* @return int > 0 if OK, < 1 if KO
*/
public function parseProduct($product, $extra_values = array())
{
@ -266,55 +239,80 @@ class PriceParser
return -1;
}
//Parse the expression and return the price
return $this->parseProductExpression($product, $price_expression->expression, $extra_values);
}
/**
* Calculates supplier product price based on product id and string expression
*
* @param int $product_id The Product id to get information
* @param string $expression The expression to parse
* @param int $quantity Supplier Min quantity
* @param int $tva_tx Supplier VAT rate
* @param array $extra_values Any aditional values for expression
* @return int > 0 if OK, < 1 if KO
*/
public function parseProductSupplierExpression($product_id, $expression, $quantity = null, $tva_tx = null, $extra_values = array())
{
//Get the product data
$product = new ProductFournisseur($this->db);
$product->fetch($product_id, '', '', 1);
//Get the supplier min
$productFournisseur = new ProductFournisseur($this->db);
$supplier_min_price = $productFournisseur->find_min_price_product_fournisseur($product->id);
//Accessible values by expressions
$extra_values = array_merge($extra_values, array(
"supplier_quantity" => $quantity,
"supplier_tva_tx" => $tva_tx,
"supplier_min_price" => $supplier_min_price,
));
return $this->parseExpression($product, $expression, $extra_values);
//Parse the expression and return the price, if not error occurred check if price is higher than min
$result = $this->parseExpression($product, $price_expression->expression, $extra_values);
if (empty($this->error)) {
if ($result < $product->price_min) {
$result = $product->price_min;
}
}
return $result;
}
/**
* Calculates supplier product price based on product id and expression id
* Calculates supplier product price based on product supplier price and associated expression
*
* @param int $product_id The Product id to get information
* @param int $expression_id The expression to parse
* @param int $quantity Min quantity
* @param int $tva_tx VAT rate
* @param array $extra_values Any aditional values for expression
* @param ProductFournisseur $product_supplier The Product supplier object to get information
* @param array $extra_values Any aditional values for expression
* @return int > 0 if OK, < 1 if KO
*/
public function parseProductSupplier($product_id, $expression_id, $quantity = null, $tva_tx = null, $extra_values = array())
public function parseProductSupplier($product_supplier, $extra_values = array())
{
//Get the expression from db
$price_expression = new PriceExpression($this->db);
$res = $price_expression->fetch($expression_id);
if ($res < 1) {
$res = $price_expression->fetch($product_supplier->fk_supplier_price_expression);
if ($res < 1)
{
$this->error = array(19, null);
return -1;
}
//Get the product data (use ignore_expression to avoid possible recursion)
$product_supplier->fetch($product_supplier->id, '', '', 1);
//Accessible values by expressions
$extra_values = array_merge($extra_values, array(
"supplier_quantity" => $product_supplier->fourn_qty,
"supplier_tva_tx" => $product_supplier->fourn_tva_tx,
));
//Parse the expression and return the price
return $this->parseProductSupplierExpression($product_id, $price_expression->expression, $quantity, $tva_tx, $extra_values);
return $this->parseExpression($product_supplier, $price_expression->expression, $extra_values);
}
/**
* Tests string expression for validity
*
* @param int $product_id The Product id to get information
* @param string $expression The expression to parse
* @param array $extra_values Any aditional values for expression
* @return int > 0 if OK, < 1 if KO
*/
public function testExpression($product_id, $expression, $extra_values = array())
{
//Get the product data
$product = new Product($this->db);
$product->fetch($product_id, '', '', 1);
//Values for product expressions
$extra_values = array_merge($extra_values, array(
"supplier_min_price" => 1,
));
//Values for supplier product expressions
$extra_values = array_merge($extra_values, array(
"supplier_quantity" => 2,
"supplier_tva_tx" => 3,
));
return $this->parseExpression($product, $expression, $extra_values);
}
}

View File

@ -16,7 +16,7 @@
*/
/**
* \file htdocs/product/expression/editor.php
* \file htdocs/product/dynamic_price/editor.php
* \ingroup product
* \brief Page for editing expression
*/
@ -73,7 +73,7 @@ if ($action == 'add')
{
//Check the expression validity by parsing it
$priceparser = new PriceParser($db);
$price_result = $priceparser->parseProductSupplierExpression($id, $expression, 0, 0);
$price_result = $priceparser->testExpression($id, $expression);
if ($price_result < 0) { //Expression is not valid
setEventMessages($priceparser->translatedError(), null, 'errors');
}
@ -113,7 +113,7 @@ if ($action == 'update')
{
//Check the expression validity by parsing it
$priceparser = new PriceParser($db);
$price_result = $priceparser->parseProductSupplierExpression($id, $expression, 0, 0);
$price_result = $priceparser->testExpression($id, $expression);
if ($price_result < 0) { //Expression is not valid
setEventMessages($priceparser->translatedError(), null, 'errors');
}

View File

@ -225,18 +225,26 @@ if (empty($reshook))
}
else
{
if ($price_expression !== '')
if (!empty($conf->dynamicprices->enabled) && $price_expression !== '')
{
//Check the expression validity by parsing it
$priceparser = new PriceParser($db);
$price_result = $priceparser->parseProductSupplier($id, $price_expression, $quantity, $tva_tx);
$prod_supplier = new ProductFournisseur($this->db);
$prod_supplier->id = $prodid;
$prod_supplier->fourn_qty = $quantity;
$prod_supplier->fourn_tva_tx = $tva_tx;
$prod_supplier->fourn_id = $id_fourn;
$prod_supplier->fk_supplier_price_expression = $price_expression;
$priceparser = new PriceParser($db);
$price_result = $priceparser->parseProductSupplier($prod_supplier);
if ($price_result < 0) { //Expression is not valid
$error++;
setEventMessages($priceparser->translatedError(), null, 'errors');
}
}
if (! $error && ! empty($conf->dynamicprices->enabled)) {
$ret=$object->setPriceExpression($price_expression);
if (! $error && ! empty($conf->dynamicprices->enabled))
{
//Set the price expression for this supplier price
$ret=$object->setSupplierPriceExpression($price_expression);
if ($ret < 0)
{
$error++;

View File

@ -307,7 +307,8 @@ if ($result)
// Sell price
if (empty($conf->global->PRODUIT_MULTIPRICES))
{
if (!empty($objp->fk_price_expression)) {
if (!empty($conf->dynamicprices->enabled) && !empty($objp->fk_price_expression))
{
$product = new Product($db);
$product->fetch($objp->rowid);
$priceparser = new PriceParser($db);