1<?php 2/* Copyright (C) 2017 Laurent Destailleur <eldy@users.sourceforge.net> 3 * Copyright (C) 2021 Alexis LAURIER <contact@alexislaurier.fr> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 3 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <https://www.gnu.org/licenses/>. 17 */ 18 19/** 20 * \file class/productfournisseurprice.class.php 21 * \ingroup product 22 * \brief This file is a CRUD class file for ProductFournisseurPrice (Create/Read/Update/Delete) 23 */ 24 25// Put here all includes required by your class file 26require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php'; 27 28/** 29 * Class for ProductFournisseurPrice 30 */ 31class ProductFournisseurPrice extends CommonObject 32{ 33 /** 34 * @var string ID to identify managed object. 35 */ 36 public $element = 'productfournisseurprice'; 37 38 /** 39 * @var string Name of table without prefix where object is stored. This is also the key used for extrafields management. 40 */ 41 public $table_element = 'product_fournisseur_price'; 42 43 /** 44 * @var int Does this object support multicompany module ? 45 * 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table 46 */ 47 public $ismultientitymanaged = 1; 48 49 /** 50 * @var int Does object support extrafields ? 0=No, 1=Yes 51 */ 52 public $isextrafieldmanaged = 1; 53 54 /** 55 * @var string String with name of icon for productfournisseurprice. Must be the part after the 'object_' into object_productfournisseurprice.png 56 */ 57 public $picto = 'productfournisseurprice@buypricehistory'; 58 59 60 const STATUS_DRAFT = 0; 61 const STATUS_VALIDATED = 1; 62 const STATUS_CANCELED = 9; 63 64 65 /** 66 * 'type' field format ('integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter]]', 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter]]]', 'varchar(x)', 'double(24,8)', 'real', 'price', 'text', 'text:none', 'html', 'date', 'datetime', 'timestamp', 'duration', 'mail', 'phone', 'url', 'password') 67 * Note: Filter can be a string like "(t.ref:like:'SO-%') or (t.date_creation:<:'20160101') or (t.nature:is:NULL)" 68 * 'label' the translation key. 69 * 'picto' is code of a picto to show before value in forms 70 * 'enabled' is a condition when the field must be managed (Example: 1 or '$conf->global->MY_SETUP_PARAM) 71 * 'position' is the sort order of field. 72 * 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0). 73 * 'visible' says if field is visible in list (Examples: 0=Not visible, 1=Visible on list and create/update/view forms, 2=Visible on list only, 3=Visible on create/update/view form only (not list), 4=Visible on list and update/view form only (not create). 5=Visible on list and view only (not create/not update). Using a negative value means field is not shown by default on list but can be selected for viewing) 74 * 'noteditable' says if field is not editable (1 or 0) 75 * 'default' is a default value for creation (can still be overwrote by the Setup of Default Values if field is editable in creation form). Note: If default is set to '(PROV)' and field is 'ref', the default value will be set to '(PROVid)' where id is rowid when a new record is created. 76 * 'index' if we want an index in database. 77 * 'foreignkey'=>'tablename.field' if the field is a foreign key (it is recommanded to name the field fk_...). 78 * 'searchall' is 1 if we want to search in this field when making a search from the quick search button. 79 * 'isameasure' must be set to 1 if you want to have a total on list for this field. Field type must be summable like integer or double(24,8). 80 * 'css' and 'cssview' and 'csslist' is the CSS style to use on field. 'css' is used in creation and update. 'cssview' is used in view mode. 'csslist' is used for columns in lists. For example: 'maxwidth200', 'wordbreak', 'tdoverflowmax200' 81 * 'help' is a 'TranslationString' to use to show a tooltip on field. You can also use 'TranslationString:keyfortooltiponlick' for a tooltip on click. 82 * 'showoncombobox' if value of the field must be visible into the label of the combobox that list record 83 * 'disabled' is 1 if we want to have the field locked by a 'disabled' attribute. In most cases, this is never set into the definition of $fields into class, but is set dynamically by some part of code. 84 * 'arrayofkeyval' to set list of value if type is a list of predefined values. For example: array("0"=>"Draft","1"=>"Active","-1"=>"Cancel") 85 * 'autofocusoncreate' to have field having the focus on a create form. Only 1 field should have this property set to 1. 86 * 'comment' is not used. You can store here any text of your choice. It is not used by application. 87 * 88 * Note: To have value dynamic, you can set value to 0 in definition and edit the value on the fly into the constructor. 89 */ 90 91 // BEGIN MODULEBUILDER PROPERTIES 92 /** 93 * @var array Array with all fields and their property. Do not use it as a static var. It may be modified by constructor. 94 */ 95 public $fields=array( 96 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>'1', 'position'=>10, 'notnull'=>1, 'visible'=>0,), 97 'entity' => array('type'=>'integer', 'label'=>'Entity', 'enabled'=>'1', 'position'=>15, 'notnull'=>1, 'visible'=>-2, 'default'=>'1', 'index'=>1,), 98 'datec' => array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>'1', 'position'=>20, 'notnull'=>0, 'visible'=>-1,), 99 'tms' => array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>'1', 'position'=>25, 'notnull'=>1, 'visible'=>-1,), 100 'fk_product' => array('type'=>'integer:Product:product/class/product.class.php:1', 'label'=>'Fkproduct', 'enabled'=>'1', 'position'=>30, 'notnull'=>0, 'visible'=>-1,), 101 'fk_soc' => array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>'1', 'position'=>35, 'notnull'=>0, 'visible'=>-1,), 102 'ref_fourn' => array('type'=>'varchar(255)', 'label'=>'Reffourn', 'enabled'=>'1', 'position'=>40, 'notnull'=>0, 'visible'=>-1,), 103 'desc_fourn' => array('type'=>'text', 'label'=>'Descfourn', 'enabled'=>'1', 'position'=>45, 'notnull'=>0, 'visible'=>-1,), 104 'fk_availability' => array('type'=>'integer', 'label'=>'Fkavailability', 'enabled'=>'1', 'position'=>50, 'notnull'=>0, 'visible'=>-1,), 105 'price' => array('type'=>'double(24,8)', 'label'=>'Price', 'enabled'=>'1', 'position'=>55, 'notnull'=>0, 'visible'=>-1,), 106 'quantity' => array('type'=>'double', 'label'=>'Quantity', 'enabled'=>'1', 'position'=>60, 'notnull'=>0, 'visible'=>-1,), 107 'remise_percent' => array('type'=>'double', 'label'=>'Remisepercent', 'enabled'=>'1', 'position'=>65, 'notnull'=>1, 'visible'=>-1,), 108 'remise' => array('type'=>'double', 'label'=>'Remise', 'enabled'=>'1', 'position'=>70, 'notnull'=>1, 'visible'=>-1,), 109 'unitprice' => array('type'=>'double(24,8)', 'label'=>'Unitprice', 'enabled'=>'1', 'position'=>75, 'notnull'=>0, 'visible'=>-1,), 110 'charges' => array('type'=>'double(24,8)', 'label'=>'Charges', 'enabled'=>'1', 'position'=>80, 'notnull'=>0, 'visible'=>-1,), 111 'default_vat_code' => array('type'=>'varchar(10)', 'label'=>'Defaultvatcode', 'enabled'=>'1', 'position'=>85, 'notnull'=>0, 'visible'=>-1,), 112 'tva_tx' => array('type'=>'double(6,3)', 'label'=>'Tvatx', 'enabled'=>'1', 'position'=>90, 'notnull'=>1, 'visible'=>-1,), 113 'info_bits' => array('type'=>'integer', 'label'=>'Infobits', 'enabled'=>'1', 'position'=>95, 'notnull'=>1, 'visible'=>-1,), 114 'fk_user' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'Fkuser', 'enabled'=>'1', 'position'=>100, 'notnull'=>0, 'visible'=>-1,), 115 'fk_supplier_price_expression' => array('type'=>'integer', 'label'=>'Fksupplierpriceexpression', 'enabled'=>'1', 'position'=>105, 'notnull'=>0, 'visible'=>-1,), 116 'import_key' => array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>'1', 'position'=>900, 'notnull'=>0, 'visible'=>-2,), 117 'delivery_time_days' => array('type'=>'integer', 'label'=>'Deliverytimedays', 'enabled'=>'1', 'position'=>115, 'notnull'=>0, 'visible'=>-1,), 118 'supplier_reputation' => array('type'=>'varchar(10)', 'label'=>'Supplierreputation', 'enabled'=>'1', 'position'=>120, 'notnull'=>0, 'visible'=>-1,), 119 'fk_multicurrency' => array('type'=>'integer', 'label'=>'Fkmulticurrency', 'enabled'=>'1', 'position'=>125, 'notnull'=>0, 'visible'=>-1,), 120 'multicurrency_code' => array('type'=>'varchar(255)', 'label'=>'Multicurrencycode', 'enabled'=>'1', 'position'=>130, 'notnull'=>0, 'visible'=>-1,), 121 'multicurrency_tx' => array('type'=>'double(24,8)', 'label'=>'Multicurrencytx', 'enabled'=>'1', 'position'=>135, 'notnull'=>0, 'visible'=>-1,), 122 'multicurrency_price' => array('type'=>'double(24,8)', 'label'=>'Multicurrencyprice', 'enabled'=>'1', 'position'=>140, 'notnull'=>0, 'visible'=>-1,), 123 'multicurrency_unitprice' => array('type'=>'double(24,8)', 'label'=>'Multicurrencyunitprice', 'enabled'=>'1', 'position'=>145, 'notnull'=>0, 'visible'=>-1,), 124 'localtax1_tx' => array('type'=>'double(6,3)', 'label'=>'Localtax1tx', 'enabled'=>'1', 'position'=>150, 'notnull'=>0, 'visible'=>-1,), 125 'localtax1_type' => array('type'=>'varchar(10)', 'label'=>'Localtax1type', 'enabled'=>'1', 'position'=>155, 'notnull'=>1, 'visible'=>-1,), 126 'localtax2_tx' => array('type'=>'double(6,3)', 'label'=>'Localtax2tx', 'enabled'=>'1', 'position'=>160, 'notnull'=>0, 'visible'=>-1,), 127 'localtax2_type' => array('type'=>'varchar(10)', 'label'=>'Localtax2type', 'enabled'=>'1', 'position'=>165, 'notnull'=>1, 'visible'=>-1,), 128 'barcode' => array('type'=>'varchar(180)', 'label'=>'Barcode', 'enabled'=>'1', 'position'=>170, 'notnull'=>0, 'visible'=>-1,), 129 'fk_barcode_type' => array('type'=>'integer', 'label'=>'Fkbarcodetype', 'enabled'=>'1', 'position'=>175, 'notnull'=>0, 'visible'=>-1,), 130 'packaging' => array('type'=>'varchar(64)', 'label'=>'Packaging', 'enabled'=>'1', 'position'=>180, 'notnull'=>0, 'visible'=>-1,), 131 ); 132 public $rowid; 133 public $entity; 134 public $datec; 135 public $tms; 136 public $fk_product; 137 public $fk_soc; 138 public $ref_fourn; 139 public $desc_fourn; 140 public $fk_availability; 141 public $price; 142 public $quantity; 143 public $remise_percent; 144 public $remise; 145 public $unitprice; 146 public $charges; 147 public $default_vat_code; 148 public $tva_tx; 149 public $info_bits; 150 public $fk_user; 151 public $fk_supplier_price_expression; 152 public $import_key; 153 public $delivery_time_days; 154 public $supplier_reputation; 155 public $fk_multicurrency; 156 public $multicurrency_code; 157 public $multicurrency_tx; 158 public $multicurrency_price; 159 public $multicurrency_unitprice; 160 public $localtax1_tx; 161 public $localtax1_type; 162 public $localtax2_tx; 163 public $localtax2_type; 164 public $barcode; 165 public $fk_barcode_type; 166 public $packaging; 167 // END MODULEBUILDER PROPERTIES 168 169 /** 170 * Constructor 171 * 172 * @param DoliDb $db Database handler 173 */ 174 public function __construct(DoliDB $db) 175 { 176 global $conf, $langs; 177 178 $this->db = $db; 179 180 if (empty($conf->global->MAIN_SHOW_TECHNICAL_ID) && isset($this->fields['rowid'])) $this->fields['rowid']['visible'] = 0; 181 if (empty($conf->multicompany->enabled) && isset($this->fields['entity'])) $this->fields['entity']['enabled'] = 0; 182 183 // Unset fields that are disabled 184 foreach ($this->fields as $key => $val) { 185 if (isset($val['enabled']) && empty($val['enabled'])) { 186 unset($this->fields[$key]); 187 } 188 } 189 } 190 191 /** 192 * Create object into database 193 * 194 * @param User $user User that creates 195 * @param bool $notrigger false=launch triggers after, true=disable triggers 196 * @return int <0 if KO, Id of created object if OK 197 */ 198 public function create(User $user, $notrigger = false) 199 { 200 return $this->createCommon($user, $notrigger); 201 } 202 203 /** 204 * Clone an object into another one 205 * 206 * @param User $user User that creates 207 * @param int $fromid Id of object to clone 208 * @return mixed New object created, <0 if KO 209 */ 210 public function createFromClone(User $user, $fromid) 211 { 212 global $langs, $extrafields; 213 $error = 0; 214 215 dol_syslog(__METHOD__, LOG_DEBUG); 216 217 $object = new self($this->db); 218 219 $this->db->begin(); 220 221 // Load source object 222 $result = $object->fetchCommon($fromid); 223 if ($result > 0 && !empty($object->table_element_line)) $object->fetchLines(); 224 225 // get lines so they will be clone 226 //foreach($this->lines as $line) 227 // $line->fetch_optionals(); 228 229 // Reset some properties 230 unset($object->id); 231 unset($object->fk_user_creat); 232 unset($object->import_key); 233 234 // Clear fields 235 if (property_exists($object, 'ref')) $object->ref = empty($this->fields['ref']['default']) ? "Copy_Of_".$object->ref : $this->fields['ref']['default']; 236 if (property_exists($object, 'label')) $object->label = empty($this->fields['label']['default']) ? $langs->trans("CopyOf")." ".$object->label : $this->fields['label']['default']; 237 if (property_exists($object, 'status')) { $object->status = self::STATUS_DRAFT; } 238 if (property_exists($object, 'date_creation')) { $object->date_creation = dol_now(); } 239 if (property_exists($object, 'date_modification')) { $object->date_modification = null; } 240 // ... 241 // Clear extrafields that are unique 242 if (is_array($object->array_options) && count($object->array_options) > 0) { 243 $extrafields->fetch_name_optionals_label($this->table_element); 244 foreach ($object->array_options as $key => $option) { 245 $shortkey = preg_replace('/options_/', '', $key); 246 if (!empty($extrafields->attributes[$this->table_element]['unique'][$shortkey])) { 247 //var_dump($key); var_dump($clonedObj->array_options[$key]); exit; 248 unset($object->array_options[$key]); 249 } 250 } 251 } 252 253 // Create clone 254 $object->context['createfromclone'] = 'createfromclone'; 255 $result = $object->createCommon($user); 256 if ($result < 0) { 257 $error++; 258 $this->error = $object->error; 259 $this->errors = $object->errors; 260 } 261 262 if (!$error) { 263 // copy internal contacts 264 if ($this->copy_linked_contact($object, 'internal') < 0) { 265 $error++; 266 } 267 } 268 269 if (!$error) { 270 // copy external contacts if same company 271 if (property_exists($this, 'socid') && $this->socid == $object->socid) { 272 if ($this->copy_linked_contact($object, 'external') < 0) 273 $error++; 274 } 275 } 276 277 unset($object->context['createfromclone']); 278 279 // End 280 if (!$error) { 281 $this->db->commit(); 282 return $object; 283 } else { 284 $this->db->rollback(); 285 return -1; 286 } 287 } 288 289 /** 290 * Load object in memory from the database 291 * 292 * @param int $id Id object 293 * @return int <0 if KO, 0 if not found, >0 if OK 294 */ 295 public function fetch($id) 296 { 297 return $this->fetchCommon($id); 298 } 299 300 /** 301 * Load list of objects in memory from the database. 302 * 303 * @param string $sortorder Sort Order 304 * @param string $sortfield Sort field 305 * @param int $limit limit 306 * @param int $offset Offset 307 * @param array $filter Filter array. Example array('field'=>'valueforlike', 'customurl'=>...) 308 * @param string $filtermode Filter mode (AND or OR) 309 * @return array|int int <0 if KO, array of pages if OK 310 */ 311 public function fetchAll($sortorder = '', $sortfield = '', $limit = 0, $offset = 0, array $filter = array(), $filtermode = 'AND') 312 { 313 global $conf; 314 315 dol_syslog(__METHOD__, LOG_DEBUG); 316 317 $records = array(); 318 319 $sql = 'SELECT '; 320 $sql .= $this->getFieldList(); 321 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t'; 322 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) $sql .= ' WHERE t.entity IN ('.getEntity($this->element).')'; 323 else $sql .= ' WHERE 1 = 1'; 324 // Manage filter 325 $sqlwhere = array(); 326 if (count($filter) > 0) { 327 foreach ($filter as $key => $value) { 328 if ($key == 't.rowid') { 329 $sqlwhere[] = $key.' = '.((int) $value); 330 } elseif (in_array($this->fields[$key]['type'], array('date', 'datetime', 'timestamp'))) { 331 $sqlwhere[] = $key." = '".$this->db->idate($value)."'"; 332 } elseif ($key == 'customsql') { 333 $sqlwhere[] = $value; 334 } elseif (strpos($value, '%') === false) { 335 $sqlwhere[] = $key.' IN ('.$this->db->sanitize($this->db->escape($value)).')'; 336 } else { 337 $sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'"; 338 } 339 } 340 } 341 if (count($sqlwhere) > 0) { 342 $sql .= ' AND ('.implode(' '.$filtermode.' ', $sqlwhere).')'; 343 } 344 345 if (!empty($sortfield)) { 346 $sql .= $this->db->order($sortfield, $sortorder); 347 } 348 if (!empty($limit)) { 349 $sql .= ' '.$this->db->plimit($limit, $offset); 350 } 351 352 $resql = $this->db->query($sql); 353 if ($resql) { 354 $num = $this->db->num_rows($resql); 355 $i = 0; 356 while ($i < ($limit ? min($limit, $num) : $num)) { 357 $obj = $this->db->fetch_object($resql); 358 359 $record = new self($this->db); 360 $record->setVarsFromFetchObj($obj); 361 362 $records[$record->id] = $record; 363 364 $i++; 365 } 366 $this->db->free($resql); 367 368 return $records; 369 } else { 370 $this->errors[] = 'Error '.$this->db->lasterror(); 371 dol_syslog(__METHOD__.' '.join(',', $this->errors), LOG_ERR); 372 373 return -1; 374 } 375 } 376 377 /** 378 * Update object into database 379 * 380 * @param User $user User that modifies 381 * @param bool $notrigger false=launch triggers after, true=disable triggers 382 * @return int <0 if KO, >0 if OK 383 */ 384 public function update(User $user, $notrigger = false) 385 { 386 return $this->updateCommon($user, $notrigger); 387 } 388 389 /** 390 * Delete object in database 391 * 392 * @param User $user User that deletes 393 * @param bool $notrigger false=launch triggers after, true=disable triggers 394 * @return int <0 if KO, >0 if OK 395 */ 396 public function delete(User $user, $notrigger = false) 397 { 398 return $this->deleteCommon($user, $notrigger); 399 } 400 401 /** 402 * Validate object 403 * 404 * @param User $user User making status change 405 * @param int $notrigger 1=Does not execute triggers, 0= execute triggers 406 * @return int <=0 if OK, 0=Nothing done, >0 if KO 407 */ 408 public function validate($user, $notrigger = 0) 409 { 410 global $conf, $langs; 411 412 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 413 414 $error = 0; 415 416 // Protection 417 if ($this->status == self::STATUS_VALIDATED) { 418 dol_syslog(get_class($this)."::validate action abandonned: already validated", LOG_WARNING); 419 return 0; 420 } 421 422 $now = dol_now(); 423 424 $this->db->begin(); 425 426 // Define new ref 427 if (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life 428 $num = $this->getNextNumRef(); 429 } else { 430 $num = $this->ref; 431 } 432 $this->newref = $num; 433 434 if (!empty($num)) { 435 // Validate 436 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element; 437 $sql .= " SET ref = '".$this->db->escape($num)."',"; 438 $sql .= " status = ".self::STATUS_VALIDATED; 439 if (!empty($this->fields['date_validation'])) $sql .= ", date_validation = '".$this->db->idate($now)."'"; 440 if (!empty($this->fields['fk_user_valid'])) $sql .= ", fk_user_valid = ".$user->id; 441 $sql .= " WHERE rowid = ".((int) $this->id); 442 443 dol_syslog(get_class($this)."::validate()", LOG_DEBUG); 444 $resql = $this->db->query($sql); 445 if (!$resql) { 446 dol_print_error($this->db); 447 $this->error = $this->db->lasterror(); 448 $error++; 449 } 450 451 if (!$error && !$notrigger) { 452 // Call trigger 453 $result = $this->call_trigger('PRODUCTFOURNISSEURPRICE_VALIDATE', $user); 454 if ($result < 0) $error++; 455 // End call triggers 456 } 457 } 458 459 if (!$error) { 460 $this->oldref = $this->ref; 461 462 // Rename directory if dir was a temporary ref 463 if (preg_match('/^[\(]?PROV/i', $this->ref)) { 464 // Now we rename also files into index 465 $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'productfournisseurprice/".$this->db->escape($this->newref)."'"; 466 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'productfournisseurprice/".$this->db->escape($this->ref)."' and entity = ".$conf->entity; 467 $resql = $this->db->query($sql); 468 if (!$resql) { $error++; $this->error = $this->db->lasterror(); } 469 470 // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments 471 $oldref = dol_sanitizeFileName($this->ref); 472 $newref = dol_sanitizeFileName($num); 473 $dirsource = $conf->buypricehistory->dir_output.'/productfournisseurprice/'.$oldref; 474 $dirdest = $conf->buypricehistory->dir_output.'/productfournisseurprice/'.$newref; 475 if (!$error && file_exists($dirsource)) { 476 dol_syslog(get_class($this)."::validate() rename dir ".$dirsource." into ".$dirdest); 477 478 if (@rename($dirsource, $dirdest)) { 479 dol_syslog("Rename ok"); 480 // Rename docs starting with $oldref with $newref 481 $listoffiles = dol_dir_list($conf->buypricehistory->dir_output.'/productfournisseurprice/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/')); 482 foreach ($listoffiles as $fileentry) { 483 $dirsource = $fileentry['name']; 484 $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource); 485 $dirsource = $fileentry['path'].'/'.$dirsource; 486 $dirdest = $fileentry['path'].'/'.$dirdest; 487 @rename($dirsource, $dirdest); 488 } 489 } 490 } 491 } 492 } 493 494 // Set new ref and current status 495 if (!$error) { 496 $this->ref = $num; 497 $this->status = self::STATUS_VALIDATED; 498 } 499 500 if (!$error) { 501 $this->db->commit(); 502 return 1; 503 } else { 504 $this->db->rollback(); 505 return -1; 506 } 507 } 508 509 510 /** 511 * Set draft status 512 * 513 * @param User $user Object user that modify 514 * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers 515 * @return int <0 if KO, >0 if OK 516 */ 517 public function setDraft($user, $notrigger = 0) 518 { 519 // Protection 520 if ($this->status <= self::STATUS_DRAFT) { 521 return 0; 522 } 523 524 return $this->setStatusCommon($user, self::STATUS_DRAFT, $notrigger, 'PRODUCTFOURNISSEURPRICE_UNVALIDATE'); 525 } 526 527 /** 528 * Set cancel status 529 * 530 * @param User $user Object user that modify 531 * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers 532 * @return int <0 if KO, 0=Nothing done, >0 if OK 533 */ 534 public function cancel($user, $notrigger = 0) 535 { 536 // Protection 537 if ($this->status != self::STATUS_VALIDATED) { 538 return 0; 539 } 540 541 return $this->setStatusCommon($user, self::STATUS_CANCELED, $notrigger, 'PRODUCTFOURNISSEURPRICE_CANCEL'); 542 } 543 544 /** 545 * Set back to validated status 546 * 547 * @param User $user Object user that modify 548 * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers 549 * @return int <0 if KO, 0=Nothing done, >0 if OK 550 */ 551 public function reopen($user, $notrigger = 0) 552 { 553 // Protection 554 if ($this->status != self::STATUS_CANCELED) { 555 return 0; 556 } 557 558 return $this->setStatusCommon($user, self::STATUS_VALIDATED, $notrigger, 'PRODUCTFOURNISSEURPRICE_REOPEN'); 559 } 560 561 /** 562 * Return a link to the object card (with optionaly the picto) 563 * 564 * @param int $withpicto Include picto in link (0=No picto, 1=Include picto into link, 2=Only picto) 565 * @param string $option On what the link point to ('nolink', ...) 566 * @param int $notooltip 1=Disable tooltip 567 * @param string $morecss Add more css on link 568 * @param int $save_lastsearch_value -1=Auto, 0=No save of lastsearch_values when clicking, 1=Save lastsearch_values whenclicking 569 * @return string String with URL 570 */ 571 public function getNomUrl($withpicto = 0, $option = '', $notooltip = 0, $morecss = '', $save_lastsearch_value = -1) 572 { 573 global $conf, $langs, $hookmanager; 574 575 if (!empty($conf->dol_no_mouse_hover)) $notooltip = 1; // Force disable tooltips 576 577 $result = ''; 578 579 $label = img_picto('', $this->picto).' <u>'.$langs->trans("ProductFournisseurPrice").'</u>'; 580 if (isset($this->status)) { 581 $label .= ' '.$this->getLibStatut(5); 582 } 583 $label .= '<br>'; 584 $label .= '<b>'.$langs->trans('Ref').':</b> '.$this->ref; 585 586 $url = dol_buildpath('/buypricehistory/productfournisseurprice_card.php', 1).'?id='.$this->id; 587 588 if ($option != 'nolink') { 589 // Add param to save lastsearch_values or not 590 $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0); 591 if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) $add_save_lastsearch_values = 1; 592 if ($add_save_lastsearch_values) $url .= '&save_lastsearch_values=1'; 593 } 594 595 $linkclose = ''; 596 if (empty($notooltip)) { 597 if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) { 598 $label = $langs->trans("ShowProductFournisseurPrice"); 599 $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"'; 600 } 601 $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"'; 602 $linkclose .= ' class="classfortooltip'.($morecss ? ' '.$morecss : '').'"'; 603 } else $linkclose = ($morecss ? ' class="'.$morecss.'"' : ''); 604 605 $linkstart = '<a href="'.$url.'"'; 606 $linkstart .= $linkclose.'>'; 607 $linkend = '</a>'; 608 609 $result .= $linkstart; 610 611 if (empty($this->showphoto_on_popup)) { 612 if ($withpicto) $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1); 613 } else { 614 if ($withpicto) { 615 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 616 617 list($class, $module) = explode('@', $this->picto); 618 $upload_dir = $conf->$module->multidir_output[$conf->entity]."/$class/".dol_sanitizeFileName($this->ref); 619 $filearray = dol_dir_list($upload_dir, "files"); 620 $filename = $filearray[0]['name']; 621 if (!empty($filename)) { 622 $pospoint = strpos($filearray[0]['name'], '.'); 623 624 $pathtophoto = $class.'/'.$this->ref.'/thumbs/'.substr($filename, 0, $pospoint).'_mini'.substr($filename, $pospoint); 625 if (empty($conf->global->{strtoupper($module.'_'.$class).'_FORMATLISTPHOTOSASUSERS'})) { 626 $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><div class="photoref"><img class="photo'.$module.'" alt="No photo" border="0" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$module.'&entity='.$conf->entity.'&file='.urlencode($pathtophoto).'"></div></div>'; 627 } else { 628 $result .= '<div class="floatleft inline-block valignmiddle divphotoref"><img class="photouserphoto userphoto" alt="No photo" border="0" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$module.'&entity='.$conf->entity.'&file='.urlencode($pathtophoto).'"></div>'; 629 } 630 631 $result .= '</div>'; 632 } else { 633 $result .= img_object(($notooltip ? '' : $label), ($this->picto ? $this->picto : 'generic'), ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1); 634 } 635 } 636 } 637 638 if ($withpicto != 2) $result .= $this->ref; 639 640 $result .= $linkend; 641 //if ($withpicto != 2) $result.=(($addlabel && $this->label) ? $sep . dol_trunc($this->label, ($addlabel > 1 ? $addlabel : 0)) : ''); 642 643 global $action, $hookmanager; 644 $hookmanager->initHooks(array('productfournisseurpricedao')); 645 $parameters = array('id'=>$this->id, 'getnomurl'=>$result); 646 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks 647 if ($reshook > 0) $result = $hookmanager->resPrint; 648 else $result .= $hookmanager->resPrint; 649 650 return $result; 651 } 652 653 /** 654 * Return the label of the status 655 * 656 * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto 657 * @return string Label of status 658 */ 659 public function getLibStatut($mode = 0) 660 { 661 return $this->LibStatut($this->status, $mode); 662 } 663 664 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 665 /** 666 * Return the status 667 * 668 * @param int $status Id status 669 * @param int $mode 0=long label, 1=short label, 2=Picto + short label, 3=Picto, 4=Picto + long label, 5=Short label + Picto, 6=Long label + Picto 670 * @return string Label of status 671 */ 672 public function LibStatut($status, $mode = 0) 673 { 674 // phpcs:enable 675 if (empty($this->labelStatus) || empty($this->labelStatusShort)) { 676 global $langs; 677 //$langs->load("buypricehistory@buypricehistory"); 678 $this->labelStatus[self::STATUS_DRAFT] = $langs->trans('Draft'); 679 $this->labelStatus[self::STATUS_VALIDATED] = $langs->trans('Enabled'); 680 $this->labelStatus[self::STATUS_CANCELED] = $langs->trans('Disabled'); 681 $this->labelStatusShort[self::STATUS_DRAFT] = $langs->trans('Draft'); 682 $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->trans('Enabled'); 683 $this->labelStatusShort[self::STATUS_CANCELED] = $langs->trans('Disabled'); 684 } 685 686 $statusType = 'status'.$status; 687 //if ($status == self::STATUS_VALIDATED) $statusType = 'status1'; 688 if ($status == self::STATUS_CANCELED) $statusType = 'status6'; 689 690 return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode); 691 } 692 693 /** 694 * Load the info information in the object 695 * 696 * @param int $id Id of object 697 * @return void 698 */ 699 public function info($id) 700 { 701 $sql = 'SELECT rowid, date_creation as datec, tms as datem,'; 702 $sql .= ' fk_user_creat, fk_user_modif'; 703 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element.' as t'; 704 $sql .= ' WHERE t.rowid = '.((int) $id); 705 $result = $this->db->query($sql); 706 if ($result) { 707 if ($this->db->num_rows($result)) { 708 $obj = $this->db->fetch_object($result); 709 $this->id = $obj->rowid; 710 if ($obj->fk_user_author) { 711 $cuser = new User($this->db); 712 $cuser->fetch($obj->fk_user_author); 713 $this->user_creation = $cuser; 714 } 715 716 if ($obj->fk_user_valid) { 717 $vuser = new User($this->db); 718 $vuser->fetch($obj->fk_user_valid); 719 $this->user_validation = $vuser; 720 } 721 722 if ($obj->fk_user_cloture) { 723 $cluser = new User($this->db); 724 $cluser->fetch($obj->fk_user_cloture); 725 $this->user_cloture = $cluser; 726 } 727 728 $this->date_creation = $this->db->jdate($obj->datec); 729 $this->date_modification = $this->db->jdate($obj->datem); 730 $this->date_validation = $this->db->jdate($obj->datev); 731 } 732 733 $this->db->free($result); 734 } else { 735 dol_print_error($this->db); 736 } 737 } 738 739 /** 740 * Initialise object with example values 741 * Id must be 0 if object instance is a specimen 742 * 743 * @return void 744 */ 745 public function initAsSpecimen() 746 { 747 $this->initAsSpecimenCommon(); 748 } 749 750 /** 751 * Returns the reference to the following non used object depending on the active numbering module. 752 * 753 * @return string Object free reference 754 */ 755 public function getNextNumRef() 756 { 757 global $langs, $conf; 758 $langs->load("buypricehistory@buypricehistory"); 759 760 if (empty($conf->global->BUYPRICEHISTORY_PRODUCTFOURNISSEURPRICE_ADDON)) { 761 $conf->global->BUYPRICEHISTORY_PRODUCTFOURNISSEURPRICE_ADDON = 'mod_productfournisseurprice_standard'; 762 } 763 764 if (!empty($conf->global->BUYPRICEHISTORY_PRODUCTFOURNISSEURPRICE_ADDON)) { 765 $mybool = false; 766 767 $file = $conf->global->BUYPRICEHISTORY_PRODUCTFOURNISSEURPRICE_ADDON.".php"; 768 $classname = $conf->global->BUYPRICEHISTORY_PRODUCTFOURNISSEURPRICE_ADDON; 769 770 // Include file with class 771 $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']); 772 foreach ($dirmodels as $reldir) { 773 $dir = dol_buildpath($reldir."core/modules/buypricehistory/"); 774 775 // Load file with numbering class (if found) 776 $mybool |= @include_once $dir.$file; 777 } 778 779 if ($mybool === false) { 780 dol_print_error('', "Failed to include file ".$file); 781 return ''; 782 } 783 784 if (class_exists($classname)) { 785 $obj = new $classname(); 786 $numref = $obj->getNextValue($this); 787 788 if ($numref != '' && $numref != '-1') { 789 return $numref; 790 } else { 791 $this->error = $obj->error; 792 //dol_print_error($this->db,get_class($this)."::getNextNumRef ".$obj->error); 793 return ""; 794 } 795 } else { 796 print $langs->trans("Error")." ".$langs->trans("ClassNotFound").' '.$classname; 797 return ""; 798 } 799 } else { 800 print $langs->trans("ErrorNumberingModuleNotSetup", $this->element); 801 return ""; 802 } 803 } 804 805 /** 806 * Create a document onto disk according to template module. 807 * 808 * @param string $modele Force template to use ('' to not force) 809 * @param Translate $outputlangs objet lang a utiliser pour traduction 810 * @param int $hidedetails Hide details of lines 811 * @param int $hidedesc Hide description 812 * @param int $hideref Hide ref 813 * @param null|array $moreparams Array to provide more information 814 * @return int 0 if KO, 1 if OK 815 */ 816 public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null) 817 { 818 global $conf, $langs; 819 820 $result = 0; 821 $includedocgeneration = 0; 822 823 $langs->load("buypricehistory@buypricehistory"); 824 825 if (!dol_strlen($modele)) { 826 $modele = 'standard_productfournisseurprice'; 827 828 if (!empty($this->model_pdf)) { 829 $modele = $this->model_pdf; 830 } elseif (!empty($conf->global->PRODUCTFOURNISSEURPRICE_ADDON_PDF)) { 831 $modele = $conf->global->PRODUCTFOURNISSEURPRICE_ADDON_PDF; 832 } 833 } 834 835 $modelpath = "core/modules/buypricehistory/doc/"; 836 837 if ($includedocgeneration && !empty($modele)) { 838 $result = $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams); 839 } 840 841 return $result; 842 } 843} 844