2
0
forked from Wavyzz/dolibarr

Merge branch 'dinamic-prices-product' of

https://github.com/IonAgorria/dolibarr into
IonAgorria-dinamic-prices-product

Conflicts:
	htdocs/install/mysql/migration/3.7.0-3.8.0.sql
	htdocs/product/fournisseurs.php
This commit is contained in:
Laurent Destailleur
2015-01-31 14:53:14 +01:00
16 changed files with 503 additions and 176 deletions

24
htdocs/core/boxes/box_produits.php Normal file → Executable file
View File

@@ -63,7 +63,7 @@ class box_produits extends ModeleBoxes
if ($user->rights->produit->lire || $user->rights->service->lire) if ($user->rights->produit->lire || $user->rights->service->lire)
{ {
$sql = "SELECT p.rowid, p.label, p.price, p.price_base_type, p.price_ttc, p.fk_product_type, p.tms, p.tosell, p.tobuy"; $sql = "SELECT p.rowid, p.label, p.price, p.price_base_type, p.price_ttc, p.fk_product_type, p.tms, p.tosell, p.tobuy, p.fk_price_expression";
$sql.= " FROM ".MAIN_DB_PREFIX."product as p"; $sql.= " FROM ".MAIN_DB_PREFIX."product as p";
$sql.= ' WHERE p.entity IN ('.getEntity($productstatic->element, 1).')'; $sql.= ' WHERE p.entity IN ('.getEntity($productstatic->element, 1).')';
if (empty($user->rights->produit->lire)) $sql.=' AND p.fk_product_type != 0'; if (empty($user->rights->produit->lire)) $sql.=' AND p.fk_product_type != 0';
@@ -106,7 +106,7 @@ class box_produits extends ModeleBoxes
$this->info_box_contents[$i][1] = array('td' => 'align="left"', $this->info_box_contents[$i][1] = array('td' => 'align="left"',
'text' => $objp->label, 'text' => $objp->label,
'url' => DOL_URL_ROOT."/product/card.php?id=".$objp->rowid); 'url' => DOL_URL_ROOT."/product/card.php?id=".$objp->rowid);
if (empty($objp->fk_price_expression)) {
if ($objp->price_base_type == 'HT') if ($objp->price_base_type == 'HT')
{ {
$price=price($objp->price); $price=price($objp->price);
@@ -117,6 +117,26 @@ class box_produits extends ModeleBoxes
$price=price($objp->price_ttc); $price=price($objp->price_ttc);
$price_base_type=$langs->trans("TTC"); $price_base_type=$langs->trans("TTC");
} }
}
else //Parse the dinamic price
{
$product = new Product($this->db);
$product->fetch($objp->rowid, '', '', 1);
$priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProduct($product);
if ($price_result >= 0) {
if ($objp->price_base_type == 'HT')
{
$price_base_type=$langs->trans("HT");
}
else
{
$price_result = $price_result * (1 + ($product->tva_tx / 100));
$price_base_type=$langs->trans("TTC");
}
$price=price($price_result);
}
}
$this->info_box_contents[$i][2] = array('td' => 'align="right"', $this->info_box_contents[$i][2] = array('td' => 'align="right"',
'text' => $price); 'text' => $price);

21
htdocs/core/boxes/box_produits_alerte_stock.php Normal file → Executable file
View File

@@ -114,6 +114,7 @@ class box_produits_alerte_stock extends ModeleBoxes
'text' => $objp->label, 'text' => $objp->label,
'url' => DOL_URL_ROOT."/product/card.php?id=".$objp->rowid); 'url' => DOL_URL_ROOT."/product/card.php?id=".$objp->rowid);
if (empty($objp->fk_price_expression)) {
if ($objp->price_base_type == 'HT') if ($objp->price_base_type == 'HT')
{ {
$price=price($objp->price); $price=price($objp->price);
@@ -124,6 +125,26 @@ class box_produits_alerte_stock extends ModeleBoxes
$price=price($objp->price_ttc); $price=price($objp->price_ttc);
$price_base_type=$langs->trans("TTC"); $price_base_type=$langs->trans("TTC");
} }
}
else //Parse the dinamic price
{
$product = new Product($this->db);
$product->fetch($objp->rowid, '', '', 1);
$priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProduct($product);
if ($price_result >= 0) {
if ($objp->price_base_type == 'HT')
{
$price_base_type=$langs->trans("HT");
}
else
{
$price_result = $price_result * (1 + ($product->tva_tx / 100));
$price_base_type=$langs->trans("TTC");
}
$price=price($price_result);
}
}
$this->info_box_contents[$i][2] = array('td' => 'align="right"', $this->info_box_contents[$i][2] = array('td' => 'align="right"',
'text' => $price); 'text' => $price);

View File

@@ -1466,7 +1466,7 @@ class Form
$outarray=array(); $outarray=array();
$sql = "SELECT "; $sql = "SELECT ";
$sql.= " p.rowid, p.label, p.ref, p.description, p.fk_product_type, p.price, p.price_ttc, p.price_base_type, p.tva_tx, p.duration, p.stock"; $sql.= " p.rowid, p.label, p.ref, p.description, p.fk_product_type, p.price, p.price_ttc, p.price_base_type, p.tva_tx, p.duration, p.stock, p.fk_price_expression";
//Price by customer //Price by customer
if (! empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) { if (! empty($conf->global->PRODUIT_CUSTOMER_PRICES) && !empty($socid)) {
@@ -1545,6 +1545,8 @@ class Form
$result=$this->db->query($sql); $result=$this->db->query($sql);
if ($result) if ($result)
{ {
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
require_once DOL_DOCUMENT_ROOT.'/product/class/priceparser.class.php';
$num = $this->db->num_rows($result); $num = $this->db->num_rows($result);
$out.='<select class="flat" name="'.$htmlname.'" id="'.$htmlname.'">'; $out.='<select class="flat" name="'.$htmlname.'" id="'.$htmlname.'">';
@@ -1594,6 +1596,19 @@ class Form
} }
else else
{ {
if (!empty($objp->fk_price_expression)) {
$price_product = new Product($this->db);
$price_product->fetch($objp->rowid, '', '', 1);
$priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProduct($price_product);
if ($price_result >= 0) {
$objp->price = $price_result;
$objp->unitprice = $price_result;
//Calculate the VAT
$objp->price_ttc = price2num($objp->price) * (1 + ($objp->tva_tx / 100));
$objp->price_ttc = price2num($objp->price_ttc,'MU');
}
}
$this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected); $this->constructProductListOption($objp, $opt, $optJson, $price_level, $selected);
// Add new entry // Add new entry
// "key" value of json key array is used by jQuery automatically as selected value // "key" value of json key array is used by jQuery automatically as selected value
@@ -1875,7 +1890,7 @@ class Form
$sql = "SELECT p.rowid, p.label, p.ref, p.price, p.duration,"; $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.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.remise_percent, pfp.remise, pfp.unitprice,";
$sql.= " pfp.fk_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name"; $sql.= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
$sql.= " FROM ".MAIN_DB_PREFIX."product as p"; $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"; $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; if ($socid) $sql.= " AND pfp.fk_soc = ".$socid;
@@ -1957,9 +1972,9 @@ class Form
{ {
$outqty=$objp->quantity; $outqty=$objp->quantity;
$outdiscount=$objp->remise_percent; $outdiscount=$objp->remise_percent;
if (!empty($objp->fk_price_expression)) { if (!empty($objp->fk_supplier_price_expression)) {
$priceparser = new PriceParser($this->db); $priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProductSupplier($objp->fk_product, $objp->fk_price_expression, $objp->quantity, $objp->tva_tx); $price_result = $priceparser->parseProductSupplier($objp->fk_product, $objp->fk_supplier_price_expression, $objp->quantity, $objp->tva_tx);
if ($price_result >= 0) { if ($price_result >= 0) {
$objp->fprice = $price_result; $objp->fprice = $price_result;
if ($objp->quantity >= 1) if ($objp->quantity >= 1)
@@ -2056,7 +2071,7 @@ class Form
$sql = "SELECT p.rowid, p.label, p.ref, p.price, p.duration,"; $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.unitprice,"; $sql.= " pfp.ref_fourn, pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.quantity, pfp.unitprice,";
$sql.= " pfp.fk_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name"; $sql.= " pfp.fk_supplier_price_expression, pfp.fk_product, pfp.tva_tx, s.nom as name";
$sql.= " FROM ".MAIN_DB_PREFIX."product as p"; $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"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product_fournisseur_price as pfp ON p.rowid = pfp.fk_product";
$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON pfp.fk_soc = s.rowid"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON pfp.fk_soc = s.rowid";
@@ -2096,9 +2111,9 @@ class Form
} }
$opt.= '>'.$objp->name.' - '.$objp->ref_fourn.' - '; $opt.= '>'.$objp->name.' - '.$objp->ref_fourn.' - ';
if (!empty($objp->fk_price_expression)) { if (!empty($objp->fk_supplier_price_expression)) {
$priceparser = new PriceParser($this->db); $priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProductSupplier($objp->fk_product, $objp->fk_price_expression, $objp->quantity, $objp->tva_tx); $price_result = $priceparser->parseProductSupplier($objp->fk_product, $objp->fk_supplier_price_expression, $objp->quantity, $objp->tva_tx);
if ($price_result >= 0) { if ($price_result >= 0) {
$objp->fprice = $price_result; $objp->fprice = $price_result;
if ($objp->quantity >= 1) if ($objp->quantity >= 1)

View File

@@ -52,7 +52,7 @@ if ($idprod > 0)
$sql = "SELECT p.rowid, p.label, p.ref, p.price, p.duration,"; $sql = "SELECT p.rowid, p.label, p.ref, p.price, p.duration,";
$sql.= " pfp.ref_fourn,"; $sql.= " pfp.ref_fourn,";
$sql.= " pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice, pfp.charges, pfp.unitcharges,"; $sql.= " pfp.rowid as idprodfournprice, pfp.price as fprice, pfp.remise_percent, pfp.quantity, pfp.unitprice, pfp.charges, pfp.unitcharges,";
$sql.= " pfp.fk_price_expression, pfp.tva_tx, s.nom as name"; $sql.= " pfp.fk_supplier_price_expression, pfp.tva_tx, s.nom as name";
$sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp"; $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp";
$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = pfp.fk_product"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = pfp.fk_product";
$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = pfp.fk_soc"; $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON s.rowid = pfp.fk_soc";
@@ -76,9 +76,9 @@ if ($idprod > 0)
{ {
$objp = $db->fetch_object($result); $objp = $db->fetch_object($result);
if (!empty($objp->fk_price_expression)) { if (!empty($objp->fk_supplier_price_expression)) {
$priceparser = new PriceParser($db); $priceparser = new PriceParser($db);
$price_result = $priceparser->parseProductSupplier($idprod, $objp->fk_price_expression, $objp->quantity, $objp->tva_tx); $price_result = $priceparser->parseProductSupplier($idprod, $objp->fk_supplier_price_expression, $objp->quantity, $objp->tva_tx);
if ($price_result >= 0) { if ($price_result >= 0) {
$objp->fprice = $price_result; $objp->fprice = $price_result;
if ($objp->quantity >= 1) if ($objp->quantity >= 1)

View File

@@ -56,7 +56,7 @@ class ProductFournisseur extends Product
var $fourn_unitprice; var $fourn_unitprice;
var $fourn_tva_npr; var $fourn_tva_npr;
var $fk_price_expression; var $fk_supplier_price_expression;
/** /**
@@ -330,7 +330,7 @@ class ProductFournisseur extends Product
function fetch_product_fournisseur_price($rowid, $ignore_expression = 0) function fetch_product_fournisseur_price($rowid, $ignore_expression = 0)
{ {
$sql = "SELECT pfp.rowid, pfp.price, pfp.quantity, pfp.unitprice, pfp.remise_percent, pfp.remise, pfp.tva_tx, pfp.fk_availability,"; $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_price_expression"; // , pfp.recuperableonly as fourn_tva_npr"; FIXME this field not exist in llx_product_fournisseur_price $sql.= " pfp.fk_soc, pfp.ref_fourn, pfp.fk_product, pfp.charges, pfp.unitcharges, pfp.fk_supplier_price_expression"; // , 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"; $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp";
$sql.= " WHERE pfp.rowid = ".$rowid; $sql.= " WHERE pfp.rowid = ".$rowid;
@@ -350,16 +350,16 @@ class ProductFournisseur extends Product
$this->fourn_remise = $obj->remise; $this->fourn_remise = $obj->remise;
$this->fourn_unitprice = $obj->unitprice; $this->fourn_unitprice = $obj->unitprice;
$this->fourn_unitcharges = $obj->unitcharges; $this->fourn_unitcharges = $obj->unitcharges;
$this->tva_tx = $obj->tva_tx; $this->fourn_tva_tx = $obj->tva_tx;
$this->product_id = $obj->fk_product; // deprecated $this->product_id = $obj->fk_product; // deprecated
$this->fk_product = $obj->fk_product; $this->fk_product = $obj->fk_product;
$this->fk_availability = $obj->fk_availability; $this->fk_availability = $obj->fk_availability;
//$this->fourn_tva_npr = $obj->fourn_tva_npr; // FIXME this field not exist in llx_product_fournisseur_price //$this->fourn_tva_npr = $obj->fourn_tva_npr; // FIXME this field not exist in llx_product_fournisseur_price
$this->fk_price_expression = $obj->fk_price_expression; $this->fk_supplier_price_expression = $obj->fk_supplier_price_expression;
if (empty($ignore_expression) && !empty($this->fk_price_expression)) { if (empty($ignore_expression) && !empty($this->fk_supplier_price_expression)) {
$priceparser = new PriceParser($this->db); $priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProductSupplier($this->fk_product, $this->fk_price_expression, $this->fourn_qty, $this->fourn_tva_tx); $price_result = $priceparser->parseProductSupplier($this->fk_product, $this->fk_supplier_price_expression, $this->fourn_qty, $this->fourn_tva_tx);
if ($price_result >= 0) { if ($price_result >= 0) {
$this->fourn_price = $price_result; $this->fourn_price = $price_result;
//recalculation of unitprice, as probably the price changed... //recalculation of unitprice, as probably the price changed...
@@ -402,7 +402,7 @@ class ProductFournisseur extends Product
global $conf; global $conf;
$sql = "SELECT s.nom as supplier_name, s.rowid as fourn_id,"; $sql = "SELECT s.nom as supplier_name, s.rowid as fourn_id,";
$sql.= " pfp.rowid as product_fourn_pri_id, pfp.ref_fourn, pfp.fk_product as product_fourn_id, pfp.fk_price_expression,"; $sql.= " pfp.rowid as product_fourn_pri_id, pfp.ref_fourn, pfp.fk_product as product_fourn_id, pfp.fk_supplier_price_expression,";
$sql.= " pfp.price, pfp.quantity, pfp.unitprice, pfp.remise_percent, pfp.remise, pfp.tva_tx, pfp.fk_availability, pfp.charges, pfp.unitcharges, pfp.info_bits"; $sql.= " pfp.price, pfp.quantity, pfp.unitprice, pfp.remise_percent, pfp.remise, pfp.tva_tx, pfp.fk_availability, pfp.charges, pfp.unitcharges, pfp.info_bits";
$sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp"; $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp";
$sql.= ", ".MAIN_DB_PREFIX."societe as s"; $sql.= ", ".MAIN_DB_PREFIX."societe as s";
@@ -439,11 +439,11 @@ class ProductFournisseur extends Product
$prodfourn->fk_availability = $record["fk_availability"]; $prodfourn->fk_availability = $record["fk_availability"];
$prodfourn->id = $prodid; $prodfourn->id = $prodid;
$prodfourn->fourn_tva_npr = $record["info_bits"]; $prodfourn->fourn_tva_npr = $record["info_bits"];
$prodfourn->fk_price_expression = $record["fk_price_expression"]; $prodfourn->fk_supplier_price_expression = $record["fk_supplier_price_expression"];
if (!empty($prodfourn->fk_price_expression)) { if (!empty($prodfourn->fk_supplier_price_expression)) {
$priceparser = new PriceParser($this->db); $priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProductSupplier($prodid, $prodfourn->fk_price_expression, $prodfourn->fourn_qty, $prodfourn->fourn_tva_tx); $price_result = $priceparser->parseProductSupplier($prodid, $prodfourn->fk_supplier_price_expression, $prodfourn->fourn_qty, $prodfourn->fourn_tva_tx);
if ($price_result >= 0) { if ($price_result >= 0) {
$prodfourn->fourn_price = $price_result; $prodfourn->fourn_price = $price_result;
$prodfourn->fourn_unitprice = null; //force recalculation of unitprice, as probably the price changed... $prodfourn->fourn_unitprice = null; //force recalculation of unitprice, as probably the price changed...
@@ -501,35 +501,74 @@ class ProductFournisseur extends Product
$sql = "SELECT s.nom as supplier_name, s.rowid as fourn_id,"; $sql = "SELECT s.nom as supplier_name, s.rowid as fourn_id,";
$sql.= " pfp.rowid as product_fourn_price_id, pfp.ref_fourn,"; $sql.= " pfp.rowid as product_fourn_price_id, pfp.ref_fourn,";
$sql.= " pfp.price, pfp.quantity, pfp.unitprice, pfp.tva_tx, pfp.charges, pfp.unitcharges, "; $sql.= " pfp.price, pfp.quantity, pfp.unitprice, pfp.tva_tx, pfp.charges, pfp.unitcharges, ";
$sql.= " pfp.remise, pfp.remise_percent, pfp.fk_price_expression"; $sql.= " pfp.remise, pfp.remise_percent, pfp.fk_supplier_price_expression";
$sql.= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."product_fournisseur_price as pfp"; $sql.= " FROM ".MAIN_DB_PREFIX."societe as s, ".MAIN_DB_PREFIX."product_fournisseur_price as pfp";
$sql.= " WHERE s.entity IN (".getEntity('societe', 1).")"; $sql.= " WHERE s.entity IN (".getEntity('societe', 1).")";
$sql.= " AND pfp.fk_product = ".$prodid; $sql.= " AND pfp.fk_product = ".$prodid;
$sql.= " AND pfp.fk_soc = s.rowid"; $sql.= " AND pfp.fk_soc = s.rowid";
if ($qty > 0) $sql.= " AND pfp.quantity <= ".$qty; if ($qty > 0) $sql.= " AND pfp.quantity <= ".$qty;
$sql.= " ORDER BY pfp.unitprice";
$sql.= $this->db->plimit(1);
dol_syslog(get_class($this)."::find_min_price_product_fournisseur", LOG_DEBUG); dol_syslog(get_class($this)."::find_min_price_product_fournisseur", LOG_DEBUG);
$resql = $this->db->query($sql); $resql = $this->db->query($sql);
if ($resql) if ($resql)
{ {
$record = $this->db->fetch_array($resql); $record_array = array();
//Store each record to array for later search of min
while ($record = $this->db->fetch_array($resql))
{
$record_array[]=$record;
}
if (count($record_array) == 0)
{
$this->db->free($resql);
return 0;
}
else
{
$min = -1;
foreach($record_array as $record)
{
$fourn_price = $record["price"];
$fourn_unitprice = $record["unitprice"];
if (!empty($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"]);
if ($price_result >= 0) {
$fourn_price = price2num($price_result,'MU');
if ($record["quantity"] != 0)
{
$fourn_unitprice = price2num($fourn_price/$record["quantity"],'MU');
}
else
{
$fourn_unitprice = $fourn_price;
}
}
}
if ($fourn_unitprice < $min || $min == -1)
{
$this->product_fourn_price_id = $record["product_fourn_price_id"]; $this->product_fourn_price_id = $record["product_fourn_price_id"];
$this->fourn_ref = $record["ref_fourn"]; $this->fourn_ref = $record["ref_fourn"];
$this->fourn_price = $record["price"]; $this->fourn_price = $fourn_price;
$this->fourn_qty = $record["quantity"]; $this->fourn_qty = $record["quantity"];
$this->fourn_remise_percent = $record["remise_percent"]; $this->fourn_remise_percent = $record["remise_percent"];
$this->fourn_remise = $record["remise"]; $this->fourn_remise = $record["remise"];
$this->fourn_unitprice = $record["unitprice"]; $this->fourn_unitprice = $fourn_unitprice;
$this->fourn_charges = $record["charges"]; $this->fourn_charges = $record["charges"];
$this->fourn_unitcharges = $record["unitcharges"]; $this->fourn_unitcharges = $record["unitcharges"];
$this->fourn_tva_tx = $record["tva_tx"]; $this->fourn_tva_tx = $record["tva_tx"];
$this->fourn_id = $record["fourn_id"]; $this->fourn_id = $record["fourn_id"];
$this->fourn_name = $record["supplier_name"]; $this->fourn_name = $record["supplier_name"];
$this->fk_price_expression = $record["fk_price_expression"]; $this->fk_supplier_price_expression = $record["fk_supplier_price_expression"];
$this->id = $prodid; $this->id = $prodid;
$min = $this->fourn_unitprice;
}
}
}
$this->db->free($resql); $this->db->free($resql);
return 1; return 1;
} }
@@ -541,23 +580,24 @@ class ProductFournisseur extends Product
} }
/** /**
* Sets the price expression * Sets the supplier price expression
* *
* @param string $expression_id Expression * @param int $expression_id Expression
* @return int <0 if KO, >0 if OK * @return int <0 if KO, >0 if OK
*/ */
function setPriceExpression($expression_id) function setSupplierPriceExpression($expression_id)
{ {
global $conf; global $conf;
// Clean parameters // Clean parameters
$this->db->begin(); $this->db->begin();
$expression_id = $expression_id != 0 ? $expression_id : 'NULL';
$sql = "UPDATE ".MAIN_DB_PREFIX."product_fournisseur_price"; $sql = "UPDATE ".MAIN_DB_PREFIX."product_fournisseur_price";
$sql.= " SET fk_price_expression = ".$expression_id; $sql.= " SET fk_supplier_price_expression = ".$expression_id;
$sql.= " WHERE rowid = ".$this->product_fourn_price_id; $sql.= " WHERE rowid = ".$this->product_fourn_price_id;
dol_syslog(get_class($this)."::setPriceExpression", LOG_DEBUG); dol_syslog(get_class($this)."::setSupplierPriceExpression", LOG_DEBUG);
$resql = $this->db->query($sql); $resql = $this->db->query($sql);
if ($resql) if ($resql)

View File

@@ -18,6 +18,7 @@
-- -- VPGSQL8.2 DELETE FROM llx_usergroup_user WHERE fk_user NOT IN (SELECT rowid from llx_user); -- -- VPGSQL8.2 DELETE FROM llx_usergroup_user WHERE fk_user NOT IN (SELECT rowid from llx_user);
-- -- VMYSQL4.1 DELETE FROM llx_usergroup_user WHERE fk_usergroup NOT IN (SELECT rowid from llx_usergroup); -- -- VMYSQL4.1 DELETE FROM llx_usergroup_user WHERE fk_usergroup NOT IN (SELECT rowid from llx_usergroup);
--create table for price expressions and add column in product supplier --create table for price expressions and add column in product supplier
create table llx_c_price_expression create table llx_c_price_expression
( (
@@ -26,6 +27,11 @@ create table llx_c_price_expression
expression varchar(80) NOT NULL expression varchar(80) NOT NULL
)ENGINE=innodb; )ENGINE=innodb;
ALTER TABLE llx_product_fournisseur_price ADD COLUMN fk_supplier_price_expression integer DEFAULT NULL;
ALTER TABLE llx_product ADD COLUMN fk_price_expression integer DEFAULT NULL;
ALTER TABLE llx_product_price ADD COLUMN fk_price_expression integer DEFAULT NULL;
--create table for user conf of printing driver --create table for user conf of printing driver
CREATE TABLE llx_printing CREATE TABLE llx_printing
( (

1
htdocs/install/mysql/tables/llx_product.sql Normal file → Executable file
View File

@@ -76,5 +76,6 @@ create table llx_product
finished tinyint DEFAULT NULL, finished tinyint DEFAULT NULL,
hidden tinyint DEFAULT 0, -- Not used. Deprecated. hidden tinyint DEFAULT 0, -- Not used. Deprecated.
import_key varchar(14), -- Import key import_key varchar(14), -- Import key
fk_price_expression integer, -- Link to the rule for dynamic price calculation
desiredstock integer DEFAULT 0 desiredstock integer DEFAULT 0
)ENGINE=innodb; )ENGINE=innodb;

View File

@@ -39,6 +39,6 @@ create table llx_product_fournisseur_price
tva_tx double(6,3) NOT NULL, tva_tx double(6,3) NOT NULL,
info_bits integer NOT NULL DEFAULT 0, info_bits integer NOT NULL DEFAULT 0,
fk_user integer, fk_user integer,
fk_price_expression integer, -- Link to the rule for dynamic amount calculation fk_supplier_price_expression integer, -- Link to the rule for dynamic price calculation
import_key varchar(14) -- Import key import_key varchar(14) -- Import key
)ENGINE=innodb; )ENGINE=innodb;

1
htdocs/install/mysql/tables/llx_product_price.sql Normal file → Executable file
View File

@@ -38,6 +38,7 @@ create table llx_product_price
fk_user_author integer, fk_user_author integer,
tosell tinyint DEFAULT 1, tosell tinyint DEFAULT 1,
price_by_qty integer NOT NULL DEFAULT 0, price_by_qty integer NOT NULL DEFAULT 0,
fk_price_expression integer, -- Link to the rule for dynamic price calculation
import_key varchar(14) import_key varchar(14)
)ENGINE=innodb; )ENGINE=innodb;

View File

@@ -244,6 +244,9 @@ MinimumPriceLimit=Minimum price can't be lower that %s
MinimumRecommendedPrice=Minimum recommended price is : %s MinimumRecommendedPrice=Minimum recommended price is : %s
PriceExpressionEditor=Price expression editor PriceExpressionEditor=Price expression editor
PriceExpressionSelected=Selected price expression PriceExpressionSelected=Selected price expression
PriceExpressionEditorHelp="price = 2 + 2" or "2 + 2" for setting the price<br>ExtraFields are variables like "#options_myextrafieldkey# * 2"<br>There are special variables like #quantity# and #tva_tx#<br>Use ; to separate expressions PriceExpressionEditorHelp1="price = 2 + 2" or "2 + 2" for setting the price. Use ; to separate expressions
PriceExpressionEditorHelp2=You can access ExtraFields with variables like <b>#options_myextrafieldkey#</b>
PriceExpressionEditorHelp3=In both product/service and supplier prices there are these variables available:<br><b>#tva_tx# #localtax1_tx# #localtax2_tx# #weight# #length# #surface# #price_min#</b>
PriceExpressionEditorHelp4=In product/service price only: <b>#supplier_min_price#</b><br>In supplier prices only: <b>#supplier_quantity# and #supplier_tva_tx#</b>
PriceMode=Price mode PriceMode=Price mode
PriceNumeric=Number PriceNumeric=Number

View File

@@ -22,6 +22,7 @@
*/ */
require_once DOL_DOCUMENT_ROOT.'/includes/evalmath/evalmath.class.php'; require_once DOL_DOCUMENT_ROOT.'/includes/evalmath/evalmath.class.php';
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
require_once DOL_DOCUMENT_ROOT.'/product/class/priceexpression.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/priceexpression.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
@@ -121,12 +122,33 @@ class PriceParser
/** /**
* Calculates price based on expression * Calculates price based on expression
* *
* @param array $values Strings to replaces * @param Product $product The Product object to get information
* @param String $expression The expression to parse * @param String $expression The expression to parse
* @param array $values Strings to replaces
* @return int > 0 if OK, < 1 if KO * @return int > 0 if OK, < 1 if KO
*/ */
public function parseExpression($values, $expression) public function parseExpression($product, $expression, $values)
{ {
//Accessible product values by expressions
$values = array_merge($values, array(
"tva_tx" => $product->tva_tx,
"localtax1_tx" => $product->localtax1_tx,
"localtax2_tx" => $product->localtax2_tx,
"weight" => $product->weight,
"length" => $product->length,
"surface" => $product->surface,
"price_min" => $product->price_min,
));
//Retreive all extrafield for product and add it to values
$extrafields = new ExtraFields($this->db);
$extralabels = $extrafields->fetch_name_optionals_label('product', true);
$product->fetch_optionals($product->id, $extralabels);
foreach ($extrafields->attribute_label as $key=>$label)
{
$values['options_'.$key] = $product->array_options['options_'.$key];
}
//Check if empty //Check if empty
$expression = trim($expression); $expression = trim($expression);
if (empty($expression)) if (empty($expression))
@@ -182,37 +204,79 @@ class PriceParser
return $vars["price"]; return $vars["price"];
} }
/**
* 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
*
* @param Product $product The Product object to get information
* @param int $expression_id The expression to parse
* @param array $extra_values Any aditional values for expression
* @return int > 0 if OK, < 1 if KO
*/
public function parseProduct($product, $extra_values = array())
{
//Get the expression from db
$price_expression = new PriceExpression($this->db);
$res = $price_expression->fetch($product->fk_price_expression);
if ($res < 1) {
$this->error = array(19, null);
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 * Calculates supplier product price based on product id and string expression
* *
* @param int $product_id The Product id to get information * @param int $product_id The Product id to get information
* @param string $expression The expression to parse * @param string $expression The expression to parse
* @param int $quantity Min quantity * @param int $quantity Supplier Min quantity
* @param int $tva_tx VAT rate * @param int $tva_tx Supplier VAT rate
* @param array $extra_values Any aditional values for expression * @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 parseProductSupplierExpression($product_id, $expression, $quantity = null, $tva_tx = null, $extra_values = array()) public function parseProductSupplierExpression($product_id, $expression, $quantity = null, $tva_tx = null, $extra_values = array())
{ {
//Accessible values by expressions //Get the product data
$expression_values = array(
"quantity" => $quantity,
"tva_tx" => $tva_tx,
);
$expression_values = array_merge($expression_values, $extra_values);
//Retreive all extrafield for product and add it to expression_values
$extrafields = new ExtraFields($this->db);
$extralabels = $extrafields->fetch_name_optionals_label('product', true);
$product = new Product($this->db); $product = new Product($this->db);
$product->fetch_optionals($product_id, $extralabels); $product->fetch($product_id, '', '', 1);
foreach($extrafields->attribute_label as $key=>$label)
{
$expression_values['options_'.$key] = $product->array_options['options_'.$key];
}
//Parse the expression and return the price //Accessible values by expressions
return $this->parseExpression($expression_values, $expression); $extra_values = array_merge($extra_values, array(
"supplier_quantity" => $quantity,
"supplier_tva_tx" => $tva_tx,
));
return $this->parseExpression($product, $expression, $extra_values);
} }
/** /**
@@ -227,9 +291,10 @@ class PriceParser
*/ */
public function parseProductSupplier($product_id, $expression_id, $quantity = null, $tva_tx = null, $extra_values = array()) public function parseProductSupplier($product_id, $expression_id, $quantity = null, $tva_tx = null, $extra_values = array())
{ {
//Get the expression from db
$price_expression = new PriceExpression($this->db); $price_expression = new PriceExpression($this->db);
$res = $price_expression->fetch($expression_id); $res = $price_expression->fetch($expression_id);
if ($res > 1) { if ($res < 1) {
$this->error = array(19, null); $this->error = array(19, null);
return -1; return -1;
} }

View File

@@ -10,6 +10,7 @@
* Copyright (C) 2011-2014 Alexandre Spangaro <alexandre.spangaro@gmail.com> * Copyright (C) 2011-2014 Alexandre Spangaro <alexandre.spangaro@gmail.com>
* Copyright (C) 2014 Henry Florian <florian.henry@open-concept.pro> * Copyright (C) 2014 Henry Florian <florian.henry@open-concept.pro>
* Copyright (C) 2014 Philippe Grand <philippe.grand@atoo-net.com> * Copyright (C) 2014 Philippe Grand <philippe.grand@atoo-net.com>
* Copyright (C) 2014 Ion agorria <ion@agorria.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -30,8 +31,9 @@
* \ingroup produit * \ingroup produit
* \brief File of class to manage predefined products or services * \brief File of class to manage predefined products or services
*/ */
require_once DOL_DOCUMENT_ROOT .'/core/class/commonobject.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/productbatch.class.php';
require_once DOL_DOCUMENT_ROOT.'/product/class/priceparser.class.php';
/** /**
@@ -166,6 +168,8 @@ class Product extends CommonObject
//note not visible on orders and invoices //note not visible on orders and invoices
var $note; var $note;
var $fk_price_expression;
/** /**
* Constructor * Constructor
* *
@@ -1112,9 +1116,9 @@ class Product extends CommonObject
// Add new price // Add new price
$sql = "INSERT INTO ".MAIN_DB_PREFIX."product_price(price_level,date_price,fk_product,fk_user_author,price,price_ttc,price_base_type,tosell,tva_tx,recuperableonly,"; $sql = "INSERT INTO ".MAIN_DB_PREFIX."product_price(price_level,date_price,fk_product,fk_user_author,price,price_ttc,price_base_type,tosell,tva_tx,recuperableonly,";
$sql.= " localtax1_tx, localtax2_tx, price_min,price_min_ttc,price_by_qty,entity) "; $sql.= " localtax1_tx, localtax2_tx, price_min,price_min_ttc,price_by_qty,entity,fk_price_expression) ";
$sql.= " VALUES(".($level?$level:1).", '".$this->db->idate($now)."',".$this->id.",".$user->id.",".$this->price.",".$this->price_ttc.",'".$this->price_base_type."',".$this->status.",".$this->tva_tx.",".$this->tva_npr.","; $sql.= " VALUES(".($level?$level:1).", '".$this->db->idate($now)."',".$this->id.",".$user->id.",".$this->price.",".$this->price_ttc.",'".$this->price_base_type."',".$this->status.",".$this->tva_tx.",".$this->tva_npr.",";
$sql.= " ".$this->localtax1_tx.",".$this->localtax2_tx.",".$this->price_min.",".$this->price_min_ttc.",".$this->price_by_qty.",".$conf->entity; $sql.= " ".$this->localtax1_tx.",".$this->localtax2_tx.",".$this->price_min.",".$this->price_min_ttc.",".$this->price_by_qty.",".$conf->entity.",".$this->fk_price_expression;
$sql.= ")"; $sql.= ")";
dol_syslog(get_class($this)."_log_price", LOG_DEBUG); dol_syslog(get_class($this)."_log_price", LOG_DEBUG);
@@ -1176,7 +1180,7 @@ class Product extends CommonObject
// We do select by searching with qty and prodfournprice // We do select by searching with qty and prodfournprice
$sql = "SELECT pfp.rowid, pfp.price as price, pfp.quantity as quantity,"; $sql = "SELECT pfp.rowid, pfp.price as price, pfp.quantity as quantity,";
$sql.= " pfp.fk_product, pfp.ref_fourn, pfp.fk_soc, pfp.tva_tx, pfp.fk_price_expression"; $sql.= " pfp.fk_product, pfp.ref_fourn, pfp.fk_soc, pfp.tva_tx, pfp.fk_supplier_price_expression";
$sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp"; $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp";
$sql.= " WHERE pfp.rowid = ".$prodfournprice; $sql.= " WHERE pfp.rowid = ".$prodfournprice;
if ($qty) $sql.= " AND pfp.quantity <= ".$qty; if ($qty) $sql.= " AND pfp.quantity <= ".$qty;
@@ -1188,9 +1192,9 @@ class Product extends CommonObject
$obj = $this->db->fetch_object($resql); $obj = $this->db->fetch_object($resql);
if ($obj && $obj->quantity > 0) // If found if ($obj && $obj->quantity > 0) // If found
{ {
if (!empty($obj->fk_price_expression)) { if (!empty($obj->fk_supplier_price_expression)) {
$priceparser = new PriceParser($this->db); $priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProductSupplier($obj->fk_product, $obj->fk_price_expression, $obj->quantity, $obj->tva_tx); $price_result = $priceparser->parseProductSupplier($obj->fk_product, $obj->fk_supplier_price_expression, $obj->quantity, $obj->tva_tx);
if ($price_result >= 0) { if ($price_result >= 0) {
$obj->price = $price_result; $obj->price = $price_result;
} }
@@ -1206,7 +1210,7 @@ class Product extends CommonObject
{ {
// We do same select again but searching with qty, ref and id product // We do same select again but searching with qty, ref and id product
$sql = "SELECT pfp.rowid, pfp.price as price, pfp.quantity as quantity, pfp.fk_soc,"; $sql = "SELECT pfp.rowid, pfp.price as price, pfp.quantity as quantity, pfp.fk_soc,";
$sql.= " pfp.fk_product, pfp.ref_fourn as ref_supplier, pfp.tva_tx, pfp.fk_price_expression"; $sql.= " pfp.fk_product, pfp.ref_fourn as ref_supplier, pfp.tva_tx, pfp.fk_supplier_price_expression";
$sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp"; $sql.= " FROM ".MAIN_DB_PREFIX."product_fournisseur_price as pfp";
$sql.= " WHERE pfp.ref_fourn = '".$fourn_ref."'"; $sql.= " WHERE pfp.ref_fourn = '".$fourn_ref."'";
$sql.= " AND pfp.fk_product = ".$product_id; $sql.= " AND pfp.fk_product = ".$product_id;
@@ -1221,9 +1225,9 @@ class Product extends CommonObject
$obj = $this->db->fetch_object($resql); $obj = $this->db->fetch_object($resql);
if ($obj && $obj->quantity > 0) // If found if ($obj && $obj->quantity > 0) // If found
{ {
if (!empty($obj->fk_price_expression)) { if (!empty($obj->fk_supplier_price_expression)) {
$priceparser = new PriceParser($this->db); $priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProductSupplier($obj->fk_product, $obj->fk_price_expression, $obj->quantity, $obj->tva_tx); $price_result = $priceparser->parseProductSupplier($obj->fk_product, $obj->fk_supplier_price_expression, $obj->quantity, $obj->tva_tx);
if ($result >= 0) { if ($result >= 0) {
$obj->price = $price_result; $obj->price = $price_result;
} }
@@ -1397,6 +1401,39 @@ class Product extends CommonObject
return 1; return 1;
} }
/**
* Sets the supplier price expression
*
* @param int $expression_id Expression
* @return int <0 if KO, >0 if OK
*/
function setPriceExpression($expression_id)
{
global $conf;
// Clean parameters
$this->db->begin();
$expression_id = $expression_id != 0 ? $expression_id : 'NULL';
$sql = "UPDATE ".MAIN_DB_PREFIX."product";
$sql.= " SET fk_price_expression = ".$expression_id;
$sql.= " WHERE rowid = ".$this->id;
dol_syslog(get_class($this)."::setPriceExpression", LOG_DEBUG);
$resql = $this->db->query($sql);
if ($resql)
{
$this->db->commit();
return 1;
}
else
{
$this->error=$this->db->error()." sql=".$sql;
$this->db->rollback();
return -1;
}
}
/** /**
* Load a product in memory from database * Load a product in memory from database
@@ -1404,9 +1441,10 @@ class Product extends CommonObject
* @param int $id Id of product/service to load * @param int $id Id of product/service to load
* @param string $ref Ref of product/service to load * @param string $ref Ref of product/service to load
* @param string $ref_ext Ref ext of product/service to load * @param string $ref_ext Ref ext of product/service to load
* @param int $ignore_expression Ignores the math expression for calculating price and uses the db value instead
* @return int <0 if KO, 0 if not found, >0 if OK * @return int <0 if KO, 0 if not found, >0 if OK
*/ */
function fetch($id='',$ref='',$ref_ext='') function fetch($id='', $ref='', $ref_ext='', $ignore_expression = 0)
{ {
include_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; include_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
@@ -1428,7 +1466,7 @@ class Product extends CommonObject
$sql.= " weight, weight_units, length, length_units, surface, surface_units, volume, volume_units, barcode, fk_barcode_type, finished,"; $sql.= " weight, weight_units, length, length_units, surface, surface_units, volume, volume_units, barcode, fk_barcode_type, finished,";
$sql.= " accountancy_code_buy, accountancy_code_sell, stock, pmp,"; $sql.= " accountancy_code_buy, accountancy_code_sell, stock, pmp,";
$sql.= " datec, tms, import_key, entity, desiredstock, tobatch"; $sql.= " datec, tms, import_key, entity, desiredstock, tobatch";
$sql.= " ,ref_ext"; $sql.= " ,ref_ext, fk_price_expression";
$sql.= " FROM ".MAIN_DB_PREFIX."product"; $sql.= " FROM ".MAIN_DB_PREFIX."product";
if ($id) $sql.= " WHERE rowid = ".$this->db->escape($id); if ($id) $sql.= " WHERE rowid = ".$this->db->escape($id);
else else
@@ -1505,6 +1543,7 @@ class Product extends CommonObject
$this->entity = $obj->entity; $this->entity = $obj->entity;
$this->ref_ext = $obj->ref_ext; $this->ref_ext = $obj->ref_ext;
$this->fk_price_expression = $obj->fk_price_expression;
$this->db->free($resql); $this->db->free($resql);
@@ -1640,6 +1679,19 @@ class Product extends CommonObject
} }
} }
if (!empty($this->fk_price_expression) && empty($ignore_expression))
{
$priceparser = new PriceParser($this->db);
$price_result = $priceparser->parseProduct($this);
if ($price_result >= 0)
{
$this->price = $price_result;
//Calculate the VAT
$this->price_ttc = price2num($this->price) * (1 + ($this->tva_tx / 100));
$this->price_ttc = price2num($this->price_ttc,'MU');
}
}
// We should not load stock at each fetch. If someone need stock, he must call load_stock after fetch. // We should not load stock at each fetch. If someone need stock, he must call load_stock after fetch.
//$res=$this->load_stock(); //$res=$this->load_stock();

View File

@@ -37,6 +37,7 @@ $title = GETPOST('expression_title', 'alpha');
$expression = GETPOST('expression'); $expression = GETPOST('expression');
$tab = GETPOST('tab', 'alpha'); $tab = GETPOST('tab', 'alpha');
$tab = (!empty($tab)) ? $tab : 'card'; $tab = (!empty($tab)) ? $tab : 'card';
$tab = strtolower($tab);
// Security check // Security check
$result=restrictedArea($user,'produit|service&fournisseur',$id,'product&product','','','rowid'); $result=restrictedArea($user,'produit|service&fournisseur',$id,'product&product','','','rowid');
@@ -82,6 +83,7 @@ if ($action == 'add')
if ($result > 0) //created successfully, set the eid to newly created entry if ($result > 0) //created successfully, set the eid to newly created entry
{ {
$eid = $price_expression->id; $eid = $price_expression->id;
setEventMessage($langs->trans("RecordSaved"));
} }
else else
{ {
@@ -123,6 +125,10 @@ if ($action == 'update')
{ {
setEventMessage("update: ".$price_expression->error, 'errors'); setEventMessage("update: ".$price_expression->error, 'errors');
} }
else
{
setEventMessage($langs->trans("RecordSaved"));
}
} }
} }
else if ($result < 0) else if ($result < 0)
@@ -179,7 +185,8 @@ print '<input class="flat" name="expression_title" size="15" value="'.($price_ex
print '</td></tr>'; print '</td></tr>';
//Price expression editor //Price expression editor
print '<tr><td class="fieldrequired">'.$form->textwithpicto($langs->trans("PriceExpressionEditor"),$langs->trans("PriceExpressionEditorHelp"),1).'</td><td>'; $help_text = $langs->trans("PriceExpressionEditorHelp1").'<br><br>'.$langs->trans("PriceExpressionEditorHelp2").'<br><br>'.$langs->trans("PriceExpressionEditorHelp3").'<br><br>'.$langs->trans("PriceExpressionEditorHelp4");
print '<tr><td class="fieldrequired">'.$form->textwithpicto($langs->trans("PriceExpressionEditor"),$help_text,1).'</td><td>';
require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
$doleditor=new DolEditor('expression',isset($price_expression->expression)?$price_expression->expression:'','',300,'','',false,false,false,4,80); $doleditor=new DolEditor('expression',isset($price_expression->expression)?$price_expression->expression:'','',300,'','',false,false,false,4,80);
$doleditor->Create(); $doleditor->Create();
@@ -202,7 +209,7 @@ print '</center>';
print '</form>'; print '</form>';
// This code reloads the page depending of selected option, goes back in history when back is pressed // This code reloads the page depending of selected option, goes to page selected by tab when back is pressed
print '<script type="text/javascript"> print '<script type="text/javascript">
jQuery(document).ready(run); jQuery(document).ready(run);
function run() { function run() {
@@ -210,7 +217,7 @@ print '<script type="text/javascript">
jQuery("#expression_selection").change(on_change); jQuery("#expression_selection").change(on_change);
} }
function on_click() { function on_click() {
window.location = "'.str_replace('expression.php', $tab.'.php', $_SERVER["PHP_SELF"]).'?id='.$id.'"; window.location = "'.str_replace('expression.php', $tab.'.php', $_SERVER["PHP_SELF"]).'?id='.$id.($tab == 'price' ? '&action=edit_price' : '').'";
} }
function on_change() { function on_change() {
window.location = "'.$_SERVER["PHP_SELF"].'?id='.$id.'&tab='.$tab.'&eid=" + $("#expression_selection").attr("value"); window.location = "'.$_SERVER["PHP_SELF"].'?id='.$id.'&tab='.$tab.'&eid=" + $("#expression_selection").attr("value");

View File

@@ -97,8 +97,8 @@ if (empty($reshook))
} }
} }
if ($action == 'updateprice' && GETPOST('cancel') <> $langs->trans("Cancel")) if ($action == 'updateprice' && GETPOST('cancel') <> $langs->trans("Cancel"))
{ {
$id_fourn=GETPOST("id_fourn"); $id_fourn=GETPOST("id_fourn");
if (empty($id_fourn)) $id_fourn=GETPOST("search_id_fourn"); if (empty($id_fourn)) $id_fourn=GETPOST("search_id_fourn");
$ref_fourn=GETPOST("ref_fourn"); $ref_fourn=GETPOST("ref_fourn");
@@ -108,7 +108,7 @@ if (empty($reshook))
$npr = preg_match('/\*/', $_POST['tva_tx']) ? 1 : 0 ; $npr = preg_match('/\*/', $_POST['tva_tx']) ? 1 : 0 ;
$tva_tx = str_replace('*','', GETPOST('tva_tx','alpha')); $tva_tx = str_replace('*','', GETPOST('tva_tx','alpha'));
$tva_tx = price2num($tva_tx); $tva_tx = price2num($tva_tx);
$price_expression = GETPOST('eid', 'int') == 0 ? 'NULL' : GETPOST('eid', 'int'); //Discard expression if not in expression mode $price_expression = GETPOST('eid', 'int') ? GETPOST('eid', 'int') : ''; // Discard expression if not in expression mode
if ($tva_tx == '') if ($tva_tx == '')
{ {
@@ -132,7 +132,8 @@ if (empty($reshook))
} }
if ($_POST["price"] < 0 || $_POST["price"] == '') if ($_POST["price"] < 0 || $_POST["price"] == '')
{ {
if ($price_expression == 'NULL') { //This is not because of using expression instead of numeric price if ($price_expression === '') // Return error of missing price only if price_expression not set
{
$error++; $error++;
setEventMessage($langs->trans("ErrorFieldRequired",$langs->transnoentities("Price")), 'errors'); setEventMessage($langs->trans("ErrorFieldRequired",$langs->transnoentities("Price")), 'errors');
} }
@@ -189,7 +190,8 @@ if (empty($reshook))
} }
else else
{ {
if ($price_expression != 'NULL') { if ($price_expression !== '')
{
//Check the expression validity by parsing it //Check the expression validity by parsing it
$priceparser = new PriceParser($db); $priceparser = new PriceParser($db);
$price_result = $priceparser->parseProductSupplier($id, $price_expression, $quantity, $tva_tx); $price_result = $priceparser->parseProductSupplier($id, $price_expression, $quantity, $tva_tx);
@@ -410,7 +412,7 @@ if ($id || $ref)
foreach ($price_expression->list_price_expression() as $entry) { foreach ($price_expression->list_price_expression() as $entry) {
$price_expression_list[$entry->id] = $entry->title; $price_expression_list[$entry->id] = $entry->title;
} }
$price_expression_preselection = GETPOST('eid') ? GETPOST('eid') : ($product->fk_price_expression ? $product->fk_price_expression : '0'); $price_expression_preselection = GETPOST('eid') ? GETPOST('eid') : ($product->fk_supplier_price_expression ? $product->fk_supplier_price_expression : '0');
print $form->selectarray('eid', $price_expression_list, $price_expression_preselection); print $form->selectarray('eid', $price_expression_list, $price_expression_preselection);
print '&nbsp; <div id="expression_editor" class="button">'.$langs->trans("PriceExpressionEditor").'</div>'; print '&nbsp; <div id="expression_editor" class="button">'.$langs->trans("PriceExpressionEditor").'</div>';
print '</td></tr>'; print '</td></tr>';
@@ -436,7 +438,7 @@ if ($id || $ref)
} }
// Price qty min // Price qty min
print '<tr id="price_numeric"><td class="fieldrequired">'.$langs->trans("PriceQtyMin").'</td>'; print '<tr><td class="fieldrequired">'.$langs->trans("PriceQtyMin").'</td>';
print '<td><input class="flat" name="price" size="8" value="'.(GETPOST('price')?price(GETPOST('price')):(isset($product->fourn_price)?price($product->fourn_price):'')).'">'; print '<td><input class="flat" name="price" size="8" value="'.(GETPOST('price')?price(GETPOST('price')):(isset($product->fourn_price)?price($product->fourn_price):'')).'">';
print '&nbsp;'; print '&nbsp;';
print $form->select_PriceBaseType((GETPOST('price_base_type')?GETPOST('price_base_type'):$product->price_base_type), "price_base_type"); print $form->select_PriceBaseType((GETPOST('price_base_type')?GETPOST('price_base_type'):$product->price_base_type), "price_base_type");

17
htdocs/product/index.php Normal file → Executable file
View File

@@ -27,6 +27,7 @@
require '../main.inc.php'; require '../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
require_once DOL_DOCUMENT_ROOT.'/product/class/priceparser.class.php';
$type=GETPOST("type",'int'); $type=GETPOST("type",'int');
if ($type =='' && !$user->rights->produit->lire) $type='1'; // Force global page on service page only if ($type =='' && !$user->rights->produit->lire) $type='1'; // Force global page on service page only
@@ -232,7 +233,7 @@ print '</div><div class="fichetwothirdright"><div class="ficheaddleft">';
* Last modified products * Last modified products
*/ */
$max=15; $max=15;
$sql = "SELECT p.rowid, p.label, p.price, p.ref, p.fk_product_type, p.tosell, p.tobuy,"; $sql = "SELECT p.rowid, p.label, p.price, p.ref, p.fk_product_type, p.tosell, p.tobuy, p.fk_price_expression,";
$sql.= " p.tms as datem"; $sql.= " p.tms as datem";
$sql.= " FROM ".MAIN_DB_PREFIX."product as p"; $sql.= " FROM ".MAIN_DB_PREFIX."product as p";
$sql.= " WHERE p.entity IN (".getEntity($product_static->element, 1).")"; $sql.= " WHERE p.entity IN (".getEntity($product_static->element, 1).")";
@@ -298,9 +299,17 @@ if ($result)
// Sell price // Sell price
if (empty($conf->global->PRODUIT_MULTIPRICES)) if (empty($conf->global->PRODUIT_MULTIPRICES))
{ {
if (!empty($objp->fk_price_expression)) {
$product = new Product($db);
$product->fetch($objp->rowid);
$priceparser = new PriceParser($db);
$price_result = $priceparser->parseProduct($product);
if ($price_result >= 0) {
$objp->price = $price_result;
}
}
print '<td align="right">'; print '<td align="right">';
if ($objp->price_base_type == 'TTC') print price($objp->price_ttc).' '.$langs->trans("TTC"); print price($objp->price).' '.$langs->trans("HT");
else print price($objp->price).' '.$langs->trans("HT");
print '</td>'; print '</td>';
} }
print '<td align="right" class="nowrap">'; print '<td align="right" class="nowrap">';
@@ -313,7 +322,7 @@ if ($result)
$i++; $i++;
} }
$db->free(); $db->free($result);
print "</table>"; print "</table>";
} }

93
htdocs/product/price.php Normal file → Executable file
View File

@@ -7,6 +7,7 @@
* Copyright (C) 2014 Florian Henry <florian.henry@open-concept.pro> * Copyright (C) 2014 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2014 Juanjo Menent <jmenent@2byte.es> * Copyright (C) 2014 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2014 Philippe Grand <philippe.grand@atoo-net.com> * Copyright (C) 2014 Philippe Grand <philippe.grand@atoo-net.com>
* Copyright (C) 2014 Ion agorria <ion@agorria.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -30,6 +31,8 @@
require '../main.inc.php'; require '../main.inc.php';
require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php'; require_once DOL_DOCUMENT_ROOT . '/core/lib/product.lib.php';
require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php'; require_once DOL_DOCUMENT_ROOT . '/product/class/product.class.php';
require_once DOL_DOCUMENT_ROOT . '/product/class/priceexpression.class.php';
require_once DOL_DOCUMENT_ROOT . '/product/class/priceparser.class.php';
if (! empty($conf->global->PRODUIT_CUSTOMER_PRICES)) { if (! empty($conf->global->PRODUIT_CUSTOMER_PRICES)) {
require_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php'; require_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php';
@@ -72,6 +75,7 @@ if ($action == 'update_price' && ! GETPOST("cancel") && ($user->rights->produit-
$error=0; $error=0;
$maxpricesupplier = $object->min_recommended_price(); $maxpricesupplier = $object->min_recommended_price();
$object->fk_price_expression = empty(GETPOST('eid', 'int')) ? 0 : GETPOST('eid', 'int'); //0 discards expression
// MultiPrix // MultiPrix
if (! empty($conf->global->PRODUIT_MULTIPRICES)) if (! empty($conf->global->PRODUIT_MULTIPRICES))
@@ -114,9 +118,34 @@ if ($action == 'update_price' && ! GETPOST("cancel") && ($user->rights->produit-
$action='edit_price'; $action='edit_price';
} }
if ($newprice < $newprice_min && ! empty($object->fk_price_expression)) {
$newprice = $newprice_min; //Set price same as min, the user will not see the
}
if ($object->updatePrice($newprice, $newpricebase, $user, $newvat, $newprice_min, $level, $newnpr, $newpsq) > 0) { if ($object->updatePrice($newprice, $newpricebase, $user, $newvat, $newprice_min, $level, $newnpr, $newpsq) > 0) {
if ($object->fk_price_expression != 0) {
//Check the expression validity by parsing it
$priceparser = new PriceParser($db);
$price_result = $priceparser->parseProduct($object);
if ($price_result < 0) { //Expression is not valid
$error++;
$action='edit_price';
setEventMessage($priceparser->translatedError(), 'errors');
}
}
if (empty($error) && ! empty($conf->dynamicprices->enabled)) {
$ret=$object->setPriceExpression($object->fk_price_expression);
if ($ret < 0)
{
$error++;
$action='edit_price';
setEventMessage($object->error, 'errors');
}
}
if (empty($error)) {
$action = ''; $action = '';
setEventMessage($langs->trans("RecordSaved")); setEventMessage($langs->trans("RecordSaved"));
}
} else { } else {
$action = 'edit_price'; $action = 'edit_price';
setEventMessage($object->error, 'errors'); setEventMessage($object->error, 'errors');
@@ -653,15 +682,51 @@ if ($action == 'edit_price' && ($user->rights->produit->creer || $user->rights->
print '</td>'; print '</td>';
print '</tr>'; print '</tr>';
//Only show price mode and expression selector if module is enabled
if (! empty($conf->dynamicprices->enabled)) {
// Price mode selector
print '<tr><td>'.$langs->trans("PriceMode").'</td><td>';
$price_expression = new PriceExpression($db);
$price_expression_list = array(0 => $langs->trans("PriceNumeric")); //Put the numeric mode as first option
foreach ($price_expression->list_price_expression() as $entry) {
$price_expression_list[$entry->id] = $entry->title;
}
$price_expression_preselection = GETPOST('eid') ? GETPOST('eid') : ($object->fk_price_expression ? $object->fk_price_expression : '0');
print $form->selectarray('eid', $price_expression_list, $price_expression_preselection);
print '&nbsp; <div id="expression_editor" class="button">'.$langs->trans("PriceExpressionEditor").'</div>';
print '</td></tr>';
// This code hides the numeric price input if is not selected, loads the editor page if editor button is pressed
print '<script type="text/javascript">
jQuery(document).ready(run);
function run() {
jQuery("#expression_editor").click(on_click);
jQuery("#eid").change(on_change);
on_change();
}
function on_click() {
window.location = "'.DOL_URL_ROOT.'/product/expression.php?id='.$id.'&tab=price&eid=" + $("#eid").attr("value");
}
function on_change() {
if ($("#eid").attr("value") == 0) {
jQuery("#price_numeric").show();
} else {
jQuery("#price_numeric").hide();
}
}
</script>';
}
// Price // Price
print '<tr><td width="20%">'; $product = new Product($db);
$product->fetch($id, $ref, '', 1); //Ignore the math expression when getting the price
print '<tr id="price_numeric"><td width="20%">';
$text = $langs->trans('SellingPrice'); $text = $langs->trans('SellingPrice');
print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", $conf->global->MAIN_MAX_DECIMALS_UNIT), 1, 1); print $form->textwithpicto($text, $langs->trans("PrecisionUnitIsLimitedToXDecimals", $conf->global->MAIN_MAX_DECIMALS_UNIT), 1, 1);
print '</td><td>'; print '</td><td>';
if ($object->price_base_type == 'TTC') { if ($object->price_base_type == 'TTC') {
print '<input name="price" size="10" value="' . price($object->price_ttc) . '">'; print '<input name="price" size="10" value="' . price($product->price_ttc) . '">';
} else { } else {
print '<input name="price" size="10" value="' . price($object->price) . '">'; print '<input name="price" size="10" value="' . price($product->price) . '">';
} }
print '</td></tr>'; print '</td></tr>';
@@ -745,7 +810,7 @@ if ($action == 'edit_price' && ($user->rights->produit->creer || $user->rights->
// Liste des evolutions du prix // Liste des evolutions du prix
$sql = "SELECT p.rowid, p.price, p.price_ttc, p.price_base_type, p.tva_tx, p.recuperableonly,"; $sql = "SELECT p.rowid, p.price, p.price_ttc, p.price_base_type, p.tva_tx, p.recuperableonly,";
$sql .= " p.price_level, p.price_min, p.price_min_ttc,p.price_by_qty,"; $sql .= " p.price_level, p.price_min, p.price_min_ttc,p.price_by_qty,";
$sql .= " p.date_price as dp, u.rowid as user_id, u.login"; $sql .= " p.date_price as dp, p.fk_price_expression, u.rowid as user_id, u.login";
$sql .= " FROM " . MAIN_DB_PREFIX . "product_price as p,"; $sql .= " FROM " . MAIN_DB_PREFIX . "product_price as p,";
$sql .= " " . MAIN_DB_PREFIX . "user as u"; $sql .= " " . MAIN_DB_PREFIX . "user as u";
$sql .= " WHERE fk_product = " . $object->id; $sql .= " WHERE fk_product = " . $object->id;
@@ -790,6 +855,9 @@ if ($result) {
print '<td align="right">' . $langs->trans("VAT") . '</td>'; print '<td align="right">' . $langs->trans("VAT") . '</td>';
print '<td align="right">' . $langs->trans("HT") . '</td>'; print '<td align="right">' . $langs->trans("HT") . '</td>';
print '<td align="right">' . $langs->trans("TTC") . '</td>'; print '<td align="right">' . $langs->trans("TTC") . '</td>';
if (! empty($conf->dynamicprices->enabled)) {
print '<td align="right">' . $langs->trans("PriceExpressionSelected") . '</td>';
}
print '<td align="right">' . $langs->trans("MinPrice") . ' ' . $langs->trans("HT") . '</td>'; print '<td align="right">' . $langs->trans("MinPrice") . ' ' . $langs->trans("HT") . '</td>';
print '<td align="right">' . $langs->trans("MinPrice") . ' ' . $langs->trans("TTC") . '</td>'; print '<td align="right">' . $langs->trans("MinPrice") . ' ' . $langs->trans("TTC") . '</td>';
print '<td align="right">' . $langs->trans("ChangedBy") . '</td>'; print '<td align="right">' . $langs->trans("ChangedBy") . '</td>';
@@ -819,8 +887,25 @@ if ($result) {
print '<td align="center">' . $langs->trans($objp->price_base_type) . "</td>"; print '<td align="center">' . $langs->trans($objp->price_base_type) . "</td>";
print '<td align="right">' . vatrate($objp->tva_tx, true, $objp->recuperableonly) . "</td>"; print '<td align="right">' . vatrate($objp->tva_tx, true, $objp->recuperableonly) . "</td>";
//Price
if (! empty($objp->fk_price_expression) && ! empty($conf->dynamicprices->enabled))
{
$price_expression = new PriceExpression($db);
$res = $price_expression->fetch($objp->fk_price_expression);
$title = $price_expression->title;
print '<td align="right"></td>';
print '<td align="right"></td>';
print '<td align="right">' . $title . "</td>";
}
else
{
print '<td align="right">' . price($objp->price) . "</td>"; print '<td align="right">' . price($objp->price) . "</td>";
print '<td align="right">' . price($objp->price_ttc) . "</td>"; print '<td align="right">' . price($objp->price_ttc) . "</td>";
if (! empty($conf->dynamicprices->enabled)) { //Only if module is enabled
print '<td align="right"></td>';
}
}
print '<td align="right">' . price($objp->price_min) . '</td>'; print '<td align="right">' . price($objp->price_min) . '</td>';
print '<td align="right">' . price($objp->price_min_ttc) . '</td>'; print '<td align="right">' . price($objp->price_min_ttc) . '</td>';