diff --git a/htdocs/core/class/html.form.class.php b/htdocs/core/class/html.form.class.php index 55bd1f37093..ae7b0e2d1e8 100644 --- a/htdocs/core/class/html.form.class.php +++ b/htdocs/core/class/html.form.class.php @@ -1643,7 +1643,7 @@ class Form if ($result) { require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; - require_once DOL_DOCUMENT_ROOT.'/product/class/priceparser.class.php'; + require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php'; $num = $this->db->num_rows($result); $out.=''; + print ''; + print ''; + + //Table + print '
'; + //Code + print ''; + print ''; + print ''; + print ''; + //Description + print ''; + print ''; + print ''; + print ''; + //Value + print ''; + print ''; + print ''; + print ''; + print '
'.$langs->trans("Code").'
'.$langs->trans("Description").'
'.$langs->trans("Value").'
'; + + //Form Buttons + print '
'; + print '  '; + print ''; + print '
'; + print ''; +} else { + //Action Buttons + print '
'; + print ''.$langs->trans("Add").''; + print '
'; + //Separator is only need for updaters table is showed after buttons + print '

'; +} + +//Updaters table +if ($action != 'create_variable' && $action != 'edit_variable') { + print $langs->trans("GlobalVariableUpdaters"); + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; //Space for buttons + print ''; + + $var=True; + foreach ($price_updaters->listUpdaters() as $i=>$entry) { + $code = ""; + if ($entry->fk_variable > 0) { + $res = $price_globals->fetch($entry->fk_variable); + if ($res > 0) { + $code = $price_globals->code; + } + } + $var = !$var; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + print ''; + } + print '
'.$langs->trans("Code").''.$langs->trans("Description").''.$langs->trans("Type").''.$langs->trans("Parameters").''.$langs->trans("UpdateInterval").''.$langs->trans("LastUpdated").' 
'.$code.''.$entry->description.''.$langs->trans("GlobalVariableUpdaterType".$entry->type).''.$entry->parameters.''.$entry->update_interval.''.$entry->getLastUpdated().'id.'">'.img_edit().'  '; + print 'id.'">'.img_delete().'
'; +} + +//Updater editor +if ($action == 'create_updater' || $action == 'edit_updater') { + //Form + print '
'; + print ''; + print ''; + print ''; + + //Table + print '
'; + //Code + print ''; + print ''; + //Description + print ''; + print ''; + print ''; + print ''; + //Type + print ''; + print ''; + //Parameters + print ''; + $help = $langs->trans("GlobalVariableUpdaterHelp".$type).'
'.$langs->trans("GlobalVariableUpdaterHelpFormat".$type).''; + print ''; + print ''; + //Interval + print ''; + print ''; + print ''; + print ''; + print '
'.$langs->trans("Code").''; + $globals_list = array(); + foreach ($price_globals->listGlobalVariables() as $entry) { + $globals_list[$entry->id]=$entry->code; + } + print $form->selectarray('fk_variable', $globals_list, (empty($price_updaters->fk_variable)?0:$price_updaters->fk_variable)); + print '
'.$langs->trans("Description").'
'.$langs->trans("Type").''; + $type = empty($price_updaters->type)?0:$price_updaters->type; + $type_list = array(); + foreach ($price_updaters->types as $val) { + $type_list[$val] = $langs->trans("GlobalVariableUpdaterType".$val); + } + print $form->selectarray('type', $type_list, $type); + // This code submits form when type is changed + print ''; + print '
'.$form->textwithpicto($langs->trans("Parameters"),$help,1).''; + require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; + $doleditor=new DolEditor('parameters',empty($price_updaters->parameters)?'':$price_updaters->parameters,'',300,'','',false,false,false,10,80); + $doleditor->Create(); + print '
'.$langs->trans("UpdateInterval").'
'; + + //Form Buttons + print '
'; + print '  '; + print ''; + print '
'; + print '
'; +} else { + //Action Buttons + print '
'; + print ''.$langs->trans("Add").''; + print '
'; +} + +llxFooter(); +$db->close(); diff --git a/htdocs/product/class/product.class.php b/htdocs/product/class/product.class.php index 31f652a18ab..f66a007e93a 100755 --- a/htdocs/product/class/product.class.php +++ b/htdocs/product/class/product.class.php @@ -1205,7 +1205,7 @@ class Product extends CommonObject { if (!empty($obj->fk_supplier_price_expression)) { - require_once DOL_DOCUMENT_ROOT.'/product/class/priceparser.class.php'; + 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); if ($price_result >= 0) { @@ -1240,7 +1240,7 @@ class Product extends CommonObject { if (!empty($obj->fk_supplier_price_expression)) { - require_once DOL_DOCUMENT_ROOT.'/product/class/priceparser.class.php'; + 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); if ($result >= 0) { @@ -1696,7 +1696,7 @@ class Product extends CommonObject if (!empty($this->fk_price_expression) && empty($ignore_expression)) { - require_once DOL_DOCUMENT_ROOT.'/product/class/priceparser.class.php'; + require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php'; $priceparser = new PriceParser($this->db); $price_result = $priceparser->parseProduct($this); if ($price_result >= 0) diff --git a/htdocs/product/class/priceexpression.class.php b/htdocs/product/dynamic_price/class/price_expression.class.php similarity index 92% rename from htdocs/product/class/priceexpression.class.php rename to htdocs/product/dynamic_price/class/price_expression.class.php index c5e24d3a228..bd4fa62692f 100644 --- a/htdocs/product/class/priceexpression.class.php +++ b/htdocs/product/dynamic_price/class/price_expression.class.php @@ -1,7 +1,7 @@ * Copyright (C) 2014 Juanjo Menent -/* Copyright (C) 2014 Ion Agorria +/* Copyright (C) 2015 Ion Agorria * * 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 @@ -18,7 +18,7 @@ */ /** - * \file htdocs/product/class/priceexpression.class.php + * \file htdocs/product/dynamic_price/class/price_expression.class.php * \ingroup product * \brief Class for accessing price expression table */ @@ -35,6 +35,7 @@ class PriceExpression var $id; var $title; var $expression; + public $table_element = "c_price_expression"; /** * Constructor @@ -57,14 +58,14 @@ class PriceExpression */ function create($user, $notrigger=0) { - $error=0; + $error=0; // Clean parameters if (isset($this->title)) $this->title=trim($this->title); if (isset($this->expression)) $this->expression=trim($this->expression); // Insert request - $sql = "INSERT INTO ".MAIN_DB_PREFIX."c_price_expression ("; + $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." ("; $sql.= "title, expression"; $sql.= ") VALUES ("; $sql.= " ".(isset($this->title)?"'".$this->db->escape($this->title)."'":"''").","; @@ -121,7 +122,7 @@ class PriceExpression function fetch($id) { $sql = "SELECT title, expression"; - $sql.= " FROM ".MAIN_DB_PREFIX."c_price_expression"; + $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element; $sql.= " WHERE rowid = ".$id; dol_syslog(get_class($this)."::fetch"); @@ -133,8 +134,8 @@ class PriceExpression { $this->id = $id; $this->title = $obj->title; - $this->expression = $obj->expression; - return 1; + $this->expression = $obj->expression; + return 1; } else { @@ -156,10 +157,10 @@ class PriceExpression function list_price_expression() { $sql = "SELECT rowid, title, expression"; - $sql.= " FROM ".MAIN_DB_PREFIX."c_price_expression"; + $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element; $sql.= " ORDER BY title"; - dol_syslog(get_class($this)."::list_price_expression"); + dol_syslog(get_class($this)."::list_price_expression"); $resql=$this->db->query($sql); if ($resql) { @@ -194,7 +195,7 @@ class PriceExpression function find_title($title) { $sql = "SELECT rowid"; - $sql.= " FROM ".MAIN_DB_PREFIX."c_price_expression"; + $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element; $sql.= " WHERE title = '".$this->db->escape($title)."'"; dol_syslog(get_class($this)."::find_title"); @@ -235,7 +236,7 @@ class PriceExpression if (isset($this->expression)) $this->expression=trim($this->expression); // Update request - $sql = "UPDATE ".MAIN_DB_PREFIX."c_price_expression SET"; + $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET"; $sql.= " title = ".(isset($this->title)?"'".$this->db->escape($this->title)."'":"''").","; $sql.= " expression = ".(isset($this->expression)?"'".$this->db->escape($this->expression)."'":"''").""; $sql.= " WHERE rowid = ".$this->id; @@ -309,7 +310,7 @@ class PriceExpression if (! $error) { - $sql = "DELETE FROM ".MAIN_DB_PREFIX."c_price_expression"; + $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element; $sql.= " WHERE rowid = ".$rowid; dol_syslog(get_class($this)."::delete"); diff --git a/htdocs/product/dynamic_price/class/price_global_variable.class.php b/htdocs/product/dynamic_price/class/price_global_variable.class.php new file mode 100644 index 00000000000..c65e694dfb9 --- /dev/null +++ b/htdocs/product/dynamic_price/class/price_global_variable.class.php @@ -0,0 +1,335 @@ + + * Copyright (C) 2014 Juanjo Menent +/* Copyright (C) 2015 Ion Agorria + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/product/dynamic_price/class/price_global_variable.class.php + * \ingroup product + * \brief Class for accessing price global variables table + */ + + +/** + * Class for accesing price global variables table + */ +class PriceGlobalVariable +{ + var $db; //!< To store db handler + var $error; //!< To return error code (or message) + var $errors=array(); //!< To return several error codes (or messages) + var $id; + var $code; + var $description; + var $value; + public $table_element = "c_price_global_variable"; + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + function __construct($db) + { + $this->db = $db; + return 1; + } + + + /** + * Create object into database + * + * @param User $user User that creates + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, Id of created object if OK + */ + function create($user, $notrigger=0) + { + $error=0; + + $this->checkParameters(); + + // Insert request + $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." ("; + $sql.= "code, description, value"; + $sql.= ") VALUES ("; + $sql.= " ".(isset($this->code)?"'".$this->db->escape($this->code)."'":"''").","; + $sql.= " ".(isset($this->description)?"'".$this->db->escape($this->description)."'":"''").","; + $sql.= " ".$this->value; + $sql.= ")"; + + $this->db->begin(); + + dol_syslog(get_class($this)."::create", LOG_DEBUG); + $resql=$this->db->query($sql); + if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); } + + if (! $error) + { + $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element); + + if (! $notrigger) + { + // Uncomment this and change MYOBJECT to your own tag if you + // want this action calls a trigger. + + //// Call triggers + //$result=$this->call_trigger('MYOBJECT_CREATE',$user); + //if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail} + //// End call triggers + } + } + + // Commit or rollback + if ($error) + { + foreach($this->errors as $errmsg) + { + dol_syslog(__METHOD__." ".$errmsg, LOG_ERR); + $this->error.=($this->error?', '.$errmsg:$errmsg); + } + $this->db->rollback(); + return -1*$error; + } + else + { + $this->db->commit(); + return $this->id; + } + } + + + /** + * Load object in memory from the database + * + * @param int $id Id object + * @return int < 0 if KO, 0 if OK but not found, > 0 if OK + */ + function fetch($id) + { + $sql = "SELECT code, description, value"; + $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element; + $sql.= " WHERE rowid = ".$id; + + dol_syslog(get_class($this)."::fetch"); + $resql=$this->db->query($sql); + if ($resql) + { + $obj = $this->db->fetch_object($resql); + if ($obj) + { + $this->id = $id; + $this->code = $obj->code; + $this->description = $obj->description; + $this->value = $obj->value; + $this->checkParameters(); + return 1; + } + else + { + return 0; + } + } + else + { + $this->error="Error ".$this->db->lasterror(); + return -1; + } + } + + /** + * Update object into database + * + * @param User $user User that modifies + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, >0 if OK + */ + function update($user=0, $notrigger=0) + { + $error=0; + + $this->checkParameters(); + + // Update request + $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET"; + $sql.= " code = ".(isset($this->code)?"'".$this->db->escape($this->code)."'":"''").","; + $sql.= " description = ".(isset($this->description)?"'".$this->db->escape($this->description)."'":"''").","; + $sql.= " value = ".$this->value; + $sql.= " WHERE rowid = ".$this->id; + + $this->db->begin(); + + dol_syslog(get_class($this)."::update"); + $resql = $this->db->query($sql); + if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); } + + if (! $error) + { + if (! $notrigger) + { + // Uncomment this and change MYOBJECT to your own tag if you + // want this action calls a trigger. + + //// Call triggers + //$result=$this->call_trigger('MYOBJECT_MODIFY',$user); + //if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail} + //// End call triggers + } + } + + // Commit or rollback + if ($error) + { + foreach($this->errors as $errmsg) + { + dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR); + $this->error.=($this->error?', '.$errmsg:$errmsg); + } + $this->db->rollback(); + return -1*$error; + } + else + { + $this->db->commit(); + return 1; + } + } + + + /** + * Delete object in database + * + * @param int $rowid Row id of global variable + * @param User $user User that deletes + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, >0 if OK + */ + function delete($rowid, $user, $notrigger=0) + { + $error=0; + + $this->db->begin(); + + if (! $error) + { + if (! $notrigger) + { + // Uncomment this and change MYOBJECT to your own tag if you + // want this action calls a trigger. + + //// Call triggers + //$result=$this->call_trigger('MYOBJECT_DELETE',$user); + //if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail} + //// End call triggers + } + } + + if (! $error) + { + $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element; + $sql.= " WHERE rowid = ".$rowid; + + dol_syslog(get_class($this)."::delete"); + $resql = $this->db->query($sql); + if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); } + } + + // Commit or rollback + if ($error) + { + foreach($this->errors as $errmsg) + { + dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR); + $this->error.=($this->error?', '.$errmsg:$errmsg); + } + $this->db->rollback(); + return -1*$error; + } + else + { + $this->db->commit(); + return 1; + } + } + + /** + * Initialise object with example values + * Id must be 0 if object instance is a specimen + * + * @return void + */ + function initAsSpecimen() + { + $this->id=0; + $this->code=''; + $this->description=''; + $this->value=''; + } + + /** + * Checks if all parameters are in order + * + * @return void + */ + function checkParameters() + { + // Clean parameters + if (isset($this->code)) $this->code=trim($this->code); + if (isset($this->description)) $this->description=trim($this->description); + + // Check parameters + if (empty($this->value) || !is_numeric($this->value)) $this->value=0; + } + + /** + * List all price global variables + * + * @return array Array of price global variables + */ + function listGlobalVariables() + { + $sql = "SELECT rowid, code, description, value"; + $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element; + $sql.= " ORDER BY code"; + + dol_syslog(get_class($this)."::listGlobalVariables"); + $resql=$this->db->query($sql); + if ($resql) + { + $retarray = array(); + + while ($record = $this->db->fetch_array($resql)) + { + $variable_obj = new PriceGlobalVariable($this->db); + $variable_obj->id = $record["rowid"]; + $variable_obj->code = $record["code"]; + $variable_obj->description = $record["description"]; + $variable_obj->value = $record["value"]; + $variable_obj->checkParameters(); + $retarray[]=$variable_obj; + } + + $this->db->free($resql); + return $retarray; + } + else + { + $this->error=$this->db->error(); + return -1; + } + } +} diff --git a/htdocs/product/dynamic_price/class/price_global_variable_updater.class.php b/htdocs/product/dynamic_price/class/price_global_variable_updater.class.php new file mode 100644 index 00000000000..3c176f0530e --- /dev/null +++ b/htdocs/product/dynamic_price/class/price_global_variable_updater.class.php @@ -0,0 +1,625 @@ + + * Copyright (C) 2014 Juanjo Menent +/* Copyright (C) 2015 Ion Agorria + * + * 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file htdocs/product/dynamic_price/class/price_global_variable_updater.class.php + * \ingroup product + * \brief Class for price global variable updaters table + */ + + +/** + * Class for price global variable updaters table + */ +class PriceGlobalVariableUpdater +{ + var $db; //!< To store db handler + var $error; //!< To return error code (or message) + var $errors=array(); //!< To return several error codes (or messages) + var $types=array(0, 1); //!< Updater types + var $update_min = 5; //!< Minimal update rate + var $id; + var $type; + var $description; + var $parameters; + var $fk_variable; + var $update_interval; //!< Interval in mins + var $next_update; //!< Next update timestamp + var $last_status; + public $table_element = "c_price_global_variable_updater"; + + /** + * Constructor + * + * @param DoliDb $db Database handler + */ + function __construct($db) + { + $this->db = $db; + return 1; + } + + + /** + * Create object into database + * + * @param User $user User that creates + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, Id of created object if OK + */ + function create($user, $notrigger=0) + { + $error=0; + + $this->checkParameters(); + + // Insert request + $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." ("; + $sql.= "type, description, parameters, fk_variable, update_interval, next_update, last_status"; + $sql.= ") VALUES ("; + $sql.= " ".$this->type.","; + $sql.= " ".(isset($this->description)?"'".$this->db->escape($this->description)."'":"''").","; + $sql.= " ".(isset($this->parameters)?"'".$this->db->escape($this->parameters)."'":"''").","; + $sql.= " ".$this->fk_variable.","; + $sql.= " ".$this->update_interval.","; + $sql.= " ".$this->next_update.","; + $sql.= " ".(isset($this->last_status)?"'".$this->db->escape($this->last_status)."'":"''"); + $sql.= ")"; + + $this->db->begin(); + + dol_syslog(get_class($this)."::create", LOG_DEBUG); + $resql=$this->db->query($sql); + if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); } + + if (! $error) + { + $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element); + + if (! $notrigger) + { + // Uncomment this and change MYOBJECT to your own tag if you + // want this action calls a trigger. + + //// Call triggers + //$result=$this->call_trigger('MYOBJECT_CREATE',$user); + //if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail} + //// End call triggers + } + } + + // Commit or rollback + if ($error) + { + foreach($this->errors as $errmsg) + { + dol_syslog(__METHOD__." ".$errmsg, LOG_ERR); + $this->error.=($this->error?', '.$errmsg:$errmsg); + } + $this->db->rollback(); + return -1*$error; + } + else + { + $this->db->commit(); + return $this->id; + } + } + + + /** + * Load object in memory from the database + * + * @param int $id Id object + * @return int < 0 if KO, 0 if OK but not found, > 0 if OK + */ + function fetch($id) + { + $sql = "SELECT type, description, parameters, fk_variable, update_interval, next_update, last_status"; + $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element; + $sql.= " WHERE rowid = ".$id; + + dol_syslog(get_class($this)."::fetch"); + $resql=$this->db->query($sql); + if ($resql) + { + $obj = $this->db->fetch_object($resql); + if ($obj) + { + $this->id = $id; + $this->type = $obj->type; + $this->description = $obj->description; + $this->parameters = $obj->parameters; + $this->fk_variable = $obj->fk_variable; + $this->update_interval = $obj->update_interval; + $this->next_update = $obj->next_update; + $this->last_status = $obj->last_status; + $this->checkParameters(); + return 1; + } + else + { + return 0; + } + } + else + { + $this->error="Error ".$this->db->lasterror(); + return -1; + } + } + + /** + * Update object into database + * + * @param User $user User that modifies + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, >0 if OK + */ + function update($user=0, $notrigger=0) + { + $error=0; + + $this->checkParameters(); + + // Update request + $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET"; + $sql.= " type = ".$this->type.","; + $sql.= " description = ".(isset($this->description)?"'".$this->db->escape($this->description)."'":"''").","; + $sql.= " parameters = ".(isset($this->parameters)?"'".$this->db->escape($this->parameters)."'":"''").","; + $sql.= " fk_variable = ".$this->fk_variable.","; + $sql.= " update_interval = ".$this->update_interval.","; + $sql.= " next_update = ".$this->next_update.","; + $sql.= " last_status = ".(isset($this->last_status)?"'".$this->db->escape($this->last_status)."'":"''"); + $sql.= " WHERE rowid = ".$this->id; + + $this->db->begin(); + + dol_syslog(get_class($this)."::update"); + $resql = $this->db->query($sql); + if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); } + + if (! $error) + { + if (! $notrigger) + { + // Uncomment this and change MYOBJECT to your own tag if you + // want this action calls a trigger. + + //// Call triggers + //$result=$this->call_trigger('MYOBJECT_MODIFY',$user); + //if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail} + //// End call triggers + } + } + + // Commit or rollback + if ($error) + { + foreach($this->errors as $errmsg) + { + dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR); + $this->error.=($this->error?', '.$errmsg:$errmsg); + } + $this->db->rollback(); + return -1*$error; + } + else + { + $this->db->commit(); + return 1; + } + } + + /** + * Delete object in database + * + * @param int $rowid Row id of global variable + * @param User $user User that deletes + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, >0 if OK + */ + function delete($rowid, $user, $notrigger=0) + { + $error=0; + + $this->db->begin(); + + if (! $error) + { + if (! $notrigger) + { + // Uncomment this and change MYOBJECT to your own tag if you + // want this action calls a trigger. + + //// Call triggers + //$result=$this->call_trigger('MYOBJECT_DELETE',$user); + //if ($result < 0) { $error++; //Do also what you must do to rollback action if trigger fail} + //// End call triggers + } + } + + if (! $error) + { + $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element; + $sql.= " WHERE rowid = ".$rowid; + + dol_syslog(get_class($this)."::delete"); + $resql = $this->db->query($sql); + if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); } + } + + // Commit or rollback + if ($error) + { + foreach($this->errors as $errmsg) + { + dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR); + $this->error.=($this->error?', '.$errmsg:$errmsg); + } + $this->db->rollback(); + return -1*$error; + } + else + { + $this->db->commit(); + return 1; + } + } + + /** + * Initialise object with example values + * Id must be 0 if object instance is a specimen + * + * @return void + */ + function initAsSpecimen() + { + $this->id=0; + $this->type=0; + $this->description=''; + $this->parameters=''; + $this->fk_variable=0; + $this->update_interval=0; + $this->next_update=0; + $this->last_status=''; + } + + /** + * Returns the last updated time in string html format, returns "never" if its less than 1 + * + * @return string + */ + function getLastUpdated() + { + global $langs; + $last = $this->next_update - ($this->update_interval * 60); + if ($last < 1) { + return $langs->trans("Never"); + } + $status = empty($this->last_status) ? $langs->trans("CorrectlyUpdated") : $this->last_status; + return $status.'
'.dol_print_date($last, '%d/%m/%Y %H:%M:%S'); + } + + /** + * Checks if all parameters are in order + * + * @return void + */ + function checkParameters() + { + // Clean parameters + if (isset($this->description)) $this->description=trim($this->description); + if (isset($this->parameters)) $this->parameters=trim($this->parameters); + else $this->parameters=""; + if (isset($this->last_status)) $this->last_status=trim($this->last_status); + + // Check parameters + if (empty($this->type) || !is_numeric($this->type) || !in_array($this->type, $this->types)) $this->type=0; + if (empty($this->update_interval) || !is_numeric($this->update_interval) || $this->update_interval < 1) $this->update_interval=$this->update_min; + if (empty($this->next_update) || !is_numeric($this->next_update) || $this->next_update < 0) $this->next_update=0; + } + + /** + * List all price global variables + * + * @return array Array of price global variable updaters + */ + function listUpdaters() + { + $sql = "SELECT rowid, type, description, parameters, fk_variable, update_interval, next_update, last_status"; + $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element; + + dol_syslog(get_class($this)."::listUpdaters"); + $resql=$this->db->query($sql); + if ($resql) + { + $retarray = array(); + + while ($record = $this->db->fetch_array($resql)) + { + $updater_obj = new PriceGlobalVariableUpdater($this->db); + $updater_obj->id = $record["rowid"]; + $updater_obj->type = $record["type"]; + $updater_obj->description = $record["description"]; + $updater_obj->parameters = $record["parameters"]; + $updater_obj->fk_variable = $record["fk_variable"]; + $updater_obj->update_interval = $record["update_interval"]; + $updater_obj->next_update = $record["next_update"]; + $updater_obj->last_status = $record["last_status"]; + $updater_obj->checkParameters(); + $retarray[]=$updater_obj; + } + + $this->db->free($resql); + return $retarray; + } + else + { + $this->error=$this->db->error(); + return -1; + } + } + + /** + * List all updaters which need to be processed + * + * @return array Array of price global variable updaters + */ + function listPendingUpdaters() + { + $sql = "SELECT rowid, type, description, parameters, fk_variable, update_interval, next_update, last_status"; + $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element; + $sql.= " WHERE next_update < ".dol_now(); + + dol_syslog(get_class($this)."::processUpdaters"); + $resql=$this->db->query($sql); + if ($resql) + { + $retarray = array(); + + while ($record = $this->db->fetch_array($resql)) + { + $updater_obj = new PriceGlobalVariableUpdater($this->db); + $updater_obj->id = $record["rowid"]; + $updater_obj->type = $record["type"]; + $updater_obj->description = $record["description"]; + $updater_obj->parameters = $record["parameters"]; + $updater_obj->fk_variable = $record["fk_variable"]; + $updater_obj->update_interval = $record["update_interval"]; + $updater_obj->next_update = $record["next_update"]; + $updater_obj->last_status = $record["last_status"]; + $updater_obj->checkParameters(); + $retarray[]=$updater_obj; + } + + $this->db->free($resql); + return $retarray; + } + else + { + $this->error=$this->db->error(); + return -1; + } + } + + /** + * Handles the processing of this updater + * + * @return int <0 if KO, 0 if OK but no global variable found, >0 if OK + */ + function process() + { + global $langs, $user; + $langs->load("errors"); + + $this->error = null; + $this->checkParameters(); + + //Try to load the target global variable and abort if fails + if ($this->fk_variable < 1) { + $this->error = $langs->trans("ErrorGlobalVariableUpdater5"); + return 0; + } + $price_globals = new PriceGlobalVariable($this->db); + $res = $price_globals->fetch($this->fk_variable); + if ($res < 1) { + $this->error = $langs->trans("ErrorGlobalVariableUpdater5"); + return 0; + } + + //Process depending of type + if ($this->type == 0 || $this->type == 1) { + //Get and check if required parameters are present + $parameters = json_decode($this->parameters, true); + if (!isset($parameters)) { + $this->error = $langs->trans("ErrorGlobalVariableUpdater1", $this->parameters); + return -1; + } + $url = $parameters['URL']; + if (!isset($url)) { + $this->error = $langs->trans("ErrorGlobalVariableUpdater2", 'URL'); + return -1; + } + $value = $parameters['VALUE']; + if (!isset($value)) { + $this->error = $langs->trans("ErrorGlobalVariableUpdater2", 'VALUE'); + return -1; + } + $result = ""; + if ($this->type == 0) { + //CURL client + $handle = curl_init(); + curl_setopt_array($handle, array( + CURLOPT_URL => $url, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_TIMEOUT => 5, + CURLOPT_POST => false, + CURLOPT_HEADER => false, + )); + + $result = curl_exec($handle); + $code = curl_getinfo($handle, CURLINFO_HTTP_CODE); + if (!isset($result)) { + $this->error = $langs->trans("ErrorGlobalVariableUpdater0", "empty response"); + return -1; + } + if ($code !== 200) { + $this->error = $langs->trans("ErrorGlobalVariableUpdater0", $code); + return -1; + } + + //Decode returned response + $result = json_decode($result, true); + } elseif ($this->type == 1) { + $ns = $parameters['NS']; + if (!isset($ns)) { + $this->error = $langs->trans("ErrorGlobalVariableUpdater2", 'NS'); + return -1; + } + $method = $parameters['METHOD']; + if (!isset($method)) { + $this->error = $langs->trans("ErrorGlobalVariableUpdater2", 'METHOD'); + return -1; + } + $data = $parameters['DATA']; + if (!isset($data)) { + $this->error = $langs->trans("ErrorGlobalVariableUpdater2", 'DATA'); + return -1; + } + + //SOAP client + require_once NUSOAP_PATH.'/nusoap.php'; + $soap_client = new nusoap_client($url); + $soap_client->soap_defencoding='UTF-8'; + $soap_client->decodeUTF8(false); + $result = $soap_client->call($method, $data, $ns,''); + + //Check if result is a error + if ($result === false) { + $this->error = $langs->trans("ErrorGlobalVariableUpdater4", $soap_client->error_str); + return -1; + } + } + + //Explode value and walk for each key in value array to get the relevant key + $value = explode(',', $value); + foreach ($value as $key) { + $result = $result[$key]; + } + if (!isset($result)) { + $this->error = $langs->trans("ErrorGlobalVariableUpdater3"); + return -1; + } + + //Save data to global and update it + $price_globals->value = $result; + $price_globals->update($user); + } + return 1; + } + + /** + * Update next_update into database + * + * @param string $next_update Next update to write + * @param User $user User that modifies + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, >0 if OK + */ + function update_next_update($next_update, $user=0, $notrigger=0) + { + $error=0; + + $this->next_update = $next_update; + $this->checkParameters(); + + // Update request + $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET"; + $sql.= " next_update = ".$this->next_update; + $sql.= " WHERE rowid = ".$this->id; + + $this->db->begin(); + + dol_syslog(get_class($this)."::update_next_update"); + $resql = $this->db->query($sql); + if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); } + + // Commit or rollback + if ($error) + { + foreach($this->errors as $errmsg) + { + dol_syslog(get_class($this)."::update_next_update ".$errmsg, LOG_ERR); + $this->error.=($this->error?', '.$errmsg:$errmsg); + } + $this->db->rollback(); + return -1*$error; + } + else + { + $this->db->commit(); + return 1; + } + } + + /** + * Update last_status into database + * + * @param string $last_status Status to write + * @param User $user User that modifies + * @param int $notrigger 0=launch triggers after, 1=disable triggers + * @return int <0 if KO, >0 if OK + */ + function update_status($last_status, $user=0, $notrigger=0) + { + $error=0; + + $this->last_status = $last_status; + $this->checkParameters(); + + // Update request + $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET"; + $sql.= " last_status = ".(isset($this->last_status)?"'".$this->db->escape($this->last_status)."'":"''"); + $sql.= " WHERE rowid = ".$this->id; + + $this->db->begin(); + + dol_syslog(get_class($this)."::update_status"); + $resql = $this->db->query($sql); + if (! $resql) { $error++; $this->errors[]="Error ".$this->db->lasterror(); } + + // Commit or rollback + if ($error) + { + foreach($this->errors as $errmsg) + { + dol_syslog(get_class($this)."::update_status ".$errmsg, LOG_ERR); + $this->error.=($this->error?', '.$errmsg:$errmsg); + } + $this->db->rollback(); + return -1*$error; + } + else + { + $this->db->commit(); + return 1; + } + } +} diff --git a/htdocs/product/class/priceparser.class.php b/htdocs/product/dynamic_price/class/price_parser.class.php similarity index 78% rename from htdocs/product/class/priceparser.class.php rename to htdocs/product/dynamic_price/class/price_parser.class.php index c2b047a7ae1..4a7bdb7acf6 100644 --- a/htdocs/product/class/priceparser.class.php +++ b/htdocs/product/dynamic_price/class/price_parser.class.php @@ -1,5 +1,5 @@ +/* Copyright (C) 2015 Ion Agorria * * 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 @@ -16,13 +16,15 @@ */ /** - * \file htdocs/product/class/priceparser.class.php + * \file htdocs/product/dynamic_price/class/price_parser.class.php * \ingroup product * \brief File of class to calculate prices using expression */ require_once DOL_DOCUMENT_ROOT.'/includes/evalmath/evalmath.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/dynamic_price/class/price_expression.class.php'; +require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_global_variable.class.php'; +require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_global_variable_updater.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; /** @@ -30,17 +32,17 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; */ class PriceParser { - protected $db; - // Limit of expressions per price - public $limit = 100; - // The error that ocurred when parsing price - public $error; - // The expression that caused the error - public $error_expr; - //The special char - public $special_chr = "#"; - //The separator char - public $separator_chr = ";"; + protected $db; + // Limit of expressions per price + public $limit = 100; + // The error that occurred when parsing price + public $error; + // The expression that caused the error + public $error_expr; + //The special char + public $special_chr = "#"; + //The separator char + public $separator_chr = ";"; /** * Constructor @@ -104,7 +106,7 @@ class PriceParser { return $langs->trans("ErrorPriceExpression".$code, $info); } - else if (in_array($code, array(6))) //Errors which have 2 args + else if (in_array($code, array(6, 23))) //Errors which have 2 args { return $langs->trans("ErrorPriceExpression".$code, $info[0], $info[1]); } @@ -128,6 +130,7 @@ class PriceParser */ public function parseExpression($product, $expression, $values) { + global $user; //Accessible product values by expressions $values = array_merge($values, array( "tva_tx" => $product->tva_tx, @@ -139,13 +142,31 @@ class PriceParser "price_min" => $product->price_min, )); - //Retreive all extrafield for product and add it to values + //Retrieve 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]; + $values["extrafield_".$key] = $product->array_options['options_'.$key]; + } + + //Process any pending updaters + $price_updaters = new PriceGlobalVariableUpdater($this->db); + foreach ($price_updaters->listPendingUpdaters() as $entry) { + //Schedule the next update by adding current timestamp (secs) + interval (mins) + $entry->update_next_update(dol_now() + ($entry->update_interval * 60), $user); + //Do processing + $res = $entry->process(); + //Store any error or clear status if OK + $entry->update_status($res < 1?$entry->error:'', $user); + } + + //Get all global values + $price_globals = new PriceGlobalVariable($this->db); + foreach ($price_globals->listGlobalVariables() as $entry) + { + $values["global_".$entry->code] = $entry->value; } //Check if empty @@ -153,36 +174,32 @@ class PriceParser if (empty($expression)) { $this->error = array(20, null); - return -1; + return -2; } //Prepare the lib, parameters and values $em = new EvalMath(); $em->suppress_errors = true; //Don't print errors on page $this->error_expr = null; - $search = array(); - $replace = array(); - foreach ($values as $key => $value) { - if ($value !== null) { - $search[] = $this->special_chr.$key.$this->special_chr; - $replace[] = $value; - } - } + $last_result = null; //Iterate over each expression splitted by $separator_chr $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); + } $expressions = explode($this->separator_chr, $expression); - $expressions = array_slice($expressions, 0, $limit); + $expressions = array_slice($expressions, 0, $this->limit); foreach ($expressions as $expr) { $expr = trim($expr); if (!empty($expr)) { - $expr = str_ireplace($search, $replace, $expr); $last_result = $em->evaluate($expr); $this->error = $em->last_error_code; if ($this->error !== null) { //$em->last_error is null if no error happened, so just check if error is not null $this->error_expr = $expr; - return -2; + return -3; } } } @@ -190,15 +207,15 @@ class PriceParser if (empty($vars["price"])) { $vars["price"] = $last_result; } - if ($vars["price"] === null) + if (!isset($vars["price"])) { $this->error = array(21, $expression); - return -3; + return -4; } if ($vars["price"] < 0) { $this->error = array(22, $expression); - return -4; + return -5; } return $vars["price"]; } @@ -209,13 +226,13 @@ class PriceParser * @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 + * @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); + $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( @@ -237,7 +254,7 @@ class PriceParser * * @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()) { @@ -261,7 +278,7 @@ class PriceParser * @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 + * @return int > 0 if OK, < 1 if KO */ public function parseProductSupplierExpression($product_id, $expression, $quantity = null, $tva_tx = null, $extra_values = array()) { @@ -285,7 +302,7 @@ class PriceParser * @param int $quantity Min quantity * @param int $tva_tx VAT rate * @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 parseProductSupplier($product_id, $expression_id, $quantity = null, $tva_tx = null, $extra_values = array()) { diff --git a/htdocs/product/expression.php b/htdocs/product/dynamic_price/editor.php similarity index 83% rename from htdocs/product/expression.php rename to htdocs/product/dynamic_price/editor.php index cb612359bd5..e05ad53a27e 100644 --- a/htdocs/product/expression.php +++ b/htdocs/product/dynamic_price/editor.php @@ -16,23 +16,24 @@ */ /** - * \file htdocs/product/expression.php + * \file htdocs/product/expression/editor.php * \ingroup product * \brief Page for editing expression */ -require '../main.inc.php'; +require '../../main.inc.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/product.lib.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/priceparser.class.php'; +require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_expression.class.php'; +require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_global_variable.class.php'; +require_once DOL_DOCUMENT_ROOT.'/product/dynamic_price/class/price_parser.class.php'; $langs->load("products"); $langs->load("accountancy"); //"Back" translation is on this file $id = GETPOST('id', 'int'); $eid = GETPOST('eid', 'int'); -$action = GETPOST('action', 'alpha'); +$action = GETPOST('action', 'alpha'); $title = GETPOST('expression_title', 'alpha'); $expression = GETPOST('expression'); $tab = GETPOST('tab', 'alpha'); @@ -47,6 +48,7 @@ $product = new Product($db); $product->fetch($id, ''); $price_expression = new PriceExpression($db); +$price_globals = new PriceGlobalVariable($db); //Fetch expression data if (empty($eid)) //This also disables fetch when eid == 0 @@ -79,7 +81,7 @@ if ($action == 'add') { $price_expression->title = $title; $price_expression->expression = $expression; - $result = $price_expression->create($user); + $result = $price_expression->create($user); if ($result > 0) //created successfully, set the eid to newly created entry { $eid = $price_expression->id; @@ -120,7 +122,7 @@ if ($action == 'update') $price_expression->id = $eid; $price_expression->title = $title; $price_expression->expression = $expression; - $result = $price_expression->update($user); + $result = $price_expression->update($user); if ($result < 0) { setEventMessage("update: ".$price_expression->error, 'errors'); @@ -146,7 +148,7 @@ if ($action == 'delete') { if ($eid != 0) { - $result = $price_expression->delete($eid, $user); + $result = $price_expression->delete($eid, $user); if ($result < 0) { setEventMessage("delete: ".$price_expression->error, 'errors'); @@ -184,8 +186,17 @@ print ''.$langs->trans("Name").''; print ''; print ''; +//Help text +$help_text = $langs->trans("PriceExpressionEditorHelp1"); +$help_text.= '

'.$langs->trans("PriceExpressionEditorHelp2"); +$help_text.= '

'.$langs->trans("PriceExpressionEditorHelp3"); +$help_text.= '

'.$langs->trans("PriceExpressionEditorHelp4"); +$help_text.= '

'.$langs->trans("PriceExpressionEditorHelp5"); +foreach ($price_globals->listGlobalVariables() as $entry) { + $help_text.= '
#globals_'.$entry->code.'# '.$entry->description.' = '.$entry->value; +} + //Price expression editor -$help_text = $langs->trans("PriceExpressionEditorHelp1").'

'.$langs->trans("PriceExpressionEditorHelp2").'

'.$langs->trans("PriceExpressionEditorHelp3").'

'.$langs->trans("PriceExpressionEditorHelp4"); print ''.$form->textwithpicto($langs->trans("PriceExpressionEditor"),$help_text,1).''; 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); @@ -194,7 +205,7 @@ print ''; print ''; //Buttons -print '
'; +print '
'; print ''; print ''.$langs->trans("Back").''; if ($eid == 0) @@ -207,7 +218,7 @@ else } print '
'; -print ''; +print ''; // This code reloads the page depending of selected option, goes to page selected by tab when back is pressed print '