1<?php 2/* Copyright (C) 2006-2015 Laurent Destailleur <eldy@users.sourceforge.net> 3 * Copyright (C) 2005-2013 Regis Houssin <regis.houssin@inodbox.com> 4 * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es> 5 * Copyright (C) 2012-2013 Christophe Battarel <christophe.battarel@altairis.fr> 6 * Copyright (C) 2011-2019 Philippe Grand <philippe.grand@atoo-net.com> 7 * Copyright (C) 2012-2015 Marcos García <marcosgdf@gmail.com> 8 * Copyright (C) 2012-2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr> 9 * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr> 10 * Copyright (C) 2015 Alexandre Spangaro <aspangaro@open-dsi.fr> 11 * Copyright (C) 2016 Bahfir abbes <dolipar@dolipar.org> 12 * Copyright (C) 2017 ATM Consulting <support@atm-consulting.fr> 13 * Copyright (C) 2017-2019 Nicolas ZABOURI <info@inovea-conseil.com> 14 * Copyright (C) 2017 Rui Strecht <rui.strecht@aliartalentos.com> 15 * Copyright (C) 2018-2020 Frédéric France <frederic.france@netlogic.fr> 16 * Copyright (C) 2018 Josep Lluís Amador <joseplluis@lliuretic.cat> 17 * 18 * This program is free software; you can redistribute it and/or modify 19 * it under the terms of the GNU General Public License as published by 20 * the Free Software Foundation; either version 3 of the License, or 21 * (at your option) any later version. 22 * 23 * This program is distributed in the hope that it will be useful, 24 * but WITHOUT ANY WARRANTY; without even the implied warranty of 25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 26 * GNU General Public License for more details. 27 * 28 * You should have received a copy of the GNU General Public License 29 * along with this program. If not, see <https://www.gnu.org/licenses/>. 30 */ 31 32/** 33 * \file htdocs/core/class/commonobject.class.php 34 * \ingroup core 35 * \brief File of parent class of all other business classes (invoices, contracts, proposals, orders, ...) 36 */ 37 38 39/** 40 * Parent class of all other business classes (invoices, contracts, proposals, orders, ...) 41 */ 42abstract class CommonObject 43{ 44 /** 45 * @var DoliDb Database handler (result of a new DoliDB) 46 */ 47 public $db; 48 49 /** 50 * @var int The object identifier 51 */ 52 public $id; 53 54 /** 55 * @var int The environment ID when using a multicompany module 56 */ 57 public $entity; 58 59 /** 60 * @var string Error string 61 * @see $errors 62 */ 63 public $error; 64 65 /** 66 * @var string Error string that is hidden but can be used to store complementatry technical code. 67 */ 68 public $errorhidden; 69 70 /** 71 * @var string[] Array of error strings 72 */ 73 public $errors = array(); 74 75 /** 76 * @var string ID to identify managed object 77 */ 78 public $element; 79 80 /** 81 * @var string Name of table without prefix where object is stored 82 */ 83 public $table_element; 84 85 /** 86 * @var string Name of subtable line 87 */ 88 public $table_element_line = ''; 89 90 /** 91 * @var string Key value used to track if data is coming from import wizard 92 */ 93 public $import_key; 94 95 /** 96 * @var mixed Contains data to manage extrafields 97 */ 98 public $array_options = array(); 99 100 /** 101 * @var mixed Array to store alternative languages values of object 102 */ 103 public $array_languages = null; // Value is array() when load already tried 104 105 /** 106 * @var int[][] Array of linked objects ids. Loaded by ->fetchObjectLinked 107 */ 108 public $linkedObjectsIds; 109 110 /** 111 * @var mixed Array of linked objects. Loaded by ->fetchObjectLinked 112 */ 113 public $linkedObjects; 114 115 /** 116 * @var Object To store a cloned copy of object before to edit it and keep track of old properties 117 */ 118 public $oldcopy; 119 120 /** 121 * @var string Column name of the ref field. 122 */ 123 protected $table_ref_field = ''; 124 125 126 127 // Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them. 128 129 /** 130 * @var array<string,mixed> Can be used to pass information when only object is provided to method 131 */ 132 public $context = array(); 133 134 /** 135 * @var string Contains canvas name if record is an alternative canvas record 136 */ 137 public $canvas; 138 139 /** 140 * @var Project The related project 141 * @see fetch_projet() 142 */ 143 public $project; 144 145 /** 146 * @var int The related project ID 147 * @see setProject(), project 148 */ 149 public $fk_project; 150 151 /** 152 * @deprecated 153 * @see project 154 */ 155 public $projet; 156 157 /** 158 * @var Contact a related contact 159 * @see fetch_contact() 160 */ 161 public $contact; 162 163 /** 164 * @var int The related contact ID 165 * @see fetch_contact() 166 */ 167 public $contact_id; 168 169 /** 170 * @var Societe A related thirdparty 171 * @see fetch_thirdparty() 172 */ 173 public $thirdparty; 174 175 /** 176 * @var User A related user 177 * @see fetch_user() 178 */ 179 public $user; 180 181 /** 182 * @var string The type of originating object ('commande', 'facture', ...) 183 * @see fetch_origin() 184 */ 185 public $origin; 186 187 /** 188 * @var int The id of originating object 189 * @see fetch_origin() 190 */ 191 public $origin_id; 192 193 /** 194 * @var string The object's reference 195 */ 196 public $ref; 197 198 /** 199 * @var string An external reference for the object 200 */ 201 public $ref_ext; 202 203 /** 204 * @var string The object's previous reference 205 */ 206 public $ref_previous; 207 208 /** 209 * @var string The object's next reference 210 */ 211 public $ref_next; 212 213 /** 214 * @var string Ref to store on object to save the new ref to use for example when making a validate() of an object 215 */ 216 public $newref; 217 218 /** 219 * @var int The object's status 220 * @see setStatut() 221 */ 222 public $statut; 223 224 /** 225 * @var int The object's status 226 * @see setStatut() 227 */ 228 public $status; 229 230 /** 231 * @var string 232 * @see getFullAddress() 233 */ 234 public $country; 235 236 /** 237 * @var int 238 * @see getFullAddress(), country 239 */ 240 public $country_id; 241 242 /** 243 * @var string 244 * @see getFullAddress(), isInEEC(), country 245 */ 246 public $country_code; 247 248 /** 249 * @var string 250 * @see getFullAddress() 251 */ 252 public $state; 253 254 /** 255 * @var int 256 * @see getFullAddress(), state 257 */ 258 public $state_id; 259 260 /** 261 * @var string 262 * @see getFullAddress(), $state 263 */ 264 public $state_code; 265 266 /** 267 * @var int 268 * @see getFullAddress(), $region_code, $region 269 */ 270 public $region_id; 271 272 /** 273 * @var string 274 * @see getFullAddress(), $region_id, $region 275 */ 276 public $region_code; 277 278 /** 279 * @var string 280 * @see getFullAddress(), $region_id, $region_code 281 */ 282 public $region; 283 284 /** 285 * @var int 286 * @see fetch_barcode() 287 */ 288 public $barcode_type; 289 290 /** 291 * @var string 292 * @see fetch_barcode(), barcode_type 293 */ 294 public $barcode_type_code; 295 296 /** 297 * @var string 298 * @see fetch_barcode(), barcode_type 299 */ 300 public $barcode_type_label; 301 302 /** 303 * @var string 304 * @see fetch_barcode(), barcode_type 305 */ 306 public $barcode_type_coder; 307 308 /** 309 * @var int Payment method ID (cheque, cash, ...) 310 * @see setPaymentMethods() 311 */ 312 public $mode_reglement_id; 313 314 /** 315 * @var int Payment terms ID 316 * @see setPaymentTerms() 317 */ 318 public $cond_reglement_id; 319 320 /** 321 * @var int Demand reason ID 322 */ 323 public $demand_reason_id; 324 325 /** 326 * @var int Transport mode ID (For module intracomm report) 327 * @see setTransportMode() 328 */ 329 public $transport_mode_id; 330 331 /** 332 * @var int Payment terms ID 333 * @deprecated Kept for compatibility 334 * @see cond_reglement_id; 335 */ 336 public $cond_reglement; 337 338 /** 339 * @var int Delivery address ID 340 * @see setDeliveryAddress() 341 * @deprecated 342 */ 343 public $fk_delivery_address; 344 345 /** 346 * @var int Shipping method ID 347 * @see setShippingMethod() 348 */ 349 public $shipping_method_id; 350 351 /** 352 * @var string 353 * @see SetDocModel() 354 */ 355 public $model_pdf; 356 357 /** 358 * @var string 359 * Contains relative path of last generated main file 360 */ 361 public $last_main_doc; 362 363 /** 364 * @var int Bank account ID sometimes, ID of record into llx_bank sometimes 365 * @deprecated 366 * @see $fk_account 367 */ 368 public $fk_bank; 369 370 /** 371 * @var int Bank account ID 372 * @see SetBankAccount() 373 */ 374 public $fk_account; 375 376 /** 377 * @var string Open ID 378 */ 379 public $openid; 380 381 /** 382 * @var string Public note 383 * @see update_note() 384 */ 385 public $note_public; 386 387 /** 388 * @var string Private note 389 * @see update_note() 390 */ 391 public $note_private; 392 393 /** 394 * @deprecated 395 * @see $note_private 396 */ 397 public $note; 398 399 /** 400 * @var float Total amount before taxes 401 * @see update_price() 402 */ 403 public $total_ht; 404 405 /** 406 * @var float Total VAT amount 407 * @see update_price() 408 */ 409 public $total_tva; 410 411 /** 412 * @var float Total local tax 1 amount 413 * @see update_price() 414 */ 415 public $total_localtax1; 416 417 /** 418 * @var float Total local tax 2 amount 419 * @see update_price() 420 */ 421 public $total_localtax2; 422 423 /** 424 * @var float Total amount with taxes 425 * @see update_price() 426 */ 427 public $total_ttc; 428 429 /** 430 * @var CommonObjectLine[] 431 */ 432 public $lines; 433 434 /** 435 * @var mixed Contains comments 436 * @see fetchComments() 437 */ 438 public $comments = array(); 439 440 /** 441 * @var string The name 442 */ 443 public $name; 444 445 /** 446 * @var string The lastname 447 */ 448 public $lastname; 449 450 /** 451 * @var string The firstname 452 */ 453 public $firstname; 454 455 /** 456 * @var string The civility code, not an integer 457 */ 458 public $civility_id; 459 460 // Dates 461 /** 462 * @var integer|string date_creation 463 */ 464 public $date_creation; 465 466 /** 467 * @var integer|string $date_validation; 468 */ 469 public $date_validation; // Date validation 470 471 /** 472 * @var integer|string $date_modification; 473 */ 474 public $date_modification; // Date last change (tms field) 475 476 public $next_prev_filter; 477 478 /** 479 * @var int 1 if object is specimen 480 */ 481 public $specimen = 0; 482 483 /** 484 * @var int Id of contact to send object (used by the trigger of module Agenda) 485 */ 486 public $sendtoid; 487 488 /** 489 * @var float Amount already paid (used to show correct status) 490 */ 491 public $alreadypaid; 492 493 /** 494 * @var array List of child tables. To test if we can delete object. 495 */ 496 protected $childtables = array(); 497 498 /** 499 * @var array List of child tables. To know object to delete on cascade. 500 * If name is like '@ClassName:FilePathClass:ParentFkFieldName', it will 501 * call method deleteByParentField(parentId, ParentFkFieldName) to fetch and delete child object. 502 */ 503 protected $childtablesoncascade = array(); 504 505 506 // No constructor as it is an abstract class 507 /** 508 * Check an object id/ref exists 509 * If you don't need/want to instantiate object and just need to know if object exists, use this method instead of fetch 510 * 511 * @param string $element String of element ('product', 'facture', ...) 512 * @param int $id Id of object 513 * @param string $ref Ref of object to check 514 * @param string $ref_ext Ref ext of object to check 515 * @return int <0 if KO, 0 if OK but not found, >0 if OK and exists 516 */ 517 public static function isExistingObject($element, $id, $ref = '', $ref_ext = '') 518 { 519 global $db, $conf; 520 521 $sql = "SELECT rowid, ref, ref_ext"; 522 $sql .= " FROM ".MAIN_DB_PREFIX.$element; 523 $sql .= " WHERE entity IN (".getEntity($element).")"; 524 525 if ($id > 0) $sql .= " AND rowid = ".$db->escape($id); 526 elseif ($ref) $sql .= " AND ref = '".$db->escape($ref)."'"; 527 elseif ($ref_ext) $sql .= " AND ref_ext = '".$db->escape($ref_ext)."'"; 528 else { 529 $error = 'ErrorWrongParameters'; 530 dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR); 531 return -1; 532 } 533 if ($ref || $ref_ext) $sql .= " AND entity = ".$conf->entity; 534 535 dol_syslog(get_class()."::isExistingObject", LOG_DEBUG); 536 $resql = $db->query($sql); 537 if ($resql) 538 { 539 $num = $db->num_rows($resql); 540 if ($num > 0) return 1; 541 else return 0; 542 } 543 return -1; 544 } 545 546 /** 547 * Method to output saved errors 548 * 549 * @return string String with errors 550 */ 551 public function errorsToString() 552 { 553 return $this->error.(is_array($this->errors) ? (($this->error != '' ? ', ' : '').join(', ', $this->errors)) : ''); 554 } 555 556 557 /** 558 * Return customer ref for screen output. 559 * 560 * @param string $objref Customer ref 561 * @return string Customer ref formated 562 */ 563 public function getFormatedCustomerRef($objref) 564 { 565 global $hookmanager; 566 567 $parameters = array('objref'=>$objref); 568 $action = ''; 569 $reshook = $hookmanager->executeHooks('getFormatedCustomerRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks 570 if ($reshook > 0) 571 { 572 return $hookmanager->resArray['objref']; 573 } 574 return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : ''); 575 } 576 577 /** 578 * Return supplier ref for screen output. 579 * 580 * @param string $objref Supplier ref 581 * @return string Supplier ref formated 582 */ 583 public function getFormatedSupplierRef($objref) 584 { 585 global $hookmanager; 586 587 $parameters = array('objref'=>$objref); 588 $action = ''; 589 $reshook = $hookmanager->executeHooks('getFormatedSupplierRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks 590 if ($reshook > 0) 591 { 592 return $hookmanager->resArray['objref']; 593 } 594 return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : ''); 595 } 596 597 /** 598 * Return full name (civility+' '+name+' '+lastname) 599 * 600 * @param Translate $langs Language object for translation of civility (used only if option is 1) 601 * @param int $option 0=No option, 1=Add civility 602 * @param int $nameorder -1=Auto, 0=Lastname+Firstname, 1=Firstname+Lastname, 2=Firstname, 3=Firstname if defined else lastname, 4=Lastname, 5=Lastname if defined else firstname 603 * @param int $maxlen Maximum length 604 * @return string String with full name 605 */ 606 public function getFullName($langs, $option = 0, $nameorder = -1, $maxlen = 0) 607 { 608 //print "lastname=".$this->lastname." name=".$this->name." nom=".$this->nom."<br>\n"; 609 $lastname = $this->lastname; 610 $firstname = $this->firstname; 611 if (empty($lastname)) $lastname = (isset($this->lastname) ? $this->lastname : (isset($this->name) ? $this->name : (isset($this->nom) ? $this->nom : (isset($this->societe) ? $this->societe : (isset($this->company) ? $this->company : ''))))); 612 613 $ret = ''; 614 if ($option && $this->civility_code) 615 { 616 if ($langs->transnoentitiesnoconv("Civility".$this->civility_code) != "Civility".$this->civility_code) $ret .= $langs->transnoentitiesnoconv("Civility".$this->civility_code).' '; 617 else $ret .= $this->civility_code.' '; 618 } 619 620 $ret .= dolGetFirstLastname($firstname, $lastname, $nameorder); 621 622 return dol_trunc($ret, $maxlen); 623 } 624 625 /** 626 * Set to upper or ucwords/lower if needed 627 * 628 * @return void; 629 */ 630 public function setUpperOrLowerCase() 631 { 632 global $conf; 633 if (!empty($conf->global->MAIN_FIRST_TO_UPPER)) { 634 $this->lastname = dol_ucwords(dol_strtolower($this->lastname)); 635 $this->firstname = dol_ucwords(dol_strtolower($this->firstname)); 636 $this->name = dol_ucwords(dol_strtolower($this->name)); 637 } 638 if (!empty($conf->global->MAIN_ALL_TO_UPPER)) { 639 $this->lastname = dol_strtoupper($this->lastname); 640 $this->name = dol_strtoupper($this->name); 641 } 642 if (!empty($conf->global->MAIN_ALL_TOWN_TO_UPPER)) { 643 $this->town = dol_strtoupper($this->town); 644 } 645 } 646 647 /** 648 * Return clicable link of object (with eventually picto) 649 * 650 * @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link) 651 * @return string HTML Code for Kanban thumb. 652 */ 653 public function getKanbanView($option = '') 654 { 655 $return = '<div class="box-flex-item">'; 656 $return .= '<div class="info-box info-box-sm">'; 657 $return .= '<span class="info-box-icon bg-infobox-action">'; 658 $return .= '<i class="fa fa-dol-action"></i>'; // Can be image 659 $return .= '</span>'; 660 $return .= '<div class="info-box-content">'; 661 $return .= '<span class="info-box-title">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>'; 662 $return .= '</div>'; 663 $return .= '</div>'; 664 $return .= '</div>'; 665 666 return $return; 667 } 668 669 /** 670 * Return full address of contact 671 * 672 * @param int $withcountry 1=Add country into address string 673 * @param string $sep Separator to use to build string 674 * @param int $withregion 1=Add region into address string 675 * @param string $extralangcode User extralanguages as value 676 * @return string Full address string 677 */ 678 public function getFullAddress($withcountry = 0, $sep = "\n", $withregion = 0, $extralangcode = '') 679 { 680 if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country))) 681 { 682 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; 683 $tmparray = getCountry($this->country_id, 'all'); 684 $this->country_code = $tmparray['code']; 685 $this->country = $tmparray['label']; 686 } 687 688 if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_code))) 689 { 690 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php'; 691 $tmparray = getState($this->state_id, 'all', 0, 1); 692 $this->state_code = $tmparray['code']; 693 $this->state = $tmparray['label']; 694 $this->region_code = $tmparray['region_code']; 695 $this->region = $tmparray['region']; 696 } 697 698 return dol_format_address($this, $withcountry, $sep, '', 0, $extralangcode); 699 } 700 701 702 /** 703 * Return full address for banner 704 * 705 * @param string $htmlkey HTML id to make banner content unique 706 * @param Object $object Object (thirdparty, thirdparty of contact for contact, null for a member) 707 * @return string Full address string 708 */ 709 public function getBannerAddress($htmlkey, $object) 710 { 711 global $conf, $langs, $form, $extralanguages; 712 713 $countriesusingstate = array('AU', 'US', 'IN', 'GB', 'ES', 'UK', 'TR'); // See also option MAIN_FORCE_STATE_INTO_ADDRESS 714 715 $contactid = 0; 716 $thirdpartyid = 0; 717 $elementforaltlanguage = $this->element; 718 if ($this->element == 'societe') { 719 $thirdpartyid = $this->id; 720 } 721 if ($this->element == 'contact') { 722 $contactid = $this->id; 723 $thirdpartyid = $object->fk_soc; 724 } 725 if ($this->element == 'user') { 726 $contactid = $this->contact_id; 727 $thirdpartyid = $object->fk_soc; 728 } 729 730 $out = ''; 731 732 $outdone = 0; 733 $coords = $this->getFullAddress(1, ', ', $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT); 734 if ($coords) 735 { 736 if (!empty($conf->use_javascript_ajax)) 737 { 738 // Add picto with tooltip on map 739 $namecoords = ''; 740 if ($this->element == 'contact' && !empty($conf->global->MAIN_SHOW_COMPANY_NAME_IN_BANNER_ADDRESS)) 741 { 742 $namecoords .= $object->name.'<br>'; 743 } 744 $namecoords .= $this->getFullName($langs, 1).'<br>'.$coords; 745 // hideonsmatphone because copyToClipboard call jquery dialog that does not work with jmobile 746 $out .= '<a href="#" class="hideonsmartphone" onclick="return copyToClipboard(\''.dol_escape_js($namecoords).'\',\''.dol_escape_js($langs->trans("HelpCopyToClipboard")).'\');">'; 747 $out .= img_picto($langs->trans("Address"), 'map-marker-alt'); 748 $out .= '</a> '; 749 } 750 $out .= dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', '); $outdone++; 751 $outdone++; 752 753 // List of extra languages 754 $arrayoflangcode = array(); 755 if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) $arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE; 756 757 if (is_array($arrayoflangcode) && count($arrayoflangcode)) { 758 if (!is_object($extralanguages)) { 759 include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php'; 760 $extralanguages = new ExtraLanguages($this->db); 761 } 762 $extralanguages->fetch_name_extralanguages($elementforaltlanguage); 763 764 if (!empty($extralanguages->attributes[$elementforaltlanguage]['address']) || !empty($extralanguages->attributes[$elementforaltlanguage]['town'])) 765 { 766 $out .= "<!-- alternatelanguage for '".$elementforaltlanguage."' set to fields '".join(',', $extralanguages->attributes[$elementforaltlanguage])."' -->\n"; 767 $this->fetchValuesForExtraLanguages(); 768 if (!is_object($form)) $form = new Form($this->db); 769 $htmltext = ''; 770 // If there is extra languages 771 foreach ($arrayoflangcode as $extralangcode) { 772 $s = picto_from_langcode($extralangcode, 'class="pictoforlang paddingright"'); 773 $coords = $this->getFullAddress(1, ', ', $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT, $extralangcode); 774 $htmltext .= $s.dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', '); 775 } 776 $out .= $form->textwithpicto('', $htmltext, -1, 'language', 'opacitymedium paddingleft'); 777 } 778 } 779 } 780 781 if (!in_array($this->country_code, $countriesusingstate) && empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS) // If MAIN_FORCE_STATE_INTO_ADDRESS is on, state is already returned previously with getFullAddress 782 && empty($conf->global->SOCIETE_DISABLE_STATE) && $this->state) 783 { 784 if (!empty($conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT) && $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT == 1 && $this->region) { 785 $out .= ($outdone ? ' - ' : '').$this->region.' - '.$this->state; 786 } else { 787 $out .= ($outdone ? ' - ' : '').$this->state; 788 } 789 $outdone++; 790 } 791 792 if (!empty($this->phone) || !empty($this->phone_pro) || !empty($this->phone_mobile) || !empty($this->phone_perso) || !empty($this->fax) || !empty($this->office_phone) || !empty($this->user_mobile) || !empty($this->office_fax)) $out .= ($outdone ? '<br>' : ''); 793 if (!empty($this->phone) && empty($this->phone_pro)) { // For objects that store pro phone into ->phone 794 $out .= dol_print_phone($this->phone, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', ' ', 'phone', $langs->trans("PhonePro")); 795 $outdone++; 796 } 797 if (!empty($this->phone_pro)) { 798 $out .= dol_print_phone($this->phone_pro, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', ' ', 'phone', $langs->trans("PhonePro")); 799 $outdone++; 800 } 801 if (!empty($this->phone_mobile)) { 802 $out .= dol_print_phone($this->phone_mobile, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', ' ', 'mobile', $langs->trans("PhoneMobile")); 803 $outdone++; 804 } 805 if (!empty($this->phone_perso)) { 806 $out .= dol_print_phone($this->phone_perso, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', ' ', 'phone', $langs->trans("PhonePerso")); 807 $outdone++; 808 } 809 if (!empty($this->office_phone)) { 810 $out .= dol_print_phone($this->office_phone, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', ' ', 'phone', $langs->trans("PhonePro")); 811 $outdone++; 812 } 813 if (!empty($this->user_mobile)) { 814 $out .= dol_print_phone($this->user_mobile, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', ' ', 'mobile', $langs->trans("PhoneMobile")); 815 $outdone++; 816 } 817 if (!empty($this->fax)) { 818 $out .= dol_print_phone($this->fax, $this->country_code, $contactid, $thirdpartyid, 'AC_FAX', ' ', 'fax', $langs->trans("Fax")); 819 $outdone++; 820 } 821 if (!empty($this->office_fax)) { 822 $out .= dol_print_phone($this->office_fax, $this->country_code, $contactid, $thirdpartyid, 'AC_FAX', ' ', 'fax', $langs->trans("Fax")); 823 $outdone++; 824 } 825 826 if ($out) $out .= '<div style="clear: both;"></div>'; 827 $outdone = 0; 828 if (!empty($this->email)) { 829 $out .= dol_print_email($this->email, $this->id, $object->id, 'AC_EMAIL', 0, 0, 1); 830 $outdone++; 831 } 832 if (!empty($this->url)) { 833 //$out.=dol_print_url($this->url,'_goout',0,1);//steve changed to blank 834 $out .= dol_print_url($this->url, '_blank', 0, 1); 835 $outdone++; 836 } 837 838 if (!empty($conf->socialnetworks->enabled)) { 839 $outsocialnetwork = ''; 840 841 if (is_array($this->socialnetworks) && count($this->socialnetworks) > 0) { 842 $socialnetworksdict = getArrayOfSocialNetworks(); 843 foreach ($this->socialnetworks as $key => $value) { 844 if ($value) { 845 $outsocialnetwork .= dol_print_socialnetworks($value, $this->id, $object->id, $key, $socialnetworksdict); 846 } 847 $outdone++; 848 } 849 } else { // Old code to remove 850 if ($this->skype) $outsocialnetwork .= dol_print_socialnetworks($this->skype, $this->id, $object->id, 'skype'); 851 $outdone++; 852 if ($this->jabberid) $outsocialnetwork .= dol_print_socialnetworks($this->jabberid, $this->id, $object->id, 'jabber'); 853 $outdone++; 854 if ($this->twitter) $outsocialnetwork .= dol_print_socialnetworks($this->twitter, $this->id, $object->id, 'twitter'); 855 $outdone++; 856 if ($this->facebook) $outsocialnetwork .= dol_print_socialnetworks($this->facebook, $this->id, $object->id, 'facebook'); 857 $outdone++; 858 if ($this->linkedin) $outsocialnetwork .= dol_print_socialnetworks($this->linkedin, $this->id, $object->id, 'linkedin'); 859 $outdone++; 860 } 861 862 if ($outsocialnetwork) { 863 $out .= '<div style="clear: both;">'.$outsocialnetwork.'</div>'; 864 } 865 } 866 867 if ($out) return '<!-- BEGIN part to show address block -->'."\n".$out.'<!-- END Part to show address block -->'."\n"; 868 else return ''; 869 } 870 871 /** 872 * Return the link of last main doc file for direct public download. 873 * 874 * @param string $modulepart Module related to document 875 * @param int $initsharekey Init the share key if it was not yet defined 876 * @param int $relativelink 0=Return full external link, 1=Return link relative to root of file 877 * @return string Link or empty string if there is no download link 878 */ 879 public function getLastMainDocLink($modulepart, $initsharekey = 0, $relativelink = 0) 880 { 881 global $user, $dolibarr_main_url_root; 882 883 if (empty($this->last_main_doc)) 884 { 885 return ''; // No way to known which document name to use 886 } 887 888 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php'; 889 $ecmfile = new EcmFiles($this->db); 890 $result = $ecmfile->fetch(0, '', $this->last_main_doc); 891 if ($result < 0) 892 { 893 $this->error = $ecmfile->error; 894 $this->errors = $ecmfile->errors; 895 return -1; 896 } 897 898 if (empty($ecmfile->id)) 899 { 900 // Add entry into index 901 if ($initsharekey) 902 { 903 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php'; 904 // TODO We can't, we dont' have full path of file, only last_main_doc adn ->element, so we must rebuild full path first 905 /* 906 $ecmfile->filepath = $rel_dir; 907 $ecmfile->filename = $filename; 908 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content 909 $ecmfile->fullpath_orig = ''; 910 $ecmfile->gen_or_uploaded = 'generated'; 911 $ecmfile->description = ''; // indexed content 912 $ecmfile->keyword = ''; // keyword content 913 $ecmfile->share = getRandomPassword(true); 914 $result = $ecmfile->create($user); 915 if ($result < 0) 916 { 917 $this->error = $ecmfile->error; 918 $this->errors = $ecmfile->errors; 919 } 920 */ 921 } else return ''; 922 } elseif (empty($ecmfile->share)) 923 { 924 // Add entry into index 925 if ($initsharekey) 926 { 927 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php'; 928 $ecmfile->share = getRandomPassword(true); 929 $ecmfile->update($user); 930 } else return ''; 931 } 932 // Define $urlwithroot 933 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root)); 934 // This is to use external domain name found into config file 935 //if (DOL_URL_ROOT && ! preg_match('/\/$/', $urlwithouturlroot) && ! preg_match('/^\//', DOL_URL_ROOT)) $urlwithroot=$urlwithouturlroot.'/'.DOL_URL_ROOT; 936 //else 937 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT; 938 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current 939 940 $forcedownload = 0; 941 942 $paramlink = ''; 943 //if (! empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart; // For sharing with hash (so public files), modulepart is not required. 944 //if (! empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; // For sharing with hash (so public files), entity is not required. 945 //$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath); // No need of name of file for public link, we will use the hash 946 if (!empty($ecmfile->share)) $paramlink .= ($paramlink ? '&' : '').'hashp='.$ecmfile->share; // Hash for public share 947 if ($forcedownload) $paramlink .= ($paramlink ? '&' : '').'attachment=1'; 948 949 if ($relativelink) 950 { 951 $linktoreturn = 'document.php'.($paramlink ? '?'.$paramlink : ''); 952 } else { 953 $linktoreturn = $urlwithroot.'/document.php'.($paramlink ? '?'.$paramlink : ''); 954 } 955 956 // Here $ecmfile->share is defined 957 return $linktoreturn; 958 } 959 960 961 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 962 /** 963 * Add a link between element $this->element and a contact 964 * 965 * @param int $fk_socpeople Id of thirdparty contact (if source = 'external') or id of user (if souce = 'internal') to link 966 * @param int|string $type_contact Type of contact (code or id). Must be id or code found into table llx_c_type_contact. For example: SALESREPFOLL 967 * @param string $source external=Contact extern (llx_socpeople), internal=Contact intern (llx_user) 968 * @param int $notrigger Disable all triggers 969 * @return int <0 if KO, >0 if OK 970 */ 971 public function add_contact($fk_socpeople, $type_contact, $source = 'external', $notrigger = 0) 972 { 973 // phpcs:enable 974 global $user, $langs; 975 976 977 dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger"); 978 979 // Check parameters 980 if ($fk_socpeople <= 0) 981 { 982 $langs->load("errors"); 983 $this->error = $langs->trans("ErrorWrongValueForParameterX", "1"); 984 dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR); 985 return -1; 986 } 987 if (!$type_contact) 988 { 989 $langs->load("errors"); 990 $this->error = $langs->trans("ErrorWrongValueForParameterX", "2"); 991 dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR); 992 return -2; 993 } 994 995 $id_type_contact = 0; 996 if (is_numeric($type_contact)) 997 { 998 $id_type_contact = $type_contact; 999 } else { 1000 // We look for id type_contact 1001 $sql = "SELECT tc.rowid"; 1002 $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc"; 1003 $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'"; 1004 $sql .= " AND tc.source='".$this->db->escape($source)."'"; 1005 $sql .= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1"; 1006 //print $sql; 1007 $resql = $this->db->query($sql); 1008 if ($resql) 1009 { 1010 $obj = $this->db->fetch_object($resql); 1011 if ($obj) $id_type_contact = $obj->rowid; 1012 } 1013 } 1014 1015 if ($id_type_contact == 0) 1016 { 1017 $this->error = 'CODE_NOT_VALID_FOR_THIS_ELEMENT'; 1018 dol_syslog("CODE_NOT_VALID_FOR_THIS_ELEMENT: Code type of contact '".$type_contact."' does not exists or is not active for element ".$this->element.", we can ignore it"); 1019 return -3; 1020 } 1021 1022 $datecreate = dol_now(); 1023 1024 // Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error 1025 $TListeContacts = $this->liste_contact(-1, $source); 1026 $already_added = false; 1027 if (is_array($TListeContacts) && !empty($TListeContacts)) { 1028 foreach ($TListeContacts as $array_contact) { 1029 if ($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) { 1030 $already_added = true; 1031 break; 1032 } 1033 } 1034 } 1035 1036 if (!$already_added) { 1037 $this->db->begin(); 1038 1039 // Insert into database 1040 $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_contact"; 1041 $sql .= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) "; 1042 $sql .= " VALUES (".$this->id.", ".$fk_socpeople." , "; 1043 $sql .= "'".$this->db->idate($datecreate)."'"; 1044 $sql .= ", 4, ".$id_type_contact; 1045 $sql .= ")"; 1046 1047 $resql = $this->db->query($sql); 1048 if ($resql) 1049 { 1050 if (!$notrigger) 1051 { 1052 $result = $this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user); 1053 if ($result < 0) 1054 { 1055 $this->db->rollback(); 1056 return -1; 1057 } 1058 } 1059 1060 $this->db->commit(); 1061 return 1; 1062 } else { 1063 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') 1064 { 1065 $this->error = $this->db->errno(); 1066 $this->db->rollback(); 1067 return -2; 1068 } else { 1069 $this->error = $this->db->error(); 1070 $this->db->rollback(); 1071 return -1; 1072 } 1073 } 1074 } else return 0; 1075 } 1076 1077 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1078 /** 1079 * Copy contact from one element to current 1080 * 1081 * @param CommonObject $objFrom Source element 1082 * @param string $source Nature of contact ('internal' or 'external') 1083 * @return int >0 if OK, <0 if KO 1084 */ 1085 public function copy_linked_contact($objFrom, $source = 'internal') 1086 { 1087 // phpcs:enable 1088 $contacts = $objFrom->liste_contact(-1, $source); 1089 foreach ($contacts as $contact) 1090 { 1091 if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0) 1092 { 1093 $this->error = $this->db->lasterror(); 1094 return -1; 1095 } 1096 } 1097 return 1; 1098 } 1099 1100 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1101 /** 1102 * Update a link to contact line 1103 * 1104 * @param int $rowid Id of line contact-element 1105 * @param int $statut New status of link 1106 * @param int $type_contact_id Id of contact type (not modified if 0) 1107 * @param int $fk_socpeople Id of soc_people to update (not modified if 0) 1108 * @return int <0 if KO, >= 0 if OK 1109 */ 1110 public function update_contact($rowid, $statut, $type_contact_id = 0, $fk_socpeople = 0) 1111 { 1112 // phpcs:enable 1113 // Insert into database 1114 $sql = "UPDATE ".MAIN_DB_PREFIX."element_contact set"; 1115 $sql .= " statut = ".$statut; 1116 if ($type_contact_id) $sql .= ", fk_c_type_contact = ".((int) $type_contact_id); 1117 if ($fk_socpeople) $sql .= ", fk_socpeople = ".((int) $fk_socpeople); 1118 $sql .= " where rowid = ".$rowid; 1119 $resql = $this->db->query($sql); 1120 if ($resql) 1121 { 1122 return 0; 1123 } else { 1124 $this->error = $this->db->lasterror(); 1125 return -1; 1126 } 1127 } 1128 1129 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1130 /** 1131 * Delete a link to contact line 1132 * 1133 * @param int $rowid Id of contact link line to delete 1134 * @param int $notrigger Disable all triggers 1135 * @return int >0 if OK, <0 if KO 1136 */ 1137 public function delete_contact($rowid, $notrigger = 0) 1138 { 1139 // phpcs:enable 1140 global $user; 1141 1142 1143 $this->db->begin(); 1144 1145 $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact"; 1146 $sql .= " WHERE rowid =".$rowid; 1147 1148 dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG); 1149 if ($this->db->query($sql)) 1150 { 1151 if (!$notrigger) 1152 { 1153 $result = $this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user); 1154 if ($result < 0) { $this->db->rollback(); return -1; } 1155 } 1156 1157 $this->db->commit(); 1158 return 1; 1159 } else { 1160 $this->error = $this->db->lasterror(); 1161 $this->db->rollback(); 1162 return -1; 1163 } 1164 } 1165 1166 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1167 /** 1168 * Delete all links between an object $this and all its contacts 1169 * 1170 * @param string $source '' or 'internal' or 'external' 1171 * @param string $code Type of contact (code or id) 1172 * @return int >0 if OK, <0 if KO 1173 */ 1174 public function delete_linked_contact($source = '', $code = '') 1175 { 1176 // phpcs:enable 1177 $temp = array(); 1178 $typeContact = $this->liste_type_contact($source, '', 0, 0, $code); 1179 1180 foreach ($typeContact as $key => $value) 1181 { 1182 array_push($temp, $key); 1183 } 1184 $listId = implode(",", $temp); 1185 1186 $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact"; 1187 $sql .= " WHERE element_id = ".$this->id; 1188 if ($listId) 1189 $sql .= " AND fk_c_type_contact IN (".$listId.")"; 1190 1191 dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG); 1192 if ($this->db->query($sql)) 1193 { 1194 return 1; 1195 } else { 1196 $this->error = $this->db->lasterror(); 1197 return -1; 1198 } 1199 } 1200 1201 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1202 /** 1203 * Get array of all contacts for an object 1204 * 1205 * @param int $status Status of links to get (-1=all) 1206 * @param string $source Source of contact: 'external' or 'thirdparty' (llx_socpeople) or 'internal' (llx_user) 1207 * @param int $list 0:Return array contains all properties, 1:Return array contains just id 1208 * @param string $code Filter on this code of contact type ('SHIPPING', 'BILLING', ...) 1209 * @return array|int Array of contacts, -1 if error 1210 */ 1211 public function liste_contact($status = -1, $source = 'external', $list = 0, $code = '') 1212 { 1213 // phpcs:enable 1214 global $langs; 1215 1216 $tab = array(); 1217 1218 $sql = "SELECT ec.rowid, ec.statut as statuslink, ec.fk_socpeople as id, ec.fk_c_type_contact"; // This field contains id of llx_socpeople or id of llx_user 1219 if ($source == 'internal') $sql .= ", '-1' as socid, t.statut as statuscontact, t.login, t.photo"; 1220 if ($source == 'external' || $source == 'thirdparty') $sql .= ", t.fk_soc as socid, t.statut as statuscontact"; 1221 $sql .= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email"; 1222 $sql .= ", tc.source, tc.element, tc.code, tc.libelle"; 1223 $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact tc"; 1224 $sql .= ", ".MAIN_DB_PREFIX."element_contact ec"; 1225 if ($source == 'internal') $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid"; 1226 if ($source == 'external' || $source == 'thirdparty') $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid"; 1227 $sql .= " WHERE ec.element_id =".$this->id; 1228 $sql .= " AND ec.fk_c_type_contact=tc.rowid"; 1229 $sql .= " AND tc.element='".$this->db->escape($this->element)."'"; 1230 if ($code) $sql .= " AND tc.code = '".$this->db->escape($code)."'"; 1231 if ($source == 'internal') $sql .= " AND tc.source = 'internal'"; 1232 if ($source == 'external' || $source == 'thirdparty') $sql .= " AND tc.source = 'external'"; 1233 $sql .= " AND tc.active=1"; 1234 if ($status >= 0) $sql .= " AND ec.statut = ".$status; 1235 $sql .= " ORDER BY t.lastname ASC"; 1236 1237 dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG); 1238 $resql = $this->db->query($sql); 1239 if ($resql) 1240 { 1241 $num = $this->db->num_rows($resql); 1242 $i = 0; 1243 while ($i < $num) 1244 { 1245 $obj = $this->db->fetch_object($resql); 1246 1247 if (!$list) 1248 { 1249 $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code; 1250 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle); 1251 $tab[$i] = array('source'=>$obj->source, 'socid'=>$obj->socid, 'id'=>$obj->id, 1252 'nom'=>$obj->lastname, // For backward compatibility 1253 'civility'=>$obj->civility, 'lastname'=>$obj->lastname, 'firstname'=>$obj->firstname, 'email'=>$obj->email, 'login'=>$obj->login, 'photo'=>$obj->photo, 'statuscontact'=>$obj->statuscontact, 1254 'rowid'=>$obj->rowid, 'code'=>$obj->code, 'libelle'=>$libelle_type, 'status'=>$obj->statuslink, 'fk_c_type_contact'=>$obj->fk_c_type_contact); 1255 } else { 1256 $tab[$i] = $obj->id; 1257 } 1258 1259 $i++; 1260 } 1261 1262 return $tab; 1263 } else { 1264 $this->error = $this->db->lasterror(); 1265 dol_print_error($this->db); 1266 return -1; 1267 } 1268 } 1269 1270 1271 /** 1272 * Update status of a contact linked to object 1273 * 1274 * @param int $rowid Id of link between object and contact 1275 * @return int <0 if KO, >=0 if OK 1276 */ 1277 public function swapContactStatus($rowid) 1278 { 1279 $sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,"; 1280 $sql .= " tc.code, tc.libelle"; 1281 $sql .= " FROM (".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as tc)"; 1282 $sql .= " WHERE ec.rowid =".$rowid; 1283 $sql .= " AND ec.fk_c_type_contact=tc.rowid"; 1284 $sql .= " AND tc.element = '".$this->db->escape($this->element)."'"; 1285 1286 dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG); 1287 $resql = $this->db->query($sql); 1288 if ($resql) 1289 { 1290 $obj = $this->db->fetch_object($resql); 1291 $newstatut = ($obj->statut == 4) ? 5 : 4; 1292 $result = $this->update_contact($rowid, $newstatut); 1293 $this->db->free($resql); 1294 return $result; 1295 } else { 1296 $this->error = $this->db->error(); 1297 dol_print_error($this->db); 1298 return -1; 1299 } 1300 } 1301 1302 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1303 /** 1304 * Return array with list of possible values for type of contacts 1305 * 1306 * @param string $source 'internal', 'external' or 'all' 1307 * @param string $order Sort order by : 'position', 'code', 'rowid'... 1308 * @param int $option 0=Return array id->label, 1=Return array code->label 1309 * @param int $activeonly 0=all status of contact, 1=only the active 1310 * @param string $code Type of contact (Example: 'CUSTOMER', 'SERVICE') 1311 * @return array Array list of type of contacts (id->label if option=0, code->label if option=1) 1312 */ 1313 public function liste_type_contact($source = 'internal', $order = 'position', $option = 0, $activeonly = 0, $code = '') 1314 { 1315 // phpcs:enable 1316 global $langs; 1317 1318 if (empty($order)) $order = 'position'; 1319 if ($order == 'position') $order .= ',code'; 1320 1321 $tab = array(); 1322 $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position"; 1323 $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc"; 1324 $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'"; 1325 if ($activeonly == 1) $sql .= " AND tc.active=1"; // only the active types 1326 if (!empty($source) && $source != 'all') $sql .= " AND tc.source='".$this->db->escape($source)."'"; 1327 if (!empty($code)) $sql .= " AND tc.code='".$this->db->escape($code)."'"; 1328 $sql .= $this->db->order($order, 'ASC'); 1329 1330 //print "sql=".$sql; 1331 $resql = $this->db->query($sql); 1332 if ($resql) 1333 { 1334 $num = $this->db->num_rows($resql); 1335 $i = 0; 1336 while ($i < $num) 1337 { 1338 $obj = $this->db->fetch_object($resql); 1339 1340 $transkey = "TypeContact_".$this->element."_".$source."_".$obj->code; 1341 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle); 1342 if (empty($option)) $tab[$obj->rowid] = $libelle_type; 1343 else $tab[$obj->code] = $libelle_type; 1344 $i++; 1345 } 1346 return $tab; 1347 } else { 1348 $this->error = $this->db->lasterror(); 1349 //dol_print_error($this->db); 1350 return null; 1351 } 1352 } 1353 1354 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1355 /** 1356 * Return array with list of possible values for type of contacts 1357 * 1358 * @param string $source 'internal', 'external' or 'all' 1359 * @param int $option 0=Return array id->label, 1=Return array code->label 1360 * @param int $activeonly 0=all status of contact, 1=only the active 1361 * @param string $code Type of contact (Example: 'CUSTOMER', 'SERVICE') 1362 * @param string $element Filter on 1 element type 1363 * @param string $excludeelement Exclude 1 element type. Example: 'agenda' 1364 * @return array Array list of type of contacts (id->label if option=0, code->label if option=1) 1365 */ 1366 public function listeTypeContacts($source = 'internal', $option = 0, $activeonly = 0, $code = '', $element = '', $excludeelement = '') 1367 { 1368 // phpcs:enable 1369 global $langs, $conf; 1370 1371 $langs->loadLangs(array('bills', 'contracts', 'interventions', 'orders', 'projects', 'propal', 'ticket', 'agenda')); 1372 1373 $tab = array(); 1374 1375 $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position, tc.element"; 1376 $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc"; 1377 1378 $sqlWhere = array(); 1379 if (!empty($element)) { 1380 $sqlWhere[] = " tc.element='".$this->db->escape($element)."'"; 1381 } 1382 if (!empty($excludeelement)) { 1383 $sqlWhere[] = " tc.element <> '".$this->db->escape($excludeelement)."'"; 1384 } 1385 1386 if ($activeonly == 1) 1387 $sqlWhere[] = " tc.active=1"; // only the active types 1388 1389 if (!empty($source) && $source != 'all') 1390 $sqlWhere[] = " tc.source='".$this->db->escape($source)."'"; 1391 1392 if (!empty($code)) 1393 $sqlWhere[] = " tc.code='".$this->db->escape($code)."'"; 1394 1395 if (count($sqlWhere) > 0) { 1396 $sql .= " WHERE ".implode(' AND ', $sqlWhere); 1397 } 1398 1399 $sql .= $this->db->order('tc.element, tc.position', 'ASC'); 1400 1401 dol_syslog(__METHOD__, LOG_DEBUG); 1402 $resql = $this->db->query($sql); 1403 if ($resql) { 1404 $num = $this->db->num_rows($resql); 1405 if ($num > 0) { 1406 $langs->loadLangs(array("propal", "orders", "bills", "suppliers", "contracts", "supplier_proposal")); 1407 1408 while ($obj = $this->db->fetch_object($resql)) { 1409 $modulename = $obj->element; 1410 if (strpos($obj->element, 'project') !== false) { 1411 $modulename = 'projet'; 1412 } elseif ($obj->element == 'contrat') { 1413 $element = 'contract'; 1414 } elseif ($obj->element == 'action') { 1415 $modulename = 'agenda'; 1416 } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') { 1417 $modulename = 'fournisseur'; 1418 } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') { 1419 $modulename = 'fournisseur'; 1420 } 1421 if ($conf->{$modulename}->enabled) { 1422 $libelle_element = $langs->trans('ContactDefault_'.$obj->element); 1423 $tmpelement = $obj->element; 1424 $transkey = "TypeContact_".$tmpelement."_".$source."_".$obj->code; 1425 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle); 1426 if (empty($option)) { 1427 $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type; 1428 } 1429 else { 1430 $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type; 1431 } 1432 } 1433 } 1434 } 1435 return $tab; 1436 } else { 1437 $this->error = $this->db->lasterror(); 1438 return null; 1439 } 1440 } 1441 1442 /** 1443 * Return id of contacts for a source and a contact code. 1444 * Example: contact client de facturation ('external', 'BILLING') 1445 * Example: contact client de livraison ('external', 'SHIPPING') 1446 * Example: contact interne suivi paiement ('internal', 'SALESREPFOLL') 1447 * 1448 * @param string $source 'external' or 'internal' 1449 * @param string $code 'BILLING', 'SHIPPING', 'SALESREPFOLL', ... 1450 * @param int $status limited to a certain status 1451 * @return array List of id for such contacts 1452 */ 1453 public function getIdContact($source, $code, $status = 0) 1454 { 1455 global $conf; 1456 1457 $result = array(); 1458 $i = 0; 1459 //cas particulier pour les expeditions 1460 if ($this->element == 'shipping' && $this->origin_id != 0) { 1461 $id = $this->origin_id; 1462 $element = 'commande'; 1463 } elseif ($this->element == 'reception' && $this->origin_id != 0) { 1464 $id = $this->origin_id; 1465 $element = 'order_supplier'; 1466 } else { 1467 $id = $this->id; 1468 $element = $this->element; 1469 } 1470 1471 $sql = "SELECT ec.fk_socpeople"; 1472 $sql .= " FROM ".MAIN_DB_PREFIX."element_contact as ec,"; 1473 if ($source == 'internal') $sql .= " ".MAIN_DB_PREFIX."user as c,"; 1474 if ($source == 'external') $sql .= " ".MAIN_DB_PREFIX."socpeople as c,"; 1475 $sql .= " ".MAIN_DB_PREFIX."c_type_contact as tc"; 1476 $sql .= " WHERE ec.element_id = ".$id; 1477 $sql .= " AND ec.fk_socpeople = c.rowid"; 1478 if ($source == 'internal') $sql .= " AND c.entity IN (".getEntity('user').")"; 1479 if ($source == 'external') $sql .= " AND c.entity IN (".getEntity('societe').")"; 1480 $sql .= " AND ec.fk_c_type_contact = tc.rowid"; 1481 $sql .= " AND tc.element = '".$this->db->escape($element)."'"; 1482 $sql .= " AND tc.source = '".$this->db->escape($source)."'"; 1483 if ($code) $sql .= " AND tc.code = '".$this->db->escape($code)."'"; 1484 $sql .= " AND tc.active = 1"; 1485 if ($status) $sql .= " AND ec.statut = ".$status; 1486 1487 dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG); 1488 $resql = $this->db->query($sql); 1489 if ($resql) 1490 { 1491 while ($obj = $this->db->fetch_object($resql)) 1492 { 1493 $result[$i] = $obj->fk_socpeople; 1494 $i++; 1495 } 1496 } else { 1497 $this->error = $this->db->error(); 1498 return null; 1499 } 1500 1501 return $result; 1502 } 1503 1504 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1505 /** 1506 * Load object contact with id=$this->contact_id into $this->contact 1507 * 1508 * @param int $contactid Id du contact. Use this->contact_id if empty. 1509 * @return int <0 if KO, >0 if OK 1510 */ 1511 public function fetch_contact($contactid = null) 1512 { 1513 // phpcs:enable 1514 if (empty($contactid)) $contactid = $this->contact_id; 1515 1516 if (empty($contactid)) return 0; 1517 1518 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php'; 1519 $contact = new Contact($this->db); 1520 $result = $contact->fetch($contactid); 1521 $this->contact = $contact; 1522 return $result; 1523 } 1524 1525 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1526 /** 1527 * Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty 1528 * 1529 * @param int $force_thirdparty_id Force thirdparty id 1530 * @return int <0 if KO, >0 if OK 1531 */ 1532 public function fetch_thirdparty($force_thirdparty_id = 0) 1533 { 1534 // phpcs:enable 1535 global $conf; 1536 1537 if (empty($this->socid) && empty($this->fk_soc) && empty($this->fk_thirdparty) && empty($force_thirdparty_id)) 1538 return 0; 1539 1540 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php'; 1541 1542 $idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : $this->fk_thirdparty); 1543 if ($force_thirdparty_id) 1544 $idtofetch = $force_thirdparty_id; 1545 1546 if ($idtofetch) { 1547 $thirdparty = new Societe($this->db); 1548 $result = $thirdparty->fetch($idtofetch); 1549 $this->thirdparty = $thirdparty; 1550 1551 // Use first price level if level not defined for third party 1552 if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) { 1553 $this->thirdparty->price_level = 1; 1554 } 1555 1556 return $result; 1557 } else return -1; 1558 } 1559 1560 1561 /** 1562 * Looks for an object with ref matching the wildcard provided 1563 * It does only work when $this->table_ref_field is set 1564 * 1565 * @param string $ref Wildcard 1566 * @return int >1 = OK, 0 = Not found or table_ref_field not defined, <0 = KO 1567 */ 1568 public function fetchOneLike($ref) 1569 { 1570 if (!$this->table_ref_field) { 1571 return 0; 1572 } 1573 1574 $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE '.$this->table_ref_field.' LIKE "'.$this->db->escape($ref).'" LIMIT 1'; 1575 1576 $query = $this->db->query($sql); 1577 1578 if (!$this->db->num_rows($query)) { 1579 return 0; 1580 } 1581 1582 $result = $this->db->fetch_object($query); 1583 1584 return $this->fetch($result->rowid); 1585 } 1586 1587 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1588 /** 1589 * Load data for barcode into properties ->barcode_type* 1590 * Properties ->barcode_type that is id of barcode. Type is used to find other properties, but 1591 * if it is not defined, ->element must be defined to know default barcode type. 1592 * 1593 * @return int <0 if KO, 0 if can't guess type of barcode (ISBN, EAN13...), >0 if OK (all barcode properties loaded) 1594 */ 1595 public function fetch_barcode() 1596 { 1597 // phpcs:enable 1598 global $conf; 1599 1600 dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type); 1601 1602 $idtype = $this->barcode_type; 1603 if (empty($idtype) && $idtype != '0') // If type of barcode no set, we try to guess. If set to '0' it means we forced to have type remain not defined 1604 { 1605 if ($this->element == 'product') $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE; 1606 elseif ($this->element == 'societe') $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY; 1607 else dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING); 1608 } 1609 1610 if ($idtype > 0) 1611 { 1612 if (empty($this->barcode_type) || empty($this->barcode_type_code) || empty($this->barcode_type_label) || empty($this->barcode_type_coder)) // If data not already loaded 1613 { 1614 $sql = "SELECT rowid, code, libelle as label, coder"; 1615 $sql .= " FROM ".MAIN_DB_PREFIX."c_barcode_type"; 1616 $sql .= " WHERE rowid = ".$idtype; 1617 dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG); 1618 $resql = $this->db->query($sql); 1619 if ($resql) 1620 { 1621 $obj = $this->db->fetch_object($resql); 1622 $this->barcode_type = $obj->rowid; 1623 $this->barcode_type_code = $obj->code; 1624 $this->barcode_type_label = $obj->label; 1625 $this->barcode_type_coder = $obj->coder; 1626 return 1; 1627 } else { 1628 dol_print_error($this->db); 1629 return -1; 1630 } 1631 } 1632 } 1633 return 0; 1634 } 1635 1636 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1637 /** 1638 * Load the project with id $this->fk_project into this->project 1639 * 1640 * @return int <0 if KO, >=0 if OK 1641 */ 1642 public function fetch_projet() 1643 { 1644 // phpcs:enable 1645 include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php'; 1646 1647 if (empty($this->fk_project) && !empty($this->fk_projet)) $this->fk_project = $this->fk_projet; // For backward compatibility 1648 if (empty($this->fk_project)) return 0; 1649 1650 $project = new Project($this->db); 1651 $result = $project->fetch($this->fk_project); 1652 1653 $this->projet = $project; // deprecated 1654 $this->project = $project; 1655 return $result; 1656 } 1657 1658 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1659 /** 1660 * Load the product with id $this->fk_product into this->product 1661 * 1662 * @return int <0 if KO, >=0 if OK 1663 */ 1664 public function fetch_product() 1665 { 1666 // phpcs:enable 1667 include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; 1668 1669 if (empty($this->fk_product)) return 0; 1670 1671 $product = new Product($this->db); 1672 $result = $product->fetch($this->fk_product); 1673 1674 $this->product = $product; 1675 return $result; 1676 } 1677 1678 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1679 /** 1680 * Load the user with id $userid into this->user 1681 * 1682 * @param int $userid Id du contact 1683 * @return int <0 if KO, >0 if OK 1684 */ 1685 public function fetch_user($userid) 1686 { 1687 // phpcs:enable 1688 $user = new User($this->db); 1689 $result = $user->fetch($userid); 1690 $this->user = $user; 1691 return $result; 1692 } 1693 1694 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1695 /** 1696 * Read linked origin object 1697 * 1698 * @return void 1699 */ 1700 public function fetch_origin() 1701 { 1702 // phpcs:enable 1703 if ($this->origin == 'shipping') $this->origin = 'expedition'; 1704 if ($this->origin == 'delivery') $this->origin = 'livraison'; 1705 if ($this->origin == 'order_supplier') $this->origin = 'commandeFournisseur'; 1706 1707 $origin = $this->origin; 1708 1709 $classname = ucfirst($origin); 1710 $this->$origin = new $classname($this->db); 1711 $this->$origin->fetch($this->origin_id); 1712 } 1713 1714 /** 1715 * Load object from specific field 1716 * 1717 * @param string $table Table element or element line 1718 * @param string $field Field selected 1719 * @param string $key Import key 1720 * @param string $element Element name 1721 * @return int <0 if KO, >0 if OK 1722 */ 1723 public function fetchObjectFrom($table, $field, $key, $element = null) 1724 { 1725 global $conf; 1726 1727 $result = false; 1728 1729 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$table; 1730 $sql .= " WHERE ".$field." = '".$key."'"; 1731 if (!empty($element)) { 1732 $sql .= " AND entity IN (".getEntity($element).")"; 1733 } else { 1734 $sql .= " AND entity = ".$conf->entity; 1735 } 1736 1737 dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG); 1738 $resql = $this->db->query($sql); 1739 if ($resql) 1740 { 1741 $row = $this->db->fetch_row($resql); 1742 // Test for avoid error -1 1743 if ($row[0] > 0) { 1744 $result = $this->fetch($row[0]); 1745 } 1746 } 1747 1748 return $result; 1749 } 1750 1751 /** 1752 * Getter generic. Load value from a specific field 1753 * 1754 * @param string $table Table of element or element line 1755 * @param int $id Element id 1756 * @param string $field Field selected 1757 * @return int <0 if KO, >0 if OK 1758 */ 1759 public function getValueFrom($table, $id, $field) 1760 { 1761 $result = false; 1762 if (!empty($id) && !empty($field) && !empty($table)) { 1763 $sql = "SELECT ".$field." FROM ".MAIN_DB_PREFIX.$table; 1764 $sql .= " WHERE rowid = ".$id; 1765 1766 dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG); 1767 $resql = $this->db->query($sql); 1768 if ($resql) 1769 { 1770 $row = $this->db->fetch_row($resql); 1771 $result = $row[0]; 1772 } 1773 } 1774 return $result; 1775 } 1776 1777 /** 1778 * Setter generic. Update a specific field into database. 1779 * Warning: Trigger is run only if param trigkey is provided. 1780 * 1781 * @param string $field Field to update 1782 * @param mixed $value New value 1783 * @param string $table To force other table element or element line (should not be used) 1784 * @param int $id To force other object id (should not be used) 1785 * @param string $format Data format ('text', 'date'). 'text' is used if not defined 1786 * @param string $id_field To force rowid field name. 'rowid' is used if not defined 1787 * @param User|string $fuser Update the user of last update field with this user. If not provided, current user is used except if value is 'none' 1788 * @param string $trigkey Trigger key to run (in most cases something like 'XXX_MODIFY') 1789 * @param string $fk_user_field Name of field to save user id making change 1790 * @return int <0 if KO, >0 if OK 1791 * @see updateExtraField() 1792 */ 1793 public function setValueFrom($field, $value, $table = '', $id = null, $format = '', $id_field = '', $fuser = null, $trigkey = '', $fk_user_field = 'fk_user_modif') 1794 { 1795 global $user, $langs, $conf; 1796 1797 if (empty($table)) $table = $this->table_element; 1798 if (empty($id)) $id = $this->id; 1799 if (empty($format)) $format = 'text'; 1800 if (empty($id_field)) $id_field = 'rowid'; 1801 1802 $error = 0; 1803 1804 $this->db->begin(); 1805 1806 // Special case 1807 if ($table == 'product' && $field == 'note_private') $field = 'note'; 1808 if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) $fk_user_field = 'fk_user_mod'; 1809 1810 $sql = "UPDATE ".MAIN_DB_PREFIX.$table." SET "; 1811 1812 if ($format == 'text') $sql .= $field." = '".$this->db->escape($value)."'"; 1813 elseif ($format == 'int') $sql .= $field." = ".$this->db->escape($value); 1814 elseif ($format == 'date') $sql .= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null"); 1815 1816 if ($fk_user_field) 1817 { 1818 if (!empty($fuser) && is_object($fuser)) $sql .= ", ".$fk_user_field." = ".$fuser->id; 1819 elseif (empty($fuser) || $fuser != 'none') $sql .= ", ".$fk_user_field." = ".$user->id; 1820 } 1821 1822 $sql .= " WHERE ".$id_field." = ".$id; 1823 1824 dol_syslog(__METHOD__."", LOG_DEBUG); 1825 $resql = $this->db->query($sql); 1826 if ($resql) 1827 { 1828 if ($trigkey) 1829 { 1830 // call trigger with updated object values 1831 if (empty($this->fields) && method_exists($this, 'fetch')) 1832 { 1833 $result = $this->fetch($id); 1834 } else { 1835 $result = $this->fetchCommon($id); 1836 } 1837 if ($result >= 0) $result = $this->call_trigger($trigkey, (!empty($fuser) && is_object($fuser)) ? $fuser : $user); // This may set this->errors 1838 if ($result < 0) $error++; 1839 } 1840 1841 if (!$error) 1842 { 1843 if (property_exists($this, $field)) $this->$field = $value; 1844 $this->db->commit(); 1845 return 1; 1846 } else { 1847 $this->db->rollback(); 1848 return -2; 1849 } 1850 } else { 1851 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') { 1852 $this->error = 'DB_ERROR_RECORD_ALREADY_EXISTS'; 1853 } else { 1854 $this->error = $this->db->lasterror(); 1855 } 1856 $this->db->rollback(); 1857 return -1; 1858 } 1859 } 1860 1861 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 1862 /** 1863 * Load properties id_previous and id_next by comparing $fieldid with $this->ref 1864 * 1865 * @param string $filter Optional filter. Example: " AND (t.field1 = 'aa' OR t.field2 = 'bb')" 1866 * @param string $fieldid Name of field to use for the select MAX and MIN 1867 * @param int $nodbprefix Do not include DB prefix to forge table name 1868 * @return int <0 if KO, >0 if OK 1869 */ 1870 public function load_previous_next_ref($filter, $fieldid, $nodbprefix = 0) 1871 { 1872 // phpcs:enable 1873 global $conf, $user; 1874 1875 if (!$this->table_element) 1876 { 1877 dol_print_error('', get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined"); 1878 return -1; 1879 } 1880 if ($fieldid == 'none') return 1; 1881 1882 // Security on socid 1883 $socid = 0; 1884 if ($user->socid > 0) $socid = $user->socid; 1885 1886 // this->ismultientitymanaged contains 1887 // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table 1888 $aliastablesociete = 's'; 1889 if ($this->element == 'societe') $aliastablesociete = 'te'; // te as table_element 1890 1891 $sql = "SELECT MAX(te.".$fieldid.")"; 1892 $sql .= " FROM ".(empty($nodbprefix) ?MAIN_DB_PREFIX:'').$this->table_element." as te"; 1893 if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) { 1894 $sql .= ",".MAIN_DB_PREFIX."usergroup_user as ug"; 1895 } 1896 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) { 1897 $tmparray = explode('@', $this->ismultientitymanaged); 1898 $sql .= ", ".MAIN_DB_PREFIX.$tmparray[1]." as ".($tmparray[1] == 'societe' ? 's' : 'parenttable'); // If we need to link to this table to limit select to entity 1899 } elseif ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe as s"; // If we need to link to societe to limit select to socid 1900 elseif ($this->restrictiononfksoc == 2 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON te.fk_soc = s.rowid"; // If we need to link to societe to limit select to socid 1901 if ($this->restrictiononfksoc && !$user->rights->societe->client->voir && !$socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc"; 1902 $sql .= " WHERE te.".$fieldid." < '".$this->db->escape($fieldid == 'rowid' ? $this->id : $this->ref)."'"; // ->ref must always be defined (set to id if field does not exists) 1903 if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql .= " AND sc.fk_user = ".$user->id; 1904 if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql .= " AND (sc.fk_user = ".$user->id.' OR te.fk_soc IS NULL)'; 1905 if (!empty($filter)) 1906 { 1907 if (!preg_match('/^\s*AND/i', $filter)) $sql .= " AND "; // For backward compatibility 1908 $sql .= $filter; 1909 } 1910 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) { 1911 $tmparray = explode('@', $this->ismultientitymanaged); 1912 $sql .= ' AND te.'.$tmparray[0].' = '.($tmparray[1] == 'societe' ? 's' : 'parenttable').'.rowid'; // If we need to link to this table to limit select to entity 1913 } elseif ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid 1914 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) { 1915 if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) { 1916 if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) { 1917 $sql .= " AND te.entity IS NOT NULL"; // Show all users 1918 } else { 1919 $sql .= " AND ug.fk_user = te.rowid"; 1920 $sql .= " AND ug.entity IN (".getEntity($this->element).")"; 1921 } 1922 } else { 1923 $sql .= ' AND te.entity IN ('.getEntity($this->element).')'; 1924 } 1925 } 1926 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') { 1927 $tmparray = explode('@', $this->ismultientitymanaged); 1928 $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')'; 1929 } 1930 if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql .= ' AND te.fk_soc = '.$socid; 1931 if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql .= ' AND (te.fk_soc = '.$socid.' OR te.fk_soc IS NULL)'; 1932 if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql .= ' AND te.rowid = '.$socid; 1933 //print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>"; 1934 1935 $result = $this->db->query($sql); 1936 if (!$result) 1937 { 1938 $this->error = $this->db->lasterror(); 1939 return -1; 1940 } 1941 $row = $this->db->fetch_row($result); 1942 $this->ref_previous = $row[0]; 1943 1944 $sql = "SELECT MIN(te.".$fieldid.")"; 1945 $sql .= " FROM ".(empty($nodbprefix) ?MAIN_DB_PREFIX:'').$this->table_element." as te"; 1946 if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) { 1947 $sql .= ",".MAIN_DB_PREFIX."usergroup_user as ug"; 1948 } 1949 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) { 1950 $tmparray = explode('@', $this->ismultientitymanaged); 1951 $sql .= ", ".MAIN_DB_PREFIX.$tmparray[1]." as ".($tmparray[1] == 'societe' ? 's' : 'parenttable'); // If we need to link to this table to limit select to entity 1952 } elseif ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe as s"; // If we need to link to societe to limit select to socid 1953 elseif ($this->restrictiononfksoc == 2 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON te.fk_soc = s.rowid"; // If we need to link to societe to limit select to socid 1954 if ($this->restrictiononfksoc && !$user->rights->societe->client->voir && !$socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc"; 1955 $sql .= " WHERE te.".$fieldid." > '".$this->db->escape($fieldid == 'rowid' ? $this->id : $this->ref)."'"; // ->ref must always be defined (set to id if field does not exists) 1956 if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql .= " AND sc.fk_user = ".$user->id; 1957 if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql .= " AND (sc.fk_user = ".$user->id.' OR te.fk_soc IS NULL)'; 1958 if (!empty($filter)) 1959 { 1960 if (!preg_match('/^\s*AND/i', $filter)) $sql .= " AND "; // For backward compatibility 1961 $sql .= $filter; 1962 } 1963 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) { 1964 $tmparray = explode('@', $this->ismultientitymanaged); 1965 $sql .= ' AND te.'.$tmparray[0].' = '.($tmparray[1] == 'societe' ? 's' : 'parenttable').'.rowid'; // If we need to link to this table to limit select to entity 1966 } elseif ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid 1967 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) { 1968 if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) { 1969 if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) { 1970 $sql .= " AND te.entity IS NOT NULL"; // Show all users 1971 } else { 1972 $sql .= " AND ug.fk_user = te.rowid"; 1973 $sql .= " AND ug.entity IN (".getEntity($this->element).")"; 1974 } 1975 } else { 1976 $sql .= ' AND te.entity IN ('.getEntity($this->element).')'; 1977 } 1978 } 1979 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') { 1980 $tmparray = explode('@', $this->ismultientitymanaged); 1981 $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')'; 1982 } 1983 if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql .= ' AND te.fk_soc = '.$socid; 1984 if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql .= ' AND (te.fk_soc = '.$socid.' OR te.fk_soc IS NULL)'; 1985 if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql .= ' AND te.rowid = '.$socid; 1986 //print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>"; 1987 // Rem: Bug in some mysql version: SELECT MIN(rowid) FROM llx_socpeople WHERE rowid > 1 when one row in database with rowid=1, returns 1 instead of null 1988 1989 $result = $this->db->query($sql); 1990 if (!$result) 1991 { 1992 $this->error = $this->db->lasterror(); 1993 return -2; 1994 } 1995 $row = $this->db->fetch_row($result); 1996 $this->ref_next = $row[0]; 1997 1998 return 1; 1999 } 2000 2001 2002 /** 2003 * Return list of id of contacts of object 2004 * 2005 * @param string $source Source of contact: external (llx_socpeople) or internal (llx_user) or thirdparty (llx_societe) 2006 * @return array Array of id of contacts (if source=external or internal) 2007 * Array of id of third parties with at least one contact on object (if source=thirdparty) 2008 */ 2009 public function getListContactId($source = 'external') 2010 { 2011 $contactAlreadySelected = array(); 2012 $tab = $this->liste_contact(-1, $source); 2013 $num = count($tab); 2014 $i = 0; 2015 while ($i < $num) 2016 { 2017 if ($source == 'thirdparty') $contactAlreadySelected[$i] = $tab[$i]['socid']; 2018 else $contactAlreadySelected[$i] = $tab[$i]['id']; 2019 $i++; 2020 } 2021 return $contactAlreadySelected; 2022 } 2023 2024 2025 /** 2026 * Link element with a project 2027 * 2028 * @param int $projectid Project id to link element to 2029 * @return int <0 if KO, >0 if OK 2030 */ 2031 public function setProject($projectid) 2032 { 2033 if (!$this->table_element) 2034 { 2035 dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined", LOG_ERR); 2036 return -1; 2037 } 2038 2039 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element; 2040 if (!empty($this->fields['fk_project'])) // Common case 2041 { 2042 if ($projectid) $sql .= ' SET fk_project = '.$projectid; 2043 else $sql .= ' SET fk_project = NULL'; 2044 $sql .= ' WHERE rowid = '.$this->id; 2045 } elseif ($this->table_element == 'actioncomm') // Special case for actioncomm 2046 { 2047 if ($projectid) $sql .= ' SET fk_project = '.$projectid; 2048 else $sql .= ' SET fk_project = NULL'; 2049 $sql .= ' WHERE id = '.$this->id; 2050 } else // Special case for old architecture objects 2051 { 2052 if ($projectid) $sql .= ' SET fk_projet = '.$projectid; 2053 else $sql .= ' SET fk_projet = NULL'; 2054 $sql .= ' WHERE rowid = '.$this->id; 2055 } 2056 2057 dol_syslog(get_class($this)."::setProject", LOG_DEBUG); 2058 if ($this->db->query($sql)) 2059 { 2060 $this->fk_project = $projectid; 2061 return 1; 2062 } else { 2063 dol_print_error($this->db); 2064 return -1; 2065 } 2066 } 2067 2068 /** 2069 * Change the payments methods 2070 * 2071 * @param int $id Id of new payment method 2072 * @return int >0 if OK, <0 if KO 2073 */ 2074 public function setPaymentMethods($id) 2075 { 2076 dol_syslog(get_class($this).'::setPaymentMethods('.$id.')'); 2077 if ($this->statut >= 0 || $this->element == 'societe') 2078 { 2079 // TODO uniformize field name 2080 $fieldname = 'fk_mode_reglement'; 2081 if ($this->element == 'societe') $fieldname = 'mode_reglement'; 2082 if (get_class($this) == 'Fournisseur') $fieldname = 'mode_reglement_supplier'; 2083 2084 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element; 2085 $sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL'); 2086 $sql .= ' WHERE rowid='.$this->id; 2087 2088 if ($this->db->query($sql)) 2089 { 2090 $this->mode_reglement_id = $id; 2091 // for supplier 2092 if (get_class($this) == 'Fournisseur') $this->mode_reglement_supplier_id = $id; 2093 return 1; 2094 } else { 2095 dol_syslog(get_class($this).'::setPaymentMethods Error '.$sql.' - '.$this->db->error()); 2096 $this->error = $this->db->error(); 2097 return -1; 2098 } 2099 } else { 2100 dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible'); 2101 $this->error = 'Status of the object is incompatible '.$this->statut; 2102 return -2; 2103 } 2104 } 2105 2106 /** 2107 * Change the multicurrency code 2108 * 2109 * @param string $code multicurrency code 2110 * @return int >0 if OK, <0 if KO 2111 */ 2112 public function setMulticurrencyCode($code) 2113 { 2114 dol_syslog(get_class($this).'::setMulticurrencyCode('.$code.')'); 2115 if ($this->statut >= 0 || $this->element == 'societe') 2116 { 2117 $fieldname = 'multicurrency_code'; 2118 2119 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element; 2120 $sql .= ' SET '.$fieldname." = '".$this->db->escape($code)."'"; 2121 $sql .= ' WHERE rowid='.$this->id; 2122 2123 if ($this->db->query($sql)) 2124 { 2125 $this->multicurrency_code = $code; 2126 2127 list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code); 2128 if ($rate) $this->setMulticurrencyRate($rate, 2); 2129 2130 return 1; 2131 } else { 2132 dol_syslog(get_class($this).'::setMulticurrencyCode Error '.$sql.' - '.$this->db->error()); 2133 $this->error = $this->db->error(); 2134 return -1; 2135 } 2136 } else { 2137 dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible'); 2138 $this->error = 'Status of the object is incompatible '.$this->statut; 2139 return -2; 2140 } 2141 } 2142 2143 /** 2144 * Change the multicurrency rate 2145 * 2146 * @param double $rate multicurrency rate 2147 * @param int $mode mode 1 : amounts in company currency will be recalculated, mode 2 : amounts in foreign currency will be recalculated 2148 * @return int >0 if OK, <0 if KO 2149 */ 2150 public function setMulticurrencyRate($rate, $mode = 1) 2151 { 2152 dol_syslog(get_class($this).'::setMulticurrencyRate('.$rate.','.$mode.')'); 2153 if ($this->statut >= 0 || $this->element == 'societe') 2154 { 2155 $fieldname = 'multicurrency_tx'; 2156 2157 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element; 2158 $sql .= ' SET '.$fieldname.' = '.$rate; 2159 $sql .= ' WHERE rowid='.$this->id; 2160 2161 if ($this->db->query($sql)) 2162 { 2163 $this->multicurrency_tx = $rate; 2164 2165 // Update line price 2166 if (!empty($this->lines)) 2167 { 2168 foreach ($this->lines as &$line) 2169 { 2170 // Amounts in company currency will be recalculated 2171 if ($mode == 1) { 2172 $line->subprice = 0; 2173 } 2174 2175 // Amounts in foreign currency will be recalculated 2176 if ($mode == 2) { 2177 $line->multicurrency_subprice = 0; 2178 } 2179 2180 switch ($this->element) { 2181 case 'propal': 2182 $this->updateline( 2183 $line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 2184 ($line->description ? $line->description : $line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line, 2185 $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start, 2186 $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice 2187 ); 2188 break; 2189 case 'commande': 2190 $this->updateline( 2191 $line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->qty, $line->remise_percent, 2192 $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->date_start, $line->date_end, 2193 $line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, 2194 $line->special_code, $line->array_options, $line->fk_unit, $line->multicurrency_subprice 2195 ); 2196 break; 2197 case 'facture': 2198 $this->updateline( 2199 $line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->qty, $line->remise_percent, 2200 $line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, 2201 $line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, 2202 $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice 2203 ); 2204 break; 2205 case 'supplier_proposal': 2206 $this->updateline( 2207 $line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 2208 ($line->description ? $line->description : $line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line, 2209 $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->array_options, 2210 $line->ref_fourn, $line->multicurrency_subprice 2211 ); 2212 break; 2213 case 'order_supplier': 2214 $this->updateline( 2215 $line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->qty, $line->remise_percent, 2216 $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, false, 2217 $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice, 2218 $line->ref_supplier 2219 ); 2220 break; 2221 case 'invoice_supplier': 2222 $this->updateline( 2223 $line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->tva_tx, $line->localtax1_tx, 2224 $line->localtax2_tx, $line->qty, 0, 'HT', $line->info_bits, $line->product_type, $line->remise_percent, false, 2225 $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice, 2226 $line->ref_supplier 2227 ); 2228 break; 2229 default: 2230 dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG); 2231 break; 2232 } 2233 } 2234 } 2235 2236 return 1; 2237 } else { 2238 dol_syslog(get_class($this).'::setMulticurrencyRate Error '.$sql.' - '.$this->db->error()); 2239 $this->error = $this->db->error(); 2240 return -1; 2241 } 2242 } else { 2243 dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible'); 2244 $this->error = 'Status of the object is incompatible '.$this->statut; 2245 return -2; 2246 } 2247 } 2248 2249 /** 2250 * Change the payments terms 2251 * 2252 * @param int $id Id of new payment terms 2253 * @return int >0 if OK, <0 if KO 2254 */ 2255 public function setPaymentTerms($id) 2256 { 2257 dol_syslog(get_class($this).'::setPaymentTerms('.$id.')'); 2258 if ($this->statut >= 0 || $this->element == 'societe') 2259 { 2260 // TODO uniformize field name 2261 $fieldname = 'fk_cond_reglement'; 2262 if ($this->element == 'societe') $fieldname = 'cond_reglement'; 2263 if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier'; 2264 2265 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element; 2266 $sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL'); 2267 $sql .= ' WHERE rowid='.$this->id; 2268 2269 if ($this->db->query($sql)) 2270 { 2271 $this->cond_reglement_id = $id; 2272 // for supplier 2273 if (get_class($this) == 'Fournisseur') $this->cond_reglement_supplier_id = $id; 2274 $this->cond_reglement = $id; // for compatibility 2275 return 1; 2276 } else { 2277 dol_syslog(get_class($this).'::setPaymentTerms Error '.$sql.' - '.$this->db->error()); 2278 $this->error = $this->db->error(); 2279 return -1; 2280 } 2281 } else { 2282 dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible'); 2283 $this->error = 'Status of the object is incompatible '.$this->statut; 2284 return -2; 2285 } 2286 } 2287 2288 /** 2289 * Change the transport mode methods 2290 * 2291 * @param int $id Id of new payment method 2292 * @return int >0 if OK, <0 if KO 2293 */ 2294 public function setTransportMode($id) 2295 { 2296 dol_syslog(get_class($this).'::setTransportMode('.$id.')'); 2297 if ($this->statut >= 0 || $this->element == 'societe') 2298 { 2299 $fieldname = 'fk_transport_mode'; 2300 if ($this->element == 'societe') $fieldname = 'transport_mode'; 2301 if (get_class($this) == 'Fournisseur') $fieldname = 'transport_mode_supplier'; 2302 2303 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element; 2304 $sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL'); 2305 $sql .= ' WHERE rowid='.$this->id; 2306 2307 if ($this->db->query($sql)) 2308 { 2309 $this->transport_mode_id = $id; 2310 // for supplier 2311 if (get_class($this) == 'Fournisseur') $this->transport_mode_supplier_id = $id; 2312 return 1; 2313 } else { 2314 dol_syslog(get_class($this).'::setTransportMode Error '.$sql.' - '.$this->db->error()); 2315 $this->error = $this->db->error(); 2316 return -1; 2317 } 2318 } else { 2319 dol_syslog(get_class($this).'::setTransportMode, status of the object is incompatible'); 2320 $this->error = 'Status of the object is incompatible '.$this->statut; 2321 return -2; 2322 } 2323 } 2324 2325 /** 2326 * Change the retained warranty payments terms 2327 * 2328 * @param int $id Id of new payment terms 2329 * @return int >0 if OK, <0 if KO 2330 */ 2331 public function setRetainedWarrantyPaymentTerms($id) 2332 { 2333 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms('.$id.')'); 2334 if ($this->statut >= 0 || $this->element == 'societe') 2335 { 2336 $fieldname = 'retained_warranty_fk_cond_reglement'; 2337 2338 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element; 2339 $sql .= ' SET '.$fieldname.' = '.$id; 2340 $sql .= ' WHERE rowid='.$this->id; 2341 2342 if ($this->db->query($sql)) 2343 { 2344 $this->retained_warranty_fk_cond_reglement = $id; 2345 return 1; 2346 } else { 2347 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms Error '.$sql.' - '.$this->db->error()); 2348 $this->error = $this->db->error(); 2349 return -1; 2350 } 2351 } else { 2352 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms, status of the object is incompatible'); 2353 $this->error = 'Status of the object is incompatible '.$this->statut; 2354 return -2; 2355 } 2356 } 2357 2358 /** 2359 * Define delivery address 2360 * @deprecated 2361 * 2362 * @param int $id Address id 2363 * @return int <0 si ko, >0 si ok 2364 */ 2365 public function setDeliveryAddress($id) 2366 { 2367 $fieldname = 'fk_delivery_address'; 2368 if ($this->element == 'delivery' || $this->element == 'shipping') $fieldname = 'fk_address'; 2369 2370 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ".$fieldname." = ".$id; 2371 $sql .= " WHERE rowid = ".$this->id." AND fk_statut = 0"; 2372 2373 if ($this->db->query($sql)) 2374 { 2375 $this->fk_delivery_address = $id; 2376 return 1; 2377 } else { 2378 $this->error = $this->db->error(); 2379 dol_syslog(get_class($this).'::setDeliveryAddress Error '.$sql.' - '.$this->error); 2380 return -1; 2381 } 2382 } 2383 2384 2385 /** 2386 * Change the shipping method 2387 * 2388 * @param int $shipping_method_id Id of shipping method 2389 * @param bool $notrigger false=launch triggers after, true=disable triggers 2390 * @param User $userused Object user 2391 * 2392 * @return int 1 if OK, 0 if KO 2393 */ 2394 public function setShippingMethod($shipping_method_id, $notrigger = false, $userused = null) 2395 { 2396 global $user; 2397 2398 if (empty($userused)) $userused = $user; 2399 2400 $error = 0; 2401 2402 if (!$this->table_element) { 2403 dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined", LOG_ERR); 2404 return -1; 2405 } 2406 2407 $this->db->begin(); 2408 2409 if ($shipping_method_id < 0) $shipping_method_id = 'NULL'; 2410 dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')'); 2411 2412 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element; 2413 $sql .= " SET fk_shipping_method = ".$shipping_method_id; 2414 $sql .= " WHERE rowid=".$this->id; 2415 $resql = $this->db->query($sql); 2416 if (!$resql) { 2417 dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG); 2418 $this->error = $this->db->lasterror(); 2419 $error++; 2420 } else { 2421 if (!$notrigger) 2422 { 2423 // Call trigger 2424 $this->context = array('shippingmethodupdate'=>1); 2425 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused); 2426 if ($result < 0) $error++; 2427 // End call trigger 2428 } 2429 } 2430 if ($error) 2431 { 2432 $this->db->rollback(); 2433 return -1; 2434 } else { 2435 $this->shipping_method_id = ($shipping_method_id == 'NULL') ?null:$shipping_method_id; 2436 $this->db->commit(); 2437 return 1; 2438 } 2439 } 2440 2441 2442 /** 2443 * Change the warehouse 2444 * 2445 * @param int $warehouse_id Id of warehouse 2446 * @return int 1 if OK, 0 if KO 2447 */ 2448 public function setWarehouse($warehouse_id) 2449 { 2450 if (!$this->table_element) { 2451 dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined", LOG_ERR); 2452 return -1; 2453 } 2454 if ($warehouse_id < 0) $warehouse_id = 'NULL'; 2455 dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')'); 2456 2457 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element; 2458 $sql .= " SET fk_warehouse = ".$warehouse_id; 2459 $sql .= " WHERE rowid=".$this->id; 2460 2461 if ($this->db->query($sql)) { 2462 $this->warehouse_id = ($warehouse_id == 'NULL') ?null:$warehouse_id; 2463 return 1; 2464 } else { 2465 dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG); 2466 $this->error = $this->db->error(); 2467 return 0; 2468 } 2469 } 2470 2471 2472 /** 2473 * Set last model used by doc generator 2474 * 2475 * @param User $user User object that make change 2476 * @param string $modelpdf Modele name 2477 * @return int <0 if KO, >0 if OK 2478 */ 2479 public function setDocModel($user, $modelpdf) 2480 { 2481 if (!$this->table_element) 2482 { 2483 dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined", LOG_ERR); 2484 return -1; 2485 } 2486 2487 $newmodelpdf = dol_trunc($modelpdf, 255); 2488 2489 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element; 2490 $sql .= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'"; 2491 $sql .= " WHERE rowid = ".$this->id; 2492 2493 dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG); 2494 $resql = $this->db->query($sql); 2495 if ($resql) 2496 { 2497 $this->model_pdf = $modelpdf; 2498 $this->modelpdf = $modelpdf; // For bakward compatibility 2499 return 1; 2500 } else { 2501 dol_print_error($this->db); 2502 return 0; 2503 } 2504 } 2505 2506 2507 /** 2508 * Change the bank account 2509 * 2510 * @param int $fk_account Id of bank account 2511 * @param bool $notrigger false=launch triggers after, true=disable triggers 2512 * @param User $userused Object user 2513 * @return int 1 if OK, 0 if KO 2514 */ 2515 public function setBankAccount($fk_account, $notrigger = false, $userused = null) 2516 { 2517 global $user; 2518 2519 if (empty($userused)) $userused = $user; 2520 2521 $error = 0; 2522 2523 if (!$this->table_element) { 2524 dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined", LOG_ERR); 2525 return -1; 2526 } 2527 $this->db->begin(); 2528 2529 if ($fk_account < 0) $fk_account = 'NULL'; 2530 dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')'); 2531 2532 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element; 2533 $sql .= " SET fk_account = ".$fk_account; 2534 $sql .= " WHERE rowid=".$this->id; 2535 2536 $resql = $this->db->query($sql); 2537 if (!$resql) 2538 { 2539 dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error()); 2540 $this->error = $this->db->lasterror(); 2541 $error++; 2542 } else { 2543 if (!$notrigger) 2544 { 2545 // Call trigger 2546 $this->context = array('bankaccountupdate'=>1); 2547 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused); 2548 if ($result < 0) $error++; 2549 // End call trigger 2550 } 2551 } 2552 if ($error) 2553 { 2554 $this->db->rollback(); 2555 return -1; 2556 } else { 2557 $this->fk_account = ($fk_account == 'NULL') ?null:$fk_account; 2558 $this->db->commit(); 2559 return 1; 2560 } 2561 } 2562 2563 2564 // TODO: Move line related operations to CommonObjectLine? 2565 2566 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 2567 /** 2568 * Save a new position (field rang) for details lines. 2569 * You can choose to set position for lines with already a position or lines without any position defined. 2570 * 2571 * @param boolean $renum True to renum all already ordered lines, false to renum only not already ordered lines. 2572 * @param string $rowidorder ASC or DESC 2573 * @param boolean $fk_parent_line Table with fk_parent_line field or not 2574 * @return int <0 if KO, >0 if OK 2575 */ 2576 public function line_order($renum = false, $rowidorder = 'ASC', $fk_parent_line = true) 2577 { 2578 // phpcs:enable 2579 if (!$this->table_element_line) 2580 { 2581 dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined", LOG_ERR); 2582 return -1; 2583 } 2584 if (!$this->fk_element) 2585 { 2586 dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined", LOG_ERR); 2587 return -1; 2588 } 2589 2590 // Count number of lines to reorder (according to choice $renum) 2591 $nl = 0; 2592 $sql = 'SELECT count(rowid) FROM '.MAIN_DB_PREFIX.$this->table_element_line; 2593 $sql .= ' WHERE '.$this->fk_element.'='.$this->id; 2594 if (!$renum) $sql .= ' AND rang = 0'; 2595 if ($renum) $sql .= ' AND rang <> 0'; 2596 2597 dol_syslog(get_class($this)."::line_order", LOG_DEBUG); 2598 $resql = $this->db->query($sql); 2599 if ($resql) 2600 { 2601 $row = $this->db->fetch_row($resql); 2602 $nl = $row[0]; 2603 } else dol_print_error($this->db); 2604 if ($nl > 0) 2605 { 2606 // The goal of this part is to reorder all lines, with all children lines sharing the same counter that parents. 2607 $rows = array(); 2608 2609 // We first search all lines that are parent lines (for multilevel details lines) 2610 $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line; 2611 $sql .= ' WHERE '.$this->fk_element.' = '.$this->id; 2612 if ($fk_parent_line) $sql .= ' AND fk_parent_line IS NULL'; 2613 $sql .= ' ORDER BY rang ASC, rowid '.$rowidorder; 2614 2615 dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG); 2616 $resql = $this->db->query($sql); 2617 if ($resql) 2618 { 2619 $i = 0; 2620 $num = $this->db->num_rows($resql); 2621 while ($i < $num) 2622 { 2623 $row = $this->db->fetch_row($resql); 2624 $rows[] = $row[0]; // Add parent line into array rows 2625 $childrens = $this->getChildrenOfLine($row[0]); 2626 if (!empty($childrens)) 2627 { 2628 foreach ($childrens as $child) 2629 { 2630 array_push($rows, $child); 2631 } 2632 } 2633 $i++; 2634 } 2635 2636 // Now we set a new number for each lines (parent and children with children included into parent tree) 2637 if (!empty($rows)) 2638 { 2639 foreach ($rows as $key => $row) 2640 { 2641 $this->updateRangOfLine($row, ($key + 1)); 2642 } 2643 } 2644 } else { 2645 dol_print_error($this->db); 2646 } 2647 } 2648 return 1; 2649 } 2650 2651 /** 2652 * Get children of line 2653 * 2654 * @param int $id Id of parent line 2655 * @param int $includealltree 0 = 1st level child, 1 = All level child 2656 * @return array Array with list of children lines id 2657 */ 2658 public function getChildrenOfLine($id, $includealltree = 0) 2659 { 2660 $rows = array(); 2661 2662 $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line; 2663 $sql .= ' WHERE '.$this->fk_element.' = '.$this->id; 2664 $sql .= ' AND fk_parent_line = '.$id; 2665 $sql .= ' ORDER BY rang ASC'; 2666 2667 dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id."", LOG_DEBUG); 2668 $resql = $this->db->query($sql); 2669 if ($resql) 2670 { 2671 if ($this->db->num_rows($resql) > 0) { 2672 while ($row = $this->db->fetch_row($resql)) { 2673 $rows[] = $row[0]; 2674 if (!empty($includealltree)) $rows = array_merge($rows, $this->getChildrenOfLine($row[0]), $includealltree); 2675 } 2676 } 2677 } 2678 return $rows; 2679 } 2680 2681 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 2682 /** 2683 * Update a line to have a lower rank 2684 * 2685 * @param int $rowid Id of line 2686 * @param boolean $fk_parent_line Table with fk_parent_line field or not 2687 * @return void 2688 */ 2689 public function line_up($rowid, $fk_parent_line = true) 2690 { 2691 // phpcs:enable 2692 $this->line_order(false, 'ASC', $fk_parent_line); 2693 2694 // Get rang of line 2695 $rang = $this->getRangOfLine($rowid); 2696 2697 // Update position of line 2698 $this->updateLineUp($rowid, $rang); 2699 } 2700 2701 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 2702 /** 2703 * Update a line to have a higher rank 2704 * 2705 * @param int $rowid Id of line 2706 * @param boolean $fk_parent_line Table with fk_parent_line field or not 2707 * @return void 2708 */ 2709 public function line_down($rowid, $fk_parent_line = true) 2710 { 2711 // phpcs:enable 2712 $this->line_order(false, 'ASC', $fk_parent_line); 2713 2714 // Get rang of line 2715 $rang = $this->getRangOfLine($rowid); 2716 2717 // Get max value for rang 2718 $max = $this->line_max(); 2719 2720 // Update position of line 2721 $this->updateLineDown($rowid, $rang, $max); 2722 } 2723 2724 /** 2725 * Update position of line (rang) 2726 * 2727 * @param int $rowid Id of line 2728 * @param int $rang Position 2729 * @return void 2730 */ 2731 public function updateRangOfLine($rowid, $rang) 2732 { 2733 $fieldposition = 'rang'; // @todo Rename 'rang' into 'position' 2734 if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position'; 2735 2736 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang; 2737 $sql .= ' WHERE rowid = '.$rowid; 2738 2739 dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG); 2740 if (!$this->db->query($sql)) 2741 { 2742 dol_print_error($this->db); 2743 } 2744 } 2745 2746 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 2747 /** 2748 * Update position of line with ajax (rang) 2749 * 2750 * @param array $rows Array of rows 2751 * @return void 2752 */ 2753 public function line_ajaxorder($rows) 2754 { 2755 // phpcs:enable 2756 $num = count($rows); 2757 for ($i = 0; $i < $num; $i++) 2758 { 2759 $this->updateRangOfLine($rows[$i], ($i + 1)); 2760 } 2761 } 2762 2763 /** 2764 * Update position of line up (rang) 2765 * 2766 * @param int $rowid Id of line 2767 * @param int $rang Position 2768 * @return void 2769 */ 2770 public function updateLineUp($rowid, $rang) 2771 { 2772 if ($rang > 1) 2773 { 2774 $fieldposition = 'rang'; 2775 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position'; 2776 2777 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang; 2778 $sql .= ' WHERE '.$this->fk_element.' = '.$this->id; 2779 $sql .= ' AND rang = '.($rang - 1); 2780 if ($this->db->query($sql)) 2781 { 2782 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang - 1); 2783 $sql .= ' WHERE rowid = '.$rowid; 2784 if (!$this->db->query($sql)) 2785 { 2786 dol_print_error($this->db); 2787 } 2788 } else { 2789 dol_print_error($this->db); 2790 } 2791 } 2792 } 2793 2794 /** 2795 * Update position of line down (rang) 2796 * 2797 * @param int $rowid Id of line 2798 * @param int $rang Position 2799 * @param int $max Max 2800 * @return void 2801 */ 2802 public function updateLineDown($rowid, $rang, $max) 2803 { 2804 if ($rang < $max) 2805 { 2806 $fieldposition = 'rang'; 2807 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position'; 2808 2809 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang; 2810 $sql .= ' WHERE '.$this->fk_element.' = '.$this->id; 2811 $sql .= ' AND rang = '.($rang + 1); 2812 if ($this->db->query($sql)) 2813 { 2814 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang + 1); 2815 $sql .= ' WHERE rowid = '.$rowid; 2816 if (!$this->db->query($sql)) 2817 { 2818 dol_print_error($this->db); 2819 } 2820 } else { 2821 dol_print_error($this->db); 2822 } 2823 } 2824 } 2825 2826 /** 2827 * Get position of line (rang) 2828 * 2829 * @param int $rowid Id of line 2830 * @return int Value of rang in table of lines 2831 */ 2832 public function getRangOfLine($rowid) 2833 { 2834 $sql = 'SELECT rang FROM '.MAIN_DB_PREFIX.$this->table_element_line; 2835 $sql .= ' WHERE rowid ='.$rowid; 2836 2837 dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG); 2838 $resql = $this->db->query($sql); 2839 if ($resql) 2840 { 2841 $row = $this->db->fetch_row($resql); 2842 return $row[0]; 2843 } 2844 } 2845 2846 /** 2847 * Get rowid of the line relative to its position 2848 * 2849 * @param int $rang Rang value 2850 * @return int Rowid of the line 2851 */ 2852 public function getIdOfLine($rang) 2853 { 2854 $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line; 2855 $sql .= ' WHERE '.$this->fk_element.' = '.$this->id; 2856 $sql .= ' AND rang = '.$rang; 2857 $resql = $this->db->query($sql); 2858 if ($resql) 2859 { 2860 $row = $this->db->fetch_row($resql); 2861 return $row[0]; 2862 } 2863 } 2864 2865 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 2866 /** 2867 * Get max value used for position of line (rang) 2868 * 2869 * @param int $fk_parent_line Parent line id 2870 * @return int Max value of rang in table of lines 2871 */ 2872 public function line_max($fk_parent_line = 0) 2873 { 2874 // phpcs:enable 2875 $positionfield = 'rang'; 2876 if ($this->table_element == 'bom_bom') $positionfield = 'position'; 2877 2878 // Search the last rang with fk_parent_line 2879 if ($fk_parent_line) 2880 { 2881 $sql = 'SELECT max('.$positionfield.') FROM '.MAIN_DB_PREFIX.$this->table_element_line; 2882 $sql .= ' WHERE '.$this->fk_element.' = '.$this->id; 2883 $sql .= ' AND fk_parent_line = '.$fk_parent_line; 2884 2885 dol_syslog(get_class($this)."::line_max", LOG_DEBUG); 2886 $resql = $this->db->query($sql); 2887 if ($resql) 2888 { 2889 $row = $this->db->fetch_row($resql); 2890 if (!empty($row[0])) 2891 { 2892 return $row[0]; 2893 } else { 2894 return $this->getRangOfLine($fk_parent_line); 2895 } 2896 } 2897 } 2898 // If not, search the last rang of element 2899 else { 2900 $sql = 'SELECT max('.$positionfield.') FROM '.MAIN_DB_PREFIX.$this->table_element_line; 2901 $sql .= ' WHERE '.$this->fk_element.' = '.$this->id; 2902 2903 dol_syslog(get_class($this)."::line_max", LOG_DEBUG); 2904 $resql = $this->db->query($sql); 2905 if ($resql) 2906 { 2907 $row = $this->db->fetch_row($resql); 2908 return $row[0]; 2909 } 2910 } 2911 } 2912 2913 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 2914 /** 2915 * Update external ref of element 2916 * 2917 * @param string $ref_ext Update field ref_ext 2918 * @return int <0 if KO, >0 if OK 2919 */ 2920 public function update_ref_ext($ref_ext) 2921 { 2922 // phpcs:enable 2923 if (!$this->table_element) 2924 { 2925 dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR); 2926 return -1; 2927 } 2928 2929 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element; 2930 $sql .= " SET ref_ext = '".$this->db->escape($ref_ext)."'"; 2931 $sql .= " WHERE ".(isset($this->table_rowid) ? $this->table_rowid : 'rowid')." = ".$this->id; 2932 2933 dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG); 2934 if ($this->db->query($sql)) 2935 { 2936 $this->ref_ext = $ref_ext; 2937 return 1; 2938 } else { 2939 $this->error = $this->db->error(); 2940 return -1; 2941 } 2942 } 2943 2944 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 2945 /** 2946 * Update note of element 2947 * 2948 * @param string $note New value for note 2949 * @param string $suffix '', '_public' or '_private' 2950 * @return int <0 if KO, >0 if OK 2951 */ 2952 public function update_note($note, $suffix = '') 2953 { 2954 // phpcs:enable 2955 global $user; 2956 2957 if (!$this->table_element) 2958 { 2959 $this->error = 'update_note was called on objet with property table_element not defined'; 2960 dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR); 2961 return -1; 2962 } 2963 if (!in_array($suffix, array('', '_public', '_private'))) 2964 { 2965 $this->error = 'update_note Parameter suffix must be empty, \'_private\' or \'_public\''; 2966 dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR); 2967 return -2; 2968 } 2969 2970 $newsuffix = $suffix; 2971 2972 // Special cas 2973 if ($this->table_element == 'product' && $newsuffix == '_private') $newsuffix = ''; 2974 2975 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element; 2976 $sql .= " SET note".$newsuffix." = ".(!empty($note) ? ("'".$this->db->escape($note)."'") : "NULL"); 2977 $sql .= " ,".(in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment')) ? "fk_user_mod" : "fk_user_modif")." = ".$user->id; 2978 $sql .= " WHERE rowid =".$this->id; 2979 2980 dol_syslog(get_class($this)."::update_note", LOG_DEBUG); 2981 if ($this->db->query($sql)) 2982 { 2983 if ($suffix == '_public') $this->note_public = $note; 2984 elseif ($suffix == '_private') $this->note_private = $note; 2985 else { 2986 $this->note = $note; // deprecated 2987 $this->note_private = $note; 2988 } 2989 return 1; 2990 } else { 2991 $this->error = $this->db->lasterror(); 2992 return -1; 2993 } 2994 } 2995 2996 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 2997 /** 2998 * Update public note (kept for backward compatibility) 2999 * 3000 * @param string $note New value for note 3001 * @return int <0 if KO, >0 if OK 3002 * @deprecated 3003 * @see update_note() 3004 */ 3005 public function update_note_public($note) 3006 { 3007 // phpcs:enable 3008 return $this->update_note($note, '_public'); 3009 } 3010 3011 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 3012 /** 3013 * Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines). 3014 * Must be called at end of methods addline or updateline. 3015 * 3016 * @param int $exclspec >0 = Exclude special product (product_type=9) 3017 * @param string $roundingadjust 'none'=Do nothing, 'auto'=Use default method (MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND if defined, or '0'), '0'=Force mode total of rounding, '1'=Force mode rounding of total 3018 * @param int $nodatabaseupdate 1=Do not update database. Update only properties of object. 3019 * @param Societe $seller If roundingadjust is '0' or '1' or maybe 'auto', it means we recalculate total for lines before calculating total for object and for this, we need seller object. 3020 * @return int <0 if KO, >0 if OK 3021 */ 3022 public function update_price($exclspec = 0, $roundingadjust = 'none', $nodatabaseupdate = 0, $seller = null) 3023 { 3024 // phpcs:enable 3025 global $conf, $hookmanager, $action; 3026 3027 $parameters = array('exclspec' => $exclspec, 'roundingadjust' => $roundingadjust, 'nodatabaseupdate' => $nodatabaseupdate, 'seller' => $seller); 3028 $reshook = $hookmanager->executeHooks('updateTotalPrice', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks 3029 if ($reshook > 0) { 3030 return 1; // replacement code 3031 } elseif ($reshook < 0) { 3032 return -1; // failure 3033 } // reshook = 0 => execute normal code 3034 3035 // Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield) 3036 $MODULE = ""; 3037 if ($this->element == 'propal') 3038 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL"; 3039 elseif ($this->element == 'commande' || $this->element == 'order') 3040 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER"; 3041 elseif ($this->element == 'facture' || $this->element == 'invoice') 3042 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE"; 3043 elseif ($this->element == 'facture_fourn' || $this->element == 'supplier_invoice' || $this->element == 'invoice_supplier') 3044 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE"; 3045 elseif ($this->element == 'order_supplier' || $this->element == 'supplier_order') 3046 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER"; 3047 elseif ($this->element == 'supplier_proposal') 3048 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL"; 3049 3050 if (!empty($MODULE)) { 3051 if (!empty($conf->global->$MODULE)) { 3052 $modsactivated = explode(',', $conf->global->$MODULE); 3053 foreach ($modsactivated as $mod) { 3054 if ($conf->$mod->enabled) 3055 return 1; // update was disabled by specific setup 3056 } 3057 } 3058 } 3059 3060 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php'; 3061 3062 if ($roundingadjust == '-1') $roundingadjust = 'auto'; // For backward compatibility 3063 3064 $forcedroundingmode = $roundingadjust; 3065 if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) $forcedroundingmode = $conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND; 3066 elseif ($forcedroundingmode == 'auto') $forcedroundingmode = '0'; 3067 3068 $error = 0; 3069 3070 $multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1; 3071 3072 // Define constants to find lines to sum 3073 $fieldtva = 'total_tva'; 3074 $fieldlocaltax1 = 'total_localtax1'; 3075 $fieldlocaltax2 = 'total_localtax2'; 3076 $fieldup = 'subprice'; 3077 if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') 3078 { 3079 $fieldtva = 'tva'; 3080 $fieldup = 'pu_ht'; 3081 } 3082 if ($this->element == 'expensereport') 3083 { 3084 $fieldup = 'value_unit'; 3085 } 3086 3087 $sql = 'SELECT rowid, qty, '.$fieldup.' as up, remise_percent, total_ht, '.$fieldtva.' as total_tva, total_ttc, '.$fieldlocaltax1.' as total_localtax1, '.$fieldlocaltax2.' as total_localtax2,'; 3088 $sql .= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type'; 3089 if ($this->table_element_line == 'facturedet') $sql .= ', situation_percent'; 3090 $sql .= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc'; 3091 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line; 3092 $sql .= ' WHERE '.$this->fk_element.' = '.$this->id; 3093 if ($exclspec) 3094 { 3095 $product_field = 'product_type'; 3096 if ($this->table_element_line == 'contratdet') $product_field = ''; // contratdet table has no product_type field 3097 if ($product_field) $sql .= ' AND '.$product_field.' <> 9'; 3098 } 3099 $sql .= ' ORDER by rowid'; // We want to be sure to always use same order of line to not change lines differently when option MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND is used 3100 3101 dol_syslog(get_class($this)."::update_price", LOG_DEBUG); 3102 $resql = $this->db->query($sql); 3103 if ($resql) 3104 { 3105 $this->total_ht = 0; 3106 $this->total_tva = 0; 3107 $this->total_localtax1 = 0; 3108 $this->total_localtax2 = 0; 3109 $this->total_ttc = 0; 3110 $total_ht_by_vats = array(); 3111 $total_tva_by_vats = array(); 3112 $total_ttc_by_vats = array(); 3113 $this->multicurrency_total_ht = 0; 3114 $this->multicurrency_total_tva = 0; 3115 $this->multicurrency_total_ttc = 0; 3116 3117 $num = $this->db->num_rows($resql); 3118 $i = 0; 3119 while ($i < $num) 3120 { 3121 $obj = $this->db->fetch_object($resql); 3122 3123 // Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none' 3124 $parameters = array('fk_element' => $obj->rowid); 3125 $reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks 3126 3127 if (empty($reshook) && $forcedroundingmode == '0') // Check if data on line are consistent. This may solve lines that were not consistent because set with $forcedroundingmode='auto' 3128 { 3129 // This part of code is to fix data. We should not call it too often. 3130 $localtax_array = array($obj->localtax1_type, $obj->localtax1_tx, $obj->localtax2_type, $obj->localtax2_tx); 3131 $tmpcal = calcul_price_total($obj->qty, $obj->up, $obj->remise_percent, $obj->vatrate, $obj->localtax1_tx, $obj->localtax2_tx, 0, 'HT', $obj->info_bits, $obj->product_type, $seller, $localtax_array, (isset($obj->situation_percent) ? $obj->situation_percent : 100), $multicurrency_tx); 3132 3133 $diff_when_using_price_ht = price2num($tmpcal[1] - $obj->total_tva, 'MT', 1); // If price was set with tax price adn unit price HT has a low number of digits, then we may have a diff on recalculation from unit price HT. 3134 $diff_on_current_total = price2num($obj->total_ttc - $obj->total_ht - $obj->total_tva - $obj->total_localtax1 - $obj->total_localtax2, 'MT', 1); 3135 //var_dump($obj->total_ht.' '.$obj->total_tva.' '.$obj->total_localtax1.' '.$obj->total_localtax2.' =? '.$obj->total_ttc); 3136 //var_dump($diff_when_using_price_ht.' '.$diff_on_current_total); 3137 3138 if ($diff_when_using_price_ht && $diff_on_current_total) 3139 { 3140 $sqlfix = "UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".$tmpcal[1].", total_ttc = ".$tmpcal[2]." WHERE rowid = ".$obj->rowid; 3141 dol_syslog('We found unconsistent data into detailed line (diff_when_using_price_ht = '.$diff_when_using_price_ht.' and diff_on_current_total = '.$diff_on_current_total.') for line rowid = '.$obj->rowid." (total vat of line calculated=".$tmpcal[1].", database=".$obj->total_tva."). We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix, LOG_WARNING); 3142 $resqlfix = $this->db->query($sqlfix); 3143 if (!$resqlfix) dol_print_error($this->db, 'Failed to update line'); 3144 $obj->total_tva = $tmpcal[1]; 3145 $obj->total_ttc = $tmpcal[2]; 3146 } 3147 } 3148 3149 $this->total_ht += $obj->total_ht; // The field visible at end of line detail 3150 $this->total_tva += $obj->total_tva; 3151 $this->total_localtax1 += $obj->total_localtax1; 3152 $this->total_localtax2 += $obj->total_localtax2; 3153 $this->total_ttc += $obj->total_ttc; 3154 $this->multicurrency_total_ht += $obj->multicurrency_total_ht; // The field visible at end of line detail 3155 $this->multicurrency_total_tva += $obj->multicurrency_total_tva; 3156 $this->multicurrency_total_ttc += $obj->multicurrency_total_ttc; 3157 3158 if (!isset($total_ht_by_vats[$obj->vatrate])) $total_ht_by_vats[$obj->vatrate] = 0; 3159 if (!isset($total_tva_by_vats[$obj->vatrate])) $total_tva_by_vats[$obj->vatrate] = 0; 3160 if (!isset($total_ttc_by_vats[$obj->vatrate])) $total_ttc_by_vats[$obj->vatrate] = 0; 3161 $total_ht_by_vats[$obj->vatrate] += $obj->total_ht; 3162 $total_tva_by_vats[$obj->vatrate] += $obj->total_tva; 3163 $total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc; 3164 3165 if ($forcedroundingmode == '1') // Check if we need adjustement onto line for vat. TODO This works on the company currency but not on multicurrency 3166 { 3167 $tmpvat = price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1); 3168 $diff = price2num($total_tva_by_vats[$obj->vatrate] - $tmpvat, 'MT', 1); 3169 //print 'Line '.$i.' rowid='.$obj->rowid.' vat_rate='.$obj->vatrate.' total_ht='.$obj->total_ht.' total_tva='.$obj->total_tva.' total_ttc='.$obj->total_ttc.' total_ht_by_vats='.$total_ht_by_vats[$obj->vatrate].' total_tva_by_vats='.$total_tva_by_vats[$obj->vatrate].' (new calculation = '.$tmpvat.') total_ttc_by_vats='.$total_ttc_by_vats[$obj->vatrate].($diff?" => DIFF":"")."<br>\n"; 3170 if ($diff) 3171 { 3172 if (abs($diff) > 0.1) { 3173 $errmsg = 'A rounding difference was detected into TOTAL but is too high to be corrected. Some data in your line may be corrupted. Try to edit each line manually.'; 3174 dol_syslog($errmsg, LOG_WARNING); 3175 dol_print_error('', $errmsg); 3176 exit; 3177 } 3178 $sqlfix = "UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".($obj->total_tva - $diff).", total_ttc = ".($obj->total_ttc - $diff)." WHERE rowid = ".$obj->rowid; 3179 dol_syslog('We found a difference of '.$diff.' for line rowid = '.$obj->rowid.". We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix); 3180 $resqlfix = $this->db->query($sqlfix); 3181 if (!$resqlfix) dol_print_error($this->db, 'Failed to update line'); 3182 $this->total_tva -= $diff; 3183 $this->total_ttc -= $diff; 3184 $total_tva_by_vats[$obj->vatrate] -= $diff; 3185 $total_ttc_by_vats[$obj->vatrate] -= $diff; 3186 } 3187 } 3188 3189 $i++; 3190 } 3191 3192 // Add revenue stamp to total 3193 $this->total_ttc += isset($this->revenuestamp) ? $this->revenuestamp : 0; 3194 $this->multicurrency_total_ttc += isset($this->revenuestamp) ? ($this->revenuestamp * $multicurrency_tx) : 0; 3195 3196 // Situations totals 3197 if (!empty($this->situation_cycle_ref) && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits') && $this->type != $this::TYPE_CREDIT_NOTE) 3198 { 3199 $prev_sits = $this->get_prev_sits(); 3200 3201 foreach ($prev_sits as $sit) { // $sit is an object Facture loaded with a fetch. 3202 $this->total_ht -= $sit->total_ht; 3203 $this->total_tva -= $sit->total_tva; 3204 $this->total_localtax1 -= $sit->total_localtax1; 3205 $this->total_localtax2 -= $sit->total_localtax2; 3206 $this->total_ttc -= $sit->total_ttc; 3207 $this->multicurrency_total_ht -= $sit->multicurrency_total_ht; 3208 $this->multicurrency_total_tva -= $sit->multicurrency_total_tva; 3209 $this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc; 3210 } 3211 } 3212 3213 $this->db->free($resql); 3214 3215 // Now update global field total_ht, total_ttc and tva 3216 $fieldht = 'total_ht'; 3217 $fieldtva = 'tva'; 3218 $fieldlocaltax1 = 'localtax1'; 3219 $fieldlocaltax2 = 'localtax2'; 3220 $fieldttc = 'total_ttc'; 3221 // Specific code for backward compatibility with old field names 3222 if ($this->element == 'facture' || $this->element == 'facturerec') $fieldht = 'total'; 3223 if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') $fieldtva = 'total_tva'; 3224 if ($this->element == 'propal') $fieldttc = 'total'; 3225 if ($this->element == 'expensereport') $fieldtva = 'total_tva'; 3226 if ($this->element == 'supplier_proposal') $fieldttc = 'total'; 3227 3228 if (empty($nodatabaseupdate)) 3229 { 3230 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET'; 3231 $sql .= " ".$fieldht."='".price2num($this->total_ht)."',"; 3232 $sql .= " ".$fieldtva."='".price2num($this->total_tva)."',"; 3233 $sql .= " ".$fieldlocaltax1."='".price2num($this->total_localtax1)."',"; 3234 $sql .= " ".$fieldlocaltax2."='".price2num($this->total_localtax2)."',"; 3235 $sql .= " ".$fieldttc."='".price2num($this->total_ttc)."'"; 3236 $sql .= ", multicurrency_total_ht='".price2num($this->multicurrency_total_ht, 'MT', 1)."'"; 3237 $sql .= ", multicurrency_total_tva='".price2num($this->multicurrency_total_tva, 'MT', 1)."'"; 3238 $sql .= ", multicurrency_total_ttc='".price2num($this->multicurrency_total_ttc, 'MT', 1)."'"; 3239 $sql .= ' WHERE rowid = '.$this->id; 3240 3241 3242 dol_syslog(get_class($this)."::update_price", LOG_DEBUG); 3243 $resql = $this->db->query($sql); 3244 if (!$resql) 3245 { 3246 $error++; 3247 $this->error = $this->db->lasterror(); 3248 $this->errors[] = $this->db->lasterror(); 3249 } 3250 } 3251 3252 if (!$error) 3253 { 3254 return 1; 3255 } else { 3256 return -1; 3257 } 3258 } else { 3259 dol_print_error($this->db, 'Bad request in update_price'); 3260 return -1; 3261 } 3262 } 3263 3264 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 3265 /** 3266 * Add objects linked in llx_element_element. 3267 * 3268 * @param string $origin Linked element type 3269 * @param int $origin_id Linked element id 3270 * @return int <=0 if KO, >0 if OK 3271 * @see fetchObjectLinked(), updateObjectLinked(), deleteObjectLinked() 3272 */ 3273 public function add_object_linked($origin = null, $origin_id = null) 3274 { 3275 // phpcs:enable 3276 $origin = (!empty($origin) ? $origin : $this->origin); 3277 $origin_id = (!empty($origin_id) ? $origin_id : $this->origin_id); 3278 3279 // Special case 3280 if ($origin == 'order') $origin = 'commande'; 3281 if ($origin == 'invoice') $origin = 'facture'; 3282 if ($origin == 'invoice_template') $origin = 'facturerec'; 3283 if ($origin == 'supplierorder') $origin = 'order_supplier'; 3284 $this->db->begin(); 3285 3286 $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element ("; 3287 $sql .= "fk_source"; 3288 $sql .= ", sourcetype"; 3289 $sql .= ", fk_target"; 3290 $sql .= ", targettype"; 3291 $sql .= ") VALUES ("; 3292 $sql .= $origin_id; 3293 $sql .= ", '".$this->db->escape($origin)."'"; 3294 $sql .= ", ".$this->id; 3295 $sql .= ", '".$this->db->escape($this->element)."'"; 3296 $sql .= ")"; 3297 3298 dol_syslog(get_class($this)."::add_object_linked", LOG_DEBUG); 3299 if ($this->db->query($sql)) 3300 { 3301 $this->db->commit(); 3302 return 1; 3303 } else { 3304 $this->error = $this->db->lasterror(); 3305 $this->db->rollback(); 3306 return 0; 3307 } 3308 } 3309 3310 /** 3311 * Fetch array of objects linked to current object (object of enabled modules only). Links are loaded into 3312 * this->linkedObjectsIds array + 3313 * this->linkedObjects array if $loadalsoobjects = 1 3314 * Possible usage for parameters: 3315 * - all parameters empty -> we look all link to current object (current object can be source or target) 3316 * - source id+type -> will get target list linked to source 3317 * - target id+type -> will get source list linked to target 3318 * - source id+type + target type -> will get target list of the type 3319 * - target id+type + target source -> will get source list of the type 3320 * 3321 * @param int $sourceid Object source id (if not defined, id of object) 3322 * @param string $sourcetype Object source type (if not defined, element name of object) 3323 * @param int $targetid Object target id (if not defined, id of object) 3324 * @param string $targettype Object target type (if not defined, elemennt name of object) 3325 * @param string $clause 'OR' or 'AND' clause used when both source id and target id are provided 3326 * @param int $alsosametype 0=Return only links to object that differs from source type. 1=Include also link to objects of same type. 3327 * @param string $orderby SQL 'ORDER BY' clause 3328 * @param int $loadalsoobjects Load also array this->linkedObjects (Use 0 to increase performances) 3329 * @return int <0 if KO, >0 if OK 3330 * @see add_object_linked(), updateObjectLinked(), deleteObjectLinked() 3331 */ 3332 public function fetchObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1) 3333 { 3334 global $conf; 3335 3336 $this->linkedObjectsIds = array(); 3337 $this->linkedObjects = array(); 3338 3339 $justsource = false; 3340 $justtarget = false; 3341 $withtargettype = false; 3342 $withsourcetype = false; 3343 3344 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid)) 3345 { 3346 $justsource = true; // the source (id and type) is a search criteria 3347 if (!empty($targettype)) $withtargettype = true; 3348 } 3349 if (!empty($targetid) && !empty($targettype) && empty($sourceid)) 3350 { 3351 $justtarget = true; // the target (id and type) is a search criteria 3352 if (!empty($sourcetype)) $withsourcetype = true; 3353 } 3354 3355 $sourceid = (!empty($sourceid) ? $sourceid : $this->id); 3356 $targetid = (!empty($targetid) ? $targetid : $this->id); 3357 $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element); 3358 $targettype = (!empty($targettype) ? $targettype : $this->element); 3359 3360 /*if (empty($sourceid) && empty($targetid)) 3361 { 3362 dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR); 3363 return -1; 3364 }*/ 3365 3366 // Links between objects are stored in table element_element 3367 $sql = 'SELECT rowid, fk_source, sourcetype, fk_target, targettype'; 3368 $sql .= ' FROM '.MAIN_DB_PREFIX.'element_element'; 3369 $sql .= " WHERE "; 3370 if ($justsource || $justtarget) 3371 { 3372 if ($justsource) 3373 { 3374 $sql .= "fk_source = ".$sourceid." AND sourcetype = '".$this->db->escape($sourcetype)."'"; 3375 if ($withtargettype) $sql .= " AND targettype = '".$this->db->escape($targettype)."'"; 3376 } elseif ($justtarget) 3377 { 3378 $sql .= "fk_target = ".$targetid." AND targettype = '".$this->db->escape($targettype)."'"; 3379 if ($withsourcetype) $sql .= " AND sourcetype = '".$this->db->escape($sourcetype)."'"; 3380 } 3381 } else { 3382 $sql .= "(fk_source = ".$sourceid." AND sourcetype = '".$this->db->escape($sourcetype)."')"; 3383 $sql .= " ".$clause." (fk_target = ".$targetid." AND targettype = '".$this->db->escape($targettype)."')"; 3384 } 3385 $sql .= ' ORDER BY '.$orderby; 3386 3387 dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG); 3388 $resql = $this->db->query($sql); 3389 if ($resql) 3390 { 3391 $num = $this->db->num_rows($resql); 3392 $i = 0; 3393 while ($i < $num) 3394 { 3395 $obj = $this->db->fetch_object($resql); 3396 if ($justsource || $justtarget) 3397 { 3398 if ($justsource) 3399 { 3400 $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target; 3401 } elseif ($justtarget) 3402 { 3403 $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source; 3404 } 3405 } else { 3406 if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype) 3407 { 3408 $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target; 3409 } 3410 if ($obj->fk_target == $targetid && $obj->targettype == $targettype) 3411 { 3412 $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source; 3413 } 3414 } 3415 $i++; 3416 } 3417 3418 if (!empty($this->linkedObjectsIds)) 3419 { 3420 $tmparray = $this->linkedObjectsIds; 3421 foreach ($tmparray as $objecttype => $objectids) // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...) 3422 { 3423 // Parse element/subelement (ex: project_task, cabinetmed_consultation, ...) 3424 $module = $element = $subelement = $objecttype; 3425 $regs = array(); 3426 if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier' 3427 && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) 3428 { 3429 $module = $element = $regs[1]; 3430 $subelement = $regs[2]; 3431 } 3432 3433 $classpath = $element.'/class'; 3434 // To work with non standard classpath or module name 3435 if ($objecttype == 'facture') { 3436 $classpath = 'compta/facture/class'; 3437 } elseif ($objecttype == 'facturerec') { 3438 $classpath = 'compta/facture/class'; $module = 'facture'; 3439 } elseif ($objecttype == 'propal') { 3440 $classpath = 'comm/propal/class'; 3441 } elseif ($objecttype == 'supplier_proposal') { 3442 $classpath = 'supplier_proposal/class'; 3443 } elseif ($objecttype == 'shipping') { 3444 $classpath = 'expedition/class'; $subelement = 'expedition'; $module = 'expedition_bon'; 3445 } elseif ($objecttype == 'delivery') { 3446 $classpath = 'delivery/class'; $subelement = 'delivery'; $module = 'delivery_note'; 3447 } elseif ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier') { 3448 $classpath = 'fourn/class'; $module = 'fournisseur'; 3449 } elseif ($objecttype == 'fichinter') { 3450 $classpath = 'fichinter/class'; $subelement = 'fichinter'; $module = 'ficheinter'; 3451 } elseif ($objecttype == 'subscription') { 3452 $classpath = 'adherents/class'; $module = 'adherent'; 3453 } elseif ($objecttype == 'contact') { 3454 $module = 'societe'; 3455 } 3456 3457 // Set classfile 3458 $classfile = strtolower($subelement); $classname = ucfirst($subelement); 3459 3460 if ($objecttype == 'order') { 3461 $classfile = 'commande'; $classname = 'Commande'; 3462 } elseif ($objecttype == 'invoice_supplier') { 3463 $classfile = 'fournisseur.facture'; $classname = 'FactureFournisseur'; 3464 } elseif ($objecttype == 'order_supplier') { 3465 $classfile = 'fournisseur.commande'; $classname = 'CommandeFournisseur'; 3466 } elseif ($objecttype == 'supplier_proposal') { 3467 $classfile = 'supplier_proposal'; $classname = 'SupplierProposal'; 3468 } elseif ($objecttype == 'facturerec') { 3469 $classfile = 'facture-rec'; $classname = 'FactureRec'; 3470 } elseif ($objecttype == 'subscription') { 3471 $classfile = 'subscription'; $classname = 'Subscription'; 3472 } elseif ($objecttype == 'project' || $objecttype == 'projet') { 3473 $classpath = 'projet/class'; $classfile = 'project'; $classname = 'Project'; 3474 } 3475 3476 // Here $module, $classfile and $classname are set 3477 if ($conf->$module->enabled && (($element != $this->element) || $alsosametype)) 3478 { 3479 if ($loadalsoobjects) 3480 { 3481 dol_include_once('/'.$classpath.'/'.$classfile.'.class.php'); 3482 //print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname); 3483 if (class_exists($classname)) 3484 { 3485 foreach ($objectids as $i => $objectid) // $i is rowid into llx_element_element 3486 { 3487 $object = new $classname($this->db); 3488 $ret = $object->fetch($objectid); 3489 if ($ret >= 0) 3490 { 3491 $this->linkedObjects[$objecttype][$i] = $object; 3492 } 3493 } 3494 } 3495 } 3496 } else { 3497 unset($this->linkedObjectsIds[$objecttype]); 3498 } 3499 } 3500 } 3501 return 1; 3502 } else { 3503 dol_print_error($this->db); 3504 return -1; 3505 } 3506 } 3507 3508 /** 3509 * Update object linked of a current object 3510 * 3511 * @param int $sourceid Object source id 3512 * @param string $sourcetype Object source type 3513 * @param int $targetid Object target id 3514 * @param string $targettype Object target type 3515 * @return int >0 if OK, <0 if KO 3516 * @see add_object_linked(), fetObjectLinked(), deleteObjectLinked() 3517 */ 3518 public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '') 3519 { 3520 $updatesource = false; 3521 $updatetarget = false; 3522 3523 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) $updatesource = true; 3524 elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) $updatetarget = true; 3525 3526 $sql = "UPDATE ".MAIN_DB_PREFIX."element_element SET "; 3527 if ($updatesource) 3528 { 3529 $sql .= "fk_source = ".$sourceid; 3530 $sql .= ", sourcetype = '".$this->db->escape($sourcetype)."'"; 3531 $sql .= " WHERE fk_target = ".$this->id; 3532 $sql .= " AND targettype = '".$this->db->escape($this->element)."'"; 3533 } elseif ($updatetarget) 3534 { 3535 $sql .= "fk_target = ".$targetid; 3536 $sql .= ", targettype = '".$this->db->escape($targettype)."'"; 3537 $sql .= " WHERE fk_source = ".$this->id; 3538 $sql .= " AND sourcetype = '".$this->db->escape($this->element)."'"; 3539 } 3540 3541 dol_syslog(get_class($this)."::updateObjectLinked", LOG_DEBUG); 3542 if ($this->db->query($sql)) 3543 { 3544 return 1; 3545 } else { 3546 $this->error = $this->db->lasterror(); 3547 return -1; 3548 } 3549 } 3550 3551 /** 3552 * Delete all links between an object $this 3553 * 3554 * @param int $sourceid Object source id 3555 * @param string $sourcetype Object source type 3556 * @param int $targetid Object target id 3557 * @param string $targettype Object target type 3558 * @param int $rowid Row id of line to delete. If defined, other parameters are not used. 3559 * @return int >0 if OK, <0 if KO 3560 * @see add_object_linked(), updateObjectLinked(), fetchObjectLinked() 3561 */ 3562 public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = '') 3563 { 3564 $deletesource = false; 3565 $deletetarget = false; 3566 3567 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) $deletesource = true; 3568 elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) $deletetarget = true; 3569 3570 $sourceid = (!empty($sourceid) ? $sourceid : $this->id); 3571 $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element); 3572 $targetid = (!empty($targetid) ? $targetid : $this->id); 3573 $targettype = (!empty($targettype) ? $targettype : $this->element); 3574 3575 $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_element"; 3576 $sql .= " WHERE"; 3577 if ($rowid > 0) 3578 { 3579 $sql .= " rowid = ".$rowid; 3580 } else { 3581 if ($deletesource) 3582 { 3583 $sql .= " fk_source = ".$sourceid." AND sourcetype = '".$this->db->escape($sourcetype)."'"; 3584 $sql .= " AND fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."'"; 3585 } elseif ($deletetarget) 3586 { 3587 $sql .= " fk_target = ".$targetid." AND targettype = '".$this->db->escape($targettype)."'"; 3588 $sql .= " AND fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."'"; 3589 } else { 3590 $sql .= " (fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."')"; 3591 $sql .= " OR"; 3592 $sql .= " (fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."')"; 3593 } 3594 } 3595 3596 dol_syslog(get_class($this)."::deleteObjectLinked", LOG_DEBUG); 3597 if ($this->db->query($sql)) 3598 { 3599 return 1; 3600 } else { 3601 $this->error = $this->db->lasterror(); 3602 $this->errors[] = $this->error; 3603 return -1; 3604 } 3605 } 3606 3607 /** 3608 * Set status of an object 3609 * 3610 * @param int $status Status to set 3611 * @param int $elementId Id of element to force (use this->id by default) 3612 * @param string $elementType Type of element to force (use this->table_element by default) 3613 * @param string $trigkey Trigger key to use for trigger 3614 * @return int <0 if KO, >0 if OK 3615 */ 3616 public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '') 3617 { 3618 global $user, $langs, $conf; 3619 3620 $savElementId = $elementId; // To be used later to know if we were using the method using the id of this or not. 3621 3622 $elementId = (!empty($elementId) ? $elementId : $this->id); 3623 $elementTable = (!empty($elementType) ? $elementType : $this->table_element); 3624 3625 $this->db->begin(); 3626 3627 $fieldstatus = "fk_statut"; 3628 if ($elementTable == 'facture_rec') $fieldstatus = "suspended"; 3629 if ($elementTable == 'mailing') $fieldstatus = "statut"; 3630 if ($elementTable == 'cronjob') $fieldstatus = "status"; 3631 if ($elementTable == 'user') $fieldstatus = "statut"; 3632 if ($elementTable == 'expensereport') $fieldstatus = "fk_statut"; 3633 if ($elementTable == 'commande_fournisseur_dispatch') $fieldstatus = "status"; 3634 if (is_array($this->fields) && array_key_exists('status', $this->fields)) $fieldstatus = 'status'; 3635 3636 $sql = "UPDATE ".MAIN_DB_PREFIX.$elementTable; 3637 $sql .= " SET ".$fieldstatus." = ".((int) $status); 3638 // If status = 1 = validated, update also fk_user_valid 3639 if ($status == 1 && $elementTable == 'expensereport') $sql .= ", fk_user_valid = ".((int) $user->id); 3640 $sql .= " WHERE rowid=".((int) $elementId); 3641 3642 dol_syslog(get_class($this)."::setStatut", LOG_DEBUG); 3643 if ($this->db->query($sql)) 3644 { 3645 $error = 0; 3646 3647 // Try autoset of trigkey 3648 if (empty($trigkey)) 3649 { 3650 if ($this->element == 'supplier_proposal' && $status == 2) $trigkey = 'SUPPLIER_PROPOSAL_SIGN'; // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class 3651 if ($this->element == 'supplier_proposal' && $status == 3) $trigkey = 'SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class 3652 if ($this->element == 'supplier_proposal' && $status == 4) $trigkey = 'SUPPLIER_PROPOSAL_CLOSE'; // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class 3653 if ($this->element == 'fichinter' && $status == 3) $trigkey = 'FICHINTER_CLASSIFY_DONE'; 3654 if ($this->element == 'fichinter' && $status == 2) $trigkey = 'FICHINTER_CLASSIFY_BILLED'; 3655 if ($this->element == 'fichinter' && $status == 1) $trigkey = 'FICHINTER_CLASSIFY_UNBILLED'; 3656 } 3657 3658 if ($trigkey) 3659 { 3660 // Call trigger 3661 $result = $this->call_trigger($trigkey, $user); 3662 if ($result < 0) $error++; 3663 // End call triggers 3664 } 3665 3666 if (!$error) 3667 { 3668 $this->db->commit(); 3669 3670 if (empty($savElementId)) // If the element we update was $this (so $elementId is null) 3671 { 3672 $this->statut = $status; 3673 $this->status = $status; 3674 } 3675 3676 return 1; 3677 } else { 3678 $this->db->rollback(); 3679 dol_syslog(get_class($this)."::setStatut ".$this->error, LOG_ERR); 3680 return -1; 3681 } 3682 } else { 3683 $this->error = $this->db->lasterror(); 3684 $this->db->rollback(); 3685 return -1; 3686 } 3687 } 3688 3689 3690 /** 3691 * Load type of canvas of an object if it exists 3692 * 3693 * @param int $id Record id 3694 * @param string $ref Record ref 3695 * @return int <0 if KO, 0 if nothing done, >0 if OK 3696 */ 3697 public function getCanvas($id = 0, $ref = '') 3698 { 3699 global $conf; 3700 3701 if (empty($id) && empty($ref)) return 0; 3702 if (!empty($conf->global->MAIN_DISABLE_CANVAS)) return 0; // To increase speed. Not enabled by default. 3703 3704 // Clean parameters 3705 $ref = trim($ref); 3706 3707 $sql = "SELECT rowid, canvas"; 3708 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element; 3709 $sql .= " WHERE entity IN (".getEntity($this->element).")"; 3710 if (!empty($id)) $sql .= " AND rowid = ".$id; 3711 if (!empty($ref)) $sql .= " AND ref = '".$this->db->escape($ref)."'"; 3712 3713 $resql = $this->db->query($sql); 3714 if ($resql) 3715 { 3716 $obj = $this->db->fetch_object($resql); 3717 if ($obj) 3718 { 3719 $this->canvas = $obj->canvas; 3720 return 1; 3721 } else return 0; 3722 } else { 3723 dol_print_error($this->db); 3724 return -1; 3725 } 3726 } 3727 3728 3729 /** 3730 * Get special code of a line 3731 * 3732 * @param int $lineid Id of line 3733 * @return int Special code 3734 */ 3735 public function getSpecialCode($lineid) 3736 { 3737 $sql = 'SELECT special_code FROM '.MAIN_DB_PREFIX.$this->table_element_line; 3738 $sql .= ' WHERE rowid = '.$lineid; 3739 $resql = $this->db->query($sql); 3740 if ($resql) 3741 { 3742 $row = $this->db->fetch_row($resql); 3743 return $row[0]; 3744 } 3745 } 3746 3747 /** 3748 * Function to check if an object is used by others. 3749 * Check is done into this->childtables. There is no check into llx_element_element. 3750 * 3751 * @param int $id Force id of object 3752 * @return int <0 if KO, 0 if not used, >0 if already used 3753 */ 3754 public function isObjectUsed($id = 0) 3755 { 3756 global $langs; 3757 3758 if (empty($id)) $id = $this->id; 3759 3760 // Check parameters 3761 if (!isset($this->childtables) || !is_array($this->childtables) || count($this->childtables) == 0) 3762 { 3763 dol_print_error('Called isObjectUsed on a class with property this->childtables not defined'); 3764 return -1; 3765 } 3766 3767 $arraytoscan = $this->childtables; 3768 // For backward compatibility, we check if array is old format array('table1', 'table2', ...) 3769 $tmparray = array_keys($this->childtables); 3770 if (is_numeric($tmparray[0])) 3771 { 3772 $arraytoscan = array_flip($this->childtables); 3773 } 3774 3775 // Test if child exists 3776 $haschild = 0; 3777 foreach ($arraytoscan as $table => $elementname) 3778 { 3779 //print $id.'-'.$table.'-'.$elementname.'<br>'; 3780 // Check if third party can be deleted 3781 $sql = "SELECT COUNT(*) as nb from ".MAIN_DB_PREFIX.$table; 3782 $sql .= " WHERE ".$this->fk_element." = ".$id; 3783 $resql = $this->db->query($sql); 3784 if ($resql) 3785 { 3786 $obj = $this->db->fetch_object($resql); 3787 if ($obj->nb > 0) 3788 { 3789 $langs->load("errors"); 3790 //print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild; 3791 $haschild += $obj->nb; 3792 if (is_numeric($elementname)) // old usage 3793 { 3794 $this->errors[] = $langs->trans("ErrorRecordHasAtLeastOneChildOfType", $table); 3795 } else // new usage: $elementname=Translation key 3796 { 3797 $this->errors[] = $langs->trans("ErrorRecordHasAtLeastOneChildOfType", $langs->transnoentitiesnoconv($elementname)); 3798 } 3799 break; // We found at least one, we stop here 3800 } 3801 } else { 3802 $this->errors[] = $this->db->lasterror(); 3803 return -1; 3804 } 3805 } 3806 if ($haschild > 0) 3807 { 3808 $this->errors[] = "ErrorRecordHasChildren"; 3809 return $haschild; 3810 } else return 0; 3811 } 3812 3813 /** 3814 * Function to say how many lines object contains 3815 * 3816 * @param int $predefined -1=All, 0=Count free product/service only, 1=Count predefined product/service only, 2=Count predefined product, 3=Count predefined service 3817 * @return int <0 if KO, 0 if no predefined products, nb of lines with predefined products if found 3818 */ 3819 public function hasProductsOrServices($predefined = -1) 3820 { 3821 $nb = 0; 3822 3823 foreach ($this->lines as $key => $val) 3824 { 3825 $qualified = 0; 3826 if ($predefined == -1) $qualified = 1; 3827 if ($predefined == 1 && $val->fk_product > 0) $qualified = 1; 3828 if ($predefined == 0 && $val->fk_product <= 0) $qualified = 1; 3829 if ($predefined == 2 && $val->fk_product > 0 && $val->product_type == 0) $qualified = 1; 3830 if ($predefined == 3 && $val->fk_product > 0 && $val->product_type == 1) $qualified = 1; 3831 if ($qualified) $nb++; 3832 } 3833 dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies'); 3834 return $nb; 3835 } 3836 3837 /** 3838 * Function that returns the total amount HT of discounts applied for all lines. 3839 * 3840 * @return float 3841 */ 3842 public function getTotalDiscount() 3843 { 3844 $total_discount = 0.00; 3845 3846 $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht"; 3847 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element."det"; 3848 $sql .= " WHERE ".$this->fk_element." = ".$this->id; 3849 3850 dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG); 3851 $resql = $this->db->query($sql); 3852 if ($resql) 3853 { 3854 $num = $this->db->num_rows($resql); 3855 $i = 0; 3856 while ($i < $num) 3857 { 3858 $obj = $this->db->fetch_object($resql); 3859 3860 $pu_ht = $obj->pu_ht; 3861 $qty = $obj->qty; 3862 $total_ht = $obj->total_ht; 3863 3864 $total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT')); 3865 $total_discount += $total_discount_line; 3866 3867 $i++; 3868 } 3869 } 3870 3871 //print $total_discount; exit; 3872 return price2num($total_discount); 3873 } 3874 3875 3876 /** 3877 * Return into unit=0, the calculated total of weight and volume of all lines * qty 3878 * Calculate by adding weight and volume of each product line, so properties ->volume/volume_units/weight/weight_units must be loaded on line. 3879 * 3880 * @return array array('weight'=>...,'volume'=>...) 3881 */ 3882 public function getTotalWeightVolume() 3883 { 3884 $totalWeight = 0; 3885 $totalVolume = 0; 3886 // defined for shipment only 3887 $totalOrdered = ''; 3888 // defined for shipment only 3889 $totalToShip = ''; 3890 3891 foreach ($this->lines as $line) 3892 { 3893 if (isset($line->qty_asked)) 3894 { 3895 if (empty($totalOrdered)) $totalOrdered = 0; // Avoid warning because $totalOrdered is '' 3896 $totalOrdered += $line->qty_asked; // defined for shipment only 3897 } 3898 if (isset($line->qty_shipped)) 3899 { 3900 if (empty($totalToShip)) $totalToShip = 0; // Avoid warning because $totalToShip is '' 3901 $totalToShip += $line->qty_shipped; // defined for shipment only 3902 } elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty)) 3903 { 3904 if (empty($totalToShip)) $totalToShip = 0; 3905 $totalToShip += $line->qty; // defined for reception only 3906 } 3907 3908 // Define qty, weight, volume, weight_units, volume_units 3909 if ($this->element == 'shipping') { 3910 // for shipments 3911 $qty = $line->qty_shipped ? $line->qty_shipped : 0; 3912 } else { 3913 $qty = $line->qty ? $line->qty : 0; 3914 } 3915 3916 $weight = $line->weight ? $line->weight : 0; 3917 ($weight == 0 && !empty($line->product->weight)) ? $weight = $line->product->weight : 0; 3918 $volume = $line->volume ? $line->volume : 0; 3919 ($volume == 0 && !empty($line->product->volume)) ? $volume = $line->product->volume : 0; 3920 3921 $weight_units = $line->weight_units; 3922 ($weight_units == 0 && !empty($line->product->weight_units)) ? $weight_units = $line->product->weight_units : 0; 3923 $volume_units = $line->volume_units; 3924 ($volume_units == 0 && !empty($line->product->volume_units)) ? $volume_units = $line->product->volume_units : 0; 3925 3926 $weightUnit = 0; 3927 $volumeUnit = 0; 3928 if (!empty($weight_units)) $weightUnit = $weight_units; 3929 if (!empty($volume_units)) $volumeUnit = $volume_units; 3930 3931 if (empty($totalWeight)) $totalWeight = 0; // Avoid warning because $totalWeight is '' 3932 if (empty($totalVolume)) $totalVolume = 0; // Avoid warning because $totalVolume is '' 3933 3934 //var_dump($line->volume_units); 3935 if ($weight_units < 50) // < 50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch) 3936 { 3937 $trueWeightUnit = pow(10, $weightUnit); 3938 $totalWeight += $weight * $qty * $trueWeightUnit; 3939 } else { 3940 if ($weight_units == 99) { 3941 // conversion 1 Pound = 0.45359237 KG 3942 $trueWeightUnit = 0.45359237; 3943 $totalWeight += $weight * $qty * $trueWeightUnit; 3944 } elseif ($weight_units == 98) { 3945 // conversion 1 Ounce = 0.0283495 KG 3946 $trueWeightUnit = 0.0283495; 3947 $totalWeight += $weight * $qty * $trueWeightUnit; 3948 } else { 3949 $totalWeight += $weight * $qty; // This may be wrong if we mix different units 3950 } 3951 } 3952 if ($volume_units < 50) // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch) 3953 { 3954 //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit; 3955 $trueVolumeUnit = pow(10, $volumeUnit); 3956 //print $line->volume; 3957 $totalVolume += $volume * $qty * $trueVolumeUnit; 3958 } else { 3959 $totalVolume += $volume * $qty; // This may be wrong if we mix different units 3960 } 3961 } 3962 3963 return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip); 3964 } 3965 3966 3967 /** 3968 * Set extra parameters 3969 * 3970 * @return int <0 if KO, >0 if OK 3971 */ 3972 public function setExtraParameters() 3973 { 3974 $this->db->begin(); 3975 3976 $extraparams = (!empty($this->extraparams) ? json_encode($this->extraparams) : null); 3977 3978 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element; 3979 $sql .= " SET extraparams = ".(!empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null"); 3980 $sql .= " WHERE rowid = ".$this->id; 3981 3982 dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG); 3983 $resql = $this->db->query($sql); 3984 if (!$resql) 3985 { 3986 $this->error = $this->db->lasterror(); 3987 $this->db->rollback(); 3988 return -1; 3989 } else { 3990 $this->db->commit(); 3991 return 1; 3992 } 3993 } 3994 3995 3996 // -------------------- 3997 // TODO: All functions here must be redesigned and moved as they are not business functions but output functions 3998 // -------------------- 3999 4000 /* This is to show add lines */ 4001 4002 /** 4003 * Show add free and predefined products/services form 4004 * 4005 * @param int $dateSelector 1=Show also date range input fields 4006 * @param Societe $seller Object thirdparty who sell 4007 * @param Societe $buyer Object thirdparty who buy 4008 * @param string $defaulttpldir Directory where to find the template 4009 * @return void 4010 */ 4011 public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl') 4012 { 4013 global $conf, $user, $langs, $object, $hookmanager, $extrafields; 4014 global $form; 4015 4016 // Line extrafield 4017 if (!is_object($extrafields)) 4018 { 4019 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; 4020 $extrafields = new ExtraFields($this->db); 4021 } 4022 $extrafields->fetch_name_optionals_label($this->table_element_line); 4023 4024 // Output template part (modules that overwrite templates must declare this into descriptor) 4025 // Use global variables + $dateSelector + $seller and $buyer 4026 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'. 4027 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir)); 4028 foreach ($dirtpls as $module => $reldir) 4029 { 4030 if (!empty($module)) 4031 { 4032 $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php'); 4033 } else { 4034 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php'; 4035 } 4036 4037 if (empty($conf->file->strict_mode)) { 4038 $res = @include $tpl; 4039 } else { 4040 $res = include $tpl; // for debug 4041 } 4042 if ($res) break; 4043 } 4044 } 4045 4046 4047 4048 /* This is to show array of line of details */ 4049 4050 4051 /** 4052 * Return HTML table for object lines 4053 * TODO Move this into an output class file (htmlline.class.php) 4054 * If lines are into a template, title must also be into a template 4055 * But for the moment we don't know if it's possible as we keep a method available on overloaded objects. 4056 * 4057 * @param string $action Action code 4058 * @param string $seller Object of seller third party 4059 * @param string $buyer Object of buyer third party 4060 * @param int $selected Object line selected 4061 * @param int $dateSelector 1=Show also date range input fields 4062 * @param string $defaulttpldir Directory where to find the template 4063 * @return void 4064 */ 4065 public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl') 4066 { 4067 global $conf, $hookmanager, $langs, $user, $form, $extrafields, $object; 4068 // TODO We should not use global var for this 4069 global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax; 4070 4071 // Define usemargins 4072 $usemargins = 0; 4073 if (!empty($conf->margin->enabled) && !empty($this->element) && in_array($this->element, array('facture', 'facturerec', 'propal', 'commande'))) $usemargins = 1; 4074 4075 $num = count($this->lines); 4076 4077 // Line extrafield 4078 if (!is_object($extrafields)) 4079 { 4080 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; 4081 $extrafields = new ExtraFields($this->db); 4082 } 4083 $extrafields->fetch_name_optionals_label($this->table_element_line); 4084 4085 $parameters = array('num'=>$num, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$this->table_element_line); 4086 $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks 4087 if (empty($reshook)) 4088 { 4089 // Output template part (modules that overwrite templates must declare this into descriptor) 4090 // Use global variables + $dateSelector + $seller and $buyer 4091 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook. 4092 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir)); 4093 foreach ($dirtpls as $module => $reldir) 4094 { 4095 if (!empty($module)) 4096 { 4097 $tpl = dol_buildpath($reldir.'/objectline_title.tpl.php'); 4098 } else { 4099 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php'; 4100 } 4101 if (empty($conf->file->strict_mode)) { 4102 $res = @include $tpl; 4103 } else { 4104 $res = include $tpl; // for debug 4105 } 4106 if ($res) break; 4107 } 4108 } 4109 4110 $i = 0; 4111 4112 print "<!-- begin printObjectLines() --><tbody>\n"; 4113 foreach ($this->lines as $line) 4114 { 4115 //Line extrafield 4116 $line->fetch_optionals(); 4117 4118 //if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line))) 4119 if (is_object($hookmanager)) // Old code is commented on preceding line. 4120 { 4121 if (empty($line->fk_parent_line)) 4122 { 4123 $parameters = array('line'=>$line, 'num'=>$num, 'i'=>$i, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$line->table_element); 4124 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks 4125 } else { 4126 $parameters = array('line'=>$line, 'num'=>$num, 'i'=>$i, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$line->table_element, 'fk_parent_line'=>$line->fk_parent_line); 4127 $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks 4128 } 4129 } 4130 if (empty($reshook)) 4131 { 4132 $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafields, $defaulttpldir); 4133 } 4134 4135 $i++; 4136 } 4137 print "</tbody><!-- end printObjectLines() -->\n"; 4138 } 4139 4140 /** 4141 * Return HTML content of a detail line 4142 * TODO Move this into an output class file (htmlline.class.php) 4143 * 4144 * @param string $action GET/POST action 4145 * @param CommonObjectLine $line Selected object line to output 4146 * @param string $var Is it a an odd line (true) 4147 * @param int $num Number of line (0) 4148 * @param int $i I 4149 * @param int $dateSelector 1=Show also date range input fields 4150 * @param string $seller Object of seller third party 4151 * @param string $buyer Object of buyer third party 4152 * @param int $selected Object line selected 4153 * @param Extrafields $extrafields Object of extrafields 4154 * @param string $defaulttpldir Directory where to find the template (deprecated) 4155 * @return void 4156 */ 4157 public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/core/tpl') 4158 { 4159 global $conf, $langs, $user, $object, $hookmanager; 4160 global $form; 4161 global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this ! 4162 4163 $object_rights = $this->getRights(); 4164 4165 $element = $this->element; 4166 4167 $text = ''; $description = ''; 4168 4169 // Line in view mode 4170 if ($action != 'editline' || $selected != $line->id) 4171 { 4172 // Product 4173 if ($line->fk_product > 0) 4174 { 4175 $product_static = new Product($this->db); 4176 $product_static->fetch($line->fk_product); 4177 4178 $product_static->ref = $line->ref; //can change ref in hook 4179 $product_static->label = $line->label; //can change label in hook 4180 4181 $text = $product_static->getNomUrl(1); 4182 4183 // Define output language and label 4184 if (!empty($conf->global->MAIN_MULTILANGS)) 4185 { 4186 if (property_exists($this, 'socid') && !is_object($this->thirdparty)) 4187 { 4188 dol_print_error('', 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before'); 4189 return; 4190 } 4191 4192 $prod = new Product($this->db); 4193 $prod->fetch($line->fk_product); 4194 4195 $outputlangs = $langs; 4196 $newlang = ''; 4197 if (empty($newlang) && GETPOST('lang_id', 'aZ09')) $newlang = GETPOST('lang_id', 'aZ09'); 4198 if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang) && is_object($this->thirdparty)) $newlang = $this->thirdparty->default_lang; // To use language of customer 4199 if (!empty($newlang)) 4200 { 4201 $outputlangs = new Translate("", $conf); 4202 $outputlangs->setDefaultLang($newlang); 4203 } 4204 4205 $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label; 4206 } else { 4207 $label = $line->product_label; 4208 } 4209 4210 $text .= ' - '.(!empty($line->label) ? $line->label : $label); 4211 $description .= (!empty($conf->global->PRODUIT_DESC_IN_FORM) ? '' : dol_htmlentitiesbr($line->description)); // Description is what to show on popup. We shown nothing if already into desc. 4212 } 4213 4214 $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU'); 4215 4216 // Output template part (modules that overwrite templates must declare this into descriptor) 4217 // Use global variables + $dateSelector + $seller and $buyer 4218 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine. 4219 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir)); 4220 foreach ($dirtpls as $module => $reldir) 4221 { 4222 if (!empty($module)) 4223 { 4224 $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php'); 4225 } else { 4226 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php'; 4227 } 4228 4229 if (empty($conf->file->strict_mode)) { 4230 $res = @include $tpl; 4231 } else { 4232 $res = include $tpl; // for debug 4233 } 4234 if ($res) break; 4235 } 4236 } 4237 4238 // Line in update mode 4239 if ($this->statut == 0 && $action == 'editline' && $selected == $line->id) 4240 { 4241 $label = (!empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : '')); 4242 4243 $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU'); 4244 4245 // Output template part (modules that overwrite templates must declare this into descriptor) 4246 // Use global variables + $dateSelector + $seller and $buyer 4247 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine. 4248 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir)); 4249 foreach ($dirtpls as $module => $reldir) 4250 { 4251 if (!empty($module)) 4252 { 4253 $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php'); 4254 } else { 4255 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php'; 4256 } 4257 4258 if (empty($conf->file->strict_mode)) { 4259 $res = @include $tpl; 4260 } else { 4261 $res = include $tpl; // for debug 4262 } 4263 if ($res) break; 4264 } 4265 } 4266 } 4267 4268 4269 /* This is to show array of line of details of source object */ 4270 4271 4272 /** 4273 * Return HTML table table of source object lines 4274 * TODO Move this and previous function into output html class file (htmlline.class.php). 4275 * If lines are into a template, title must also be into a template 4276 * But for the moment we don't know if it's possible, so we keep the method available on overloaded objects. 4277 * 4278 * @param string $restrictlist ''=All lines, 'services'=Restrict to services only 4279 * @param array $selectedLines Array of lines id for selected lines 4280 * @return void 4281 */ 4282 public function printOriginLinesList($restrictlist = '', $selectedLines = array()) 4283 { 4284 global $langs, $hookmanager, $conf, $form; 4285 4286 print '<tr class="liste_titre">'; 4287 print '<td>'.$langs->trans('Ref').'</td>'; 4288 print '<td>'.$langs->trans('Description').'</td>'; 4289 print '<td class="right">'.$langs->trans('VATRate').'</td>'; 4290 print '<td class="right">'.$langs->trans('PriceUHT').'</td>'; 4291 if (!empty($conf->multicurrency->enabled)) print '<td class="right">'.$langs->trans('PriceUHTCurrency').'</td>'; 4292 print '<td class="right">'.$langs->trans('Qty').'</td>'; 4293 if (!empty($conf->global->PRODUCT_USE_UNITS)) 4294 { 4295 print '<td class="left">'.$langs->trans('Unit').'</td>'; 4296 } 4297 print '<td class="right">'.$langs->trans('ReductionShort').'</td>'; 4298 print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>'; 4299 print '</tr>'; 4300 $i = 0; 4301 4302 if (!empty($this->lines)) 4303 { 4304 foreach ($this->lines as $line) 4305 { 4306 if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line))) 4307 { 4308 if (empty($line->fk_parent_line)) 4309 { 4310 $parameters = array('line'=>$line, 'i'=>$i); 4311 $action = ''; 4312 $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks 4313 } 4314 } else { 4315 $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines); 4316 } 4317 4318 $i++; 4319 } 4320 } 4321 } 4322 4323 /** 4324 * Return HTML with a line of table array of source object lines 4325 * TODO Move this and previous function into output html class file (htmlline.class.php). 4326 * If lines are into a template, title must also be into a template 4327 * But for the moment we don't know if it's possible as we keep a method available on overloaded objects. 4328 * 4329 * @param CommonObjectLine $line Line 4330 * @param string $var Var 4331 * @param string $restrictlist ''=All lines, 'services'=Restrict to services only (strike line if not) 4332 * @param string $defaulttpldir Directory where to find the template 4333 * @param array $selectedLines Array of lines id for selected lines 4334 * @return void 4335 */ 4336 public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array()) 4337 { 4338 global $langs, $conf; 4339 4340 //var_dump($line); 4341 if (!empty($line->date_start)) 4342 { 4343 $date_start = $line->date_start; 4344 } else { 4345 $date_start = $line->date_debut_prevue; 4346 if ($line->date_debut_reel) $date_start = $line->date_debut_reel; 4347 } 4348 if (!empty($line->date_end)) 4349 { 4350 $date_end = $line->date_end; 4351 } else { 4352 $date_end = $line->date_fin_prevue; 4353 if ($line->date_fin_reel) $date_end = $line->date_fin_reel; 4354 } 4355 4356 $this->tpl['id'] = $line->id; 4357 4358 $this->tpl['label'] = ''; 4359 if (!empty($line->fk_parent_line)) $this->tpl['label'] .= img_picto('', 'rightarrow'); 4360 4361 if (($line->info_bits & 2) == 2) // TODO Not sure this is used for source object 4362 { 4363 $discount = new DiscountAbsolute($this->db); 4364 $discount->fk_soc = $this->socid; 4365 $this->tpl['label'] .= $discount->getNomUrl(0, 'discount'); 4366 } elseif (!empty($line->fk_product)) 4367 { 4368 $productstatic = new Product($this->db); 4369 $productstatic->id = $line->fk_product; 4370 $productstatic->ref = $line->ref; 4371 $productstatic->type = $line->fk_product_type; 4372 if (empty($productstatic->ref)) { 4373 $line->fetch_product(); 4374 $productstatic = $line->product; 4375 } 4376 4377 $this->tpl['label'] .= $productstatic->getNomUrl(1); 4378 $this->tpl['label'] .= ' - '.(!empty($line->label) ? $line->label : $line->product_label); 4379 // Dates 4380 if ($line->product_type == 1 && ($date_start || $date_end)) 4381 { 4382 $this->tpl['label'] .= get_date_range($date_start, $date_end); 4383 } 4384 } else { 4385 $this->tpl['label'] .= ($line->product_type == -1 ? ' ' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product'))); 4386 if (!empty($line->desc)) { 4387 $this->tpl['label'] .= $line->desc; 4388 } else { 4389 $this->tpl['label'] .= ($line->label ? ' '.$line->label : ''); 4390 } 4391 4392 // Dates 4393 if ($line->product_type == 1 && ($date_start || $date_end)) 4394 { 4395 $this->tpl['label'] .= get_date_range($date_start, $date_end); 4396 } 4397 } 4398 4399 if (!empty($line->desc)) 4400 { 4401 if ($line->desc == '(CREDIT_NOTE)') // TODO Not sure this is used for source object 4402 { 4403 $discount = new DiscountAbsolute($this->db); 4404 $discount->fetch($line->fk_remise_except); 4405 $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0)); 4406 } elseif ($line->desc == '(DEPOSIT)') // TODO Not sure this is used for source object 4407 { 4408 $discount = new DiscountAbsolute($this->db); 4409 $discount->fetch($line->fk_remise_except); 4410 $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0)); 4411 } elseif ($line->desc == '(EXCESS RECEIVED)') 4412 { 4413 $discount = new DiscountAbsolute($this->db); 4414 $discount->fetch($line->fk_remise_except); 4415 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0)); 4416 } elseif ($line->desc == '(EXCESS PAID)') 4417 { 4418 $discount = new DiscountAbsolute($this->db); 4419 $discount->fetch($line->fk_remise_except); 4420 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0)); 4421 } else { 4422 $this->tpl['description'] = dol_trunc($line->desc, 60); 4423 } 4424 } else { 4425 $this->tpl['description'] = ' '; 4426 } 4427 4428 // VAT Rate 4429 $this->tpl['vat_rate'] = vatrate($line->tva_tx, true); 4430 $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : ''; 4431 if (!empty($line->vat_src_code) && !preg_match('/\(/', $this->tpl['vat_rate'])) $this->tpl['vat_rate'] .= ' ('.$line->vat_src_code.')'; 4432 4433 $this->tpl['price'] = price($line->subprice); 4434 $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice); 4435 $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : ' '; 4436 if (!empty($conf->global->PRODUCT_USE_UNITS)) $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long')); 4437 $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : ' '; 4438 4439 // Is the line strike or not 4440 $this->tpl['strike'] = 0; 4441 if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) $this->tpl['strike'] = 1; 4442 4443 // Output template part (modules that overwrite templates must declare this into descriptor) 4444 // Use global variables + $dateSelector + $seller and $buyer 4445 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir)); 4446 foreach ($dirtpls as $module => $reldir) 4447 { 4448 if (!empty($module)) 4449 { 4450 $tpl = dol_buildpath($reldir.'/originproductline.tpl.php'); 4451 } else { 4452 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php'; 4453 } 4454 4455 if (empty($conf->file->strict_mode)) { 4456 $res = @include $tpl; 4457 } else { 4458 $res = include $tpl; // for debug 4459 } 4460 if ($res) break; 4461 } 4462 } 4463 4464 4465 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 4466 /** 4467 * Add resources to the current object : add entry into llx_element_resources 4468 * Need $this->element & $this->id 4469 * 4470 * @param int $resource_id Resource id 4471 * @param string $resource_type 'resource' 4472 * @param int $busy Busy or not 4473 * @param int $mandatory Mandatory or not 4474 * @return int <=0 if KO, >0 if OK 4475 */ 4476 public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0) 4477 { 4478 // phpcs:enable 4479 $this->db->begin(); 4480 4481 $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_resources ("; 4482 $sql .= "resource_id"; 4483 $sql .= ", resource_type"; 4484 $sql .= ", element_id"; 4485 $sql .= ", element_type"; 4486 $sql .= ", busy"; 4487 $sql .= ", mandatory"; 4488 $sql .= ") VALUES ("; 4489 $sql .= $resource_id; 4490 $sql .= ", '".$this->db->escape($resource_type)."'"; 4491 $sql .= ", '".$this->db->escape($this->id)."'"; 4492 $sql .= ", '".$this->db->escape($this->element)."'"; 4493 $sql .= ", '".$this->db->escape($busy)."'"; 4494 $sql .= ", '".$this->db->escape($mandatory)."'"; 4495 $sql .= ")"; 4496 4497 dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG); 4498 if ($this->db->query($sql)) 4499 { 4500 $this->db->commit(); 4501 return 1; 4502 } else { 4503 $this->error = $this->db->lasterror(); 4504 $this->db->rollback(); 4505 return 0; 4506 } 4507 } 4508 4509 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 4510 /** 4511 * Delete a link to resource line 4512 * 4513 * @param int $rowid Id of resource line to delete 4514 * @param int $element element name (for trigger) TODO: use $this->element into commonobject class 4515 * @param int $notrigger Disable all triggers 4516 * @return int >0 if OK, <0 if KO 4517 */ 4518 public function delete_resource($rowid, $element, $notrigger = 0) 4519 { 4520 // phpcs:enable 4521 global $user; 4522 4523 $this->db->begin(); 4524 4525 $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_resources"; 4526 $sql .= " WHERE rowid=".$rowid; 4527 4528 dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG); 4529 4530 $resql = $this->db->query($sql); 4531 if (!$resql) 4532 { 4533 $this->error = $this->db->lasterror(); 4534 $this->db->rollback(); 4535 return -1; 4536 } else { 4537 if (!$notrigger) 4538 { 4539 $result = $this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user); 4540 if ($result < 0) { $this->db->rollback(); return -1; } 4541 } 4542 $this->db->commit(); 4543 return 1; 4544 } 4545 } 4546 4547 4548 /** 4549 * Overwrite magic function to solve problem of cloning object that are kept as references 4550 * 4551 * @return void 4552 */ 4553 public function __clone() 4554 { 4555 // Force a copy of this->lines, otherwise it will point to same object. 4556 if (isset($this->lines) && is_array($this->lines)) 4557 { 4558 $nboflines = count($this->lines); 4559 for ($i = 0; $i < $nboflines; $i++) 4560 { 4561 $this->lines[$i] = clone $this->lines[$i]; 4562 } 4563 } 4564 } 4565 4566 /** 4567 * Common function for all objects extending CommonObject for generating documents 4568 * 4569 * @param string $modelspath Relative folder where generators are placed 4570 * @param string $modele Generator to use. Caller must set it to obj->model_pdf or GETPOST('model_pdf','alpha') for example. 4571 * @param Translate $outputlangs Output language to use 4572 * @param int $hidedetails 1 to hide details. 0 by default 4573 * @param int $hidedesc 1 to hide product description. 0 by default 4574 * @param int $hideref 1 to hide product reference. 0 by default 4575 * @param null|array $moreparams Array to provide more information 4576 * @return int >0 if OK, <0 if KO 4577 * @see addFileIntoDatabaseIndex() 4578 */ 4579 protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null) 4580 { 4581 global $conf, $langs, $user, $hookmanager, $action; 4582 4583 $srctemplatepath = ''; 4584 4585 $parameters = array('modelspath'=>$modelspath, 'modele'=>$modele, 'outputlangs'=>$outputlangs, 'hidedetails'=>$hidedetails, 'hidedesc'=>$hidedesc, 'hideref'=>$hideref, 'moreparams'=>$moreparams); 4586 $reshook = $hookmanager->executeHooks('commonGenerateDocument', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks 4587 4588 if (empty($reshook)) 4589 { 4590 dol_syslog("commonGenerateDocument modele=".$modele." outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null')); 4591 4592 if (empty($modele)) { 4593 $this->error = 'BadValueForParameterModele'; 4594 return -1; 4595 } 4596 4597 // Increase limit for PDF build 4598 $err = error_reporting(); 4599 error_reporting(0); 4600 @set_time_limit(120); 4601 error_reporting($err); 4602 4603 // If selected model is a filename template (then $modele="modelname" or "modelname:filename") 4604 $tmp = explode(':', $modele, 2); 4605 if (!empty($tmp[1])) 4606 { 4607 $modele = $tmp[0]; 4608 $srctemplatepath = $tmp[1]; 4609 } 4610 4611 // Search template files 4612 $file = ''; 4613 $classname = ''; 4614 $filefound = ''; 4615 $dirmodels = array('/'); 4616 if (is_array($conf->modules_parts['models'])) $dirmodels = array_merge($dirmodels, $conf->modules_parts['models']); 4617 foreach ($dirmodels as $reldir) 4618 { 4619 foreach (array('doc', 'pdf') as $prefix) 4620 { 4621 if (in_array(get_class($this), array('Adherent'))) { 4622 // Member module use prefix_modele.class.php 4623 $file = $prefix."_".$modele.".class.php"; 4624 } else { 4625 // Other module use prefix_modele.modules.php 4626 $file = $prefix."_".$modele.".modules.php"; 4627 } 4628 4629 // On verifie l'emplacement du modele 4630 $file = dol_buildpath($reldir.$modelspath.$file, 0); 4631 if (file_exists($file)) { 4632 $filefound = $file; 4633 $classname = $prefix.'_'.$modele; 4634 break; 4635 } 4636 } 4637 if ($filefound) break; 4638 } 4639 4640 // If generator was found 4641 if ($filefound) 4642 { 4643 global $db; // Required to solve a conception default making an include of code using $db instead of $this->db just after. 4644 4645 require_once $file; 4646 4647 $obj = new $classname($this->db); 4648 4649 // If generator is ODT, we must have srctemplatepath defined, if not we set it. 4650 if ($obj->type == 'odt' && empty($srctemplatepath)) 4651 { 4652 $varfortemplatedir = $obj->scandir; 4653 if ($varfortemplatedir && !empty($conf->global->$varfortemplatedir)) 4654 { 4655 $dirtoscan = $conf->global->$varfortemplatedir; 4656 4657 $listoffiles = array(); 4658 4659 // Now we add first model found in directories scanned 4660 $listofdir = explode(',', $dirtoscan); 4661 foreach ($listofdir as $key => $tmpdir) 4662 { 4663 $tmpdir = trim($tmpdir); 4664 $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir); 4665 if (!$tmpdir) { unset($listofdir[$key]); continue; } 4666 if (is_dir($tmpdir)) 4667 { 4668 $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0); 4669 if (count($tmpfiles)) $listoffiles = array_merge($listoffiles, $tmpfiles); 4670 } 4671 } 4672 4673 if (count($listoffiles)) 4674 { 4675 foreach ($listoffiles as $record) 4676 { 4677 $srctemplatepath = $record['fullname']; 4678 break; 4679 } 4680 } 4681 } 4682 4683 if (empty($srctemplatepath)) 4684 { 4685 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined'; 4686 return -1; 4687 } 4688 } 4689 4690 if ($obj->type == 'odt' && !empty($srctemplatepath)) 4691 { 4692 if (!dol_is_file($srctemplatepath)) 4693 { 4694 dol_syslog("Failed to locate template file ".$srctemplatepath, LOG_WARNING); 4695 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound'; 4696 return -1; 4697 } 4698 } 4699 4700 // We save charset_output to restore it because write_file can change it if needed for 4701 // output format that does not support UTF8. 4702 $sav_charset_output = $outputlangs->charset_output; 4703 4704 if (in_array(get_class($this), array('Adherent'))) 4705 { 4706 $arrayofrecords = array(); // The write_file of templates of adherent class need this var 4707 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, $moreparams); 4708 } else { 4709 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams); 4710 } 4711 // After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index. 4712 4713 if ($resultwritefile > 0) 4714 { 4715 $outputlangs->charset_output = $sav_charset_output; 4716 4717 // We delete old preview 4718 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 4719 dol_delete_preview($this); 4720 4721 // Index file in database 4722 if (!empty($obj->result['fullpath'])) 4723 { 4724 $destfull = $obj->result['fullpath']; 4725 $upload_dir = dirname($destfull); 4726 $destfile = basename($destfull); 4727 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir); 4728 4729 if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) // If not a tmp dir 4730 { 4731 $filename = basename($destfile); 4732 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir); 4733 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir); 4734 4735 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php'; 4736 $ecmfile = new EcmFiles($this->db); 4737 $result = $ecmfile->fetch(0, '', ($rel_dir ? $rel_dir.'/' : '').$filename); 4738 4739 // Set the public "share" key 4740 $setsharekey = false; 4741 if ($this->element == 'propal') 4742 { 4743 $useonlinesignature = $conf->global->MAIN_FEATURES_LEVEL; // Replace this with 1 when feature to make online signature is ok 4744 if ($useonlinesignature) $setsharekey = true; 4745 if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey = true; 4746 } 4747 if ($this->element == 'commande' && !empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD)) { 4748 $setsharekey = true; 4749 } 4750 if ($this->element == 'facture' && !empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD)) { 4751 $setsharekey = true; 4752 } 4753 if ($this->element == 'bank_account' && !empty($conf->global->BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD)) { 4754 $setsharekey = true; 4755 } 4756 4757 if ($setsharekey) { 4758 if (empty($ecmfile->share)) // Because object not found or share not set yet 4759 { 4760 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php'; 4761 $ecmfile->share = getRandomPassword(true); 4762 } 4763 } 4764 4765 if ($result > 0) 4766 { 4767 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content 4768 $ecmfile->fullpath_orig = ''; 4769 $ecmfile->gen_or_uploaded = 'generated'; 4770 $ecmfile->description = ''; // indexed content 4771 $ecmfile->keyword = ''; // keyword content 4772 $result = $ecmfile->update($user); 4773 if ($result < 0) { 4774 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings'); 4775 } 4776 } else { 4777 $ecmfile->entity = $conf->entity; 4778 $ecmfile->filepath = $rel_dir; 4779 $ecmfile->filename = $filename; 4780 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content 4781 $ecmfile->fullpath_orig = ''; 4782 $ecmfile->gen_or_uploaded = 'generated'; 4783 $ecmfile->description = ''; // indexed content 4784 $ecmfile->keyword = ''; // keyword content 4785 $ecmfile->src_object_type = $this->table_element; 4786 $ecmfile->src_object_id = $this->id; 4787 4788 $result = $ecmfile->create($user); 4789 if ($result < 0) { 4790 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings'); 4791 } 4792 } 4793 4794 /*$this->result['fullname']=$destfull; 4795 $this->result['filepath']=$ecmfile->filepath; 4796 $this->result['filename']=$ecmfile->filename;*/ 4797 //var_dump($obj->update_main_doc_field);exit; 4798 4799 // Update the last_main_doc field into main object (if documenent generator has property ->update_main_doc_field set) 4800 $update_main_doc_field = 0; 4801 if (!empty($obj->update_main_doc_field)) $update_main_doc_field = 1; 4802 if ($update_main_doc_field && !empty($this->table_element)) 4803 { 4804 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath.'/'.$ecmfile->filename)."'"; 4805 $sql .= ' WHERE rowid = '.$this->id; 4806 4807 $resql = $this->db->query($sql); 4808 if (!$resql) { 4809 dol_print_error($this->db); 4810 } else { 4811 $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename; 4812 } 4813 } 4814 } 4815 } else { 4816 dol_syslog('Method ->write_file was called on object '.get_class($obj).' and return a success but the return array ->result["fullpath"] was not set.', LOG_WARNING); 4817 } 4818 4819 // Success in building document. We build meta file. 4820 dol_meta_create($this); 4821 4822 return 1; 4823 } else { 4824 $outputlangs->charset_output = $sav_charset_output; 4825 dol_print_error($this->db, "Error generating document for ".__CLASS__.". Error: ".$obj->error, $obj->errors); 4826 return -1; 4827 } 4828 } else { 4829 if (!$filefound) { 4830 $this->error = $langs->trans("Error").' Failed to load doc generator with modelpaths='.$modelspath.' - modele='.$modele; 4831 dol_print_error('', $this->error); 4832 } else { 4833 $this->error = $langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists", $filefound); 4834 dol_print_error('', $this->error); 4835 } 4836 return -1; 4837 } 4838 } else return $reshook; 4839 } 4840 4841 /** 4842 * Build thumb 4843 * @todo Move this into files.lib.php 4844 * 4845 * @param string $file Path file in UTF8 to original file to create thumbs from. 4846 * @return void 4847 */ 4848 public function addThumbs($file) 4849 { 4850 global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini, $quality; 4851 4852 require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php'; // This define also $maxwidthsmall, $quality, ... 4853 4854 $file_osencoded = dol_osencode($file); 4855 if (file_exists($file_osencoded)) 4856 { 4857 // Create small thumbs for company (Ratio is near 16/9) 4858 // Used on logon for example 4859 vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality); 4860 4861 // Create mini thumbs for company (Ratio is near 16/9) 4862 // Used on menu or for setup page for example 4863 vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality); 4864 } 4865 } 4866 4867 4868 /* Functions common to commonobject and commonobjectline */ 4869 4870 /* For default values */ 4871 4872 /** 4873 * Return the default value to use for a field when showing the create form of object. 4874 * Return values in this order: 4875 * 1) If parameter is available into POST, we return it first. 4876 * 2) If not but an alternate value was provided as parameter of function, we return it. 4877 * 3) If not but a constant $conf->global->OBJECTELEMENT_FIELDNAME is set, we return it (It is better to use the dedicated table). 4878 * 4) Return value found into database (TODO No yet implemented) 4879 * 4880 * @param string $fieldname Name of field 4881 * @param string $alternatevalue Alternate value to use 4882 * @return string|string[] Default value (can be an array if the GETPOST return an array) 4883 **/ 4884 public function getDefaultCreateValueFor($fieldname, $alternatevalue = null) 4885 { 4886 global $conf, $_POST; 4887 4888 // If param here has been posted, we use this value first. 4889 if (GETPOSTISSET($fieldname)) return GETPOST($fieldname, 'alphanohtml', 3); 4890 4891 if (isset($alternatevalue)) return $alternatevalue; 4892 4893 $newelement = $this->element; 4894 if ($newelement == 'facture') $newelement = 'invoice'; 4895 if ($newelement == 'commande') $newelement = 'order'; 4896 if (empty($newelement)) 4897 { 4898 dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING); 4899 return ''; 4900 } 4901 4902 $keyforfieldname = strtoupper($newelement.'_DEFAULT_'.$fieldname); 4903 //var_dump($keyforfieldname); 4904 if (isset($conf->global->$keyforfieldname)) return $conf->global->$keyforfieldname; 4905 4906 // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname 4907 } 4908 4909 4910 /* For triggers */ 4911 4912 4913 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 4914 /** 4915 * Call trigger based on this instance. 4916 * Some context information may also be provided into array property this->context. 4917 * NB: Error from trigger are stacked in interface->errors 4918 * NB2: If return code of triggers are < 0, action calling trigger should cancel all transaction. 4919 * 4920 * @param string $triggerName trigger's name to execute 4921 * @param User $user Object user 4922 * @return int Result of run_triggers 4923 */ 4924 public function call_trigger($triggerName, $user) 4925 { 4926 // phpcs:enable 4927 global $langs, $conf; 4928 4929 if (!is_object($langs)) { // If lang was not defined, we set it. It is required by run_triggers. 4930 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php'; 4931 $langs = new Translate('', $conf); 4932 } 4933 4934 include_once DOL_DOCUMENT_ROOT.'/core/class/interfaces.class.php'; 4935 $interface = new Interfaces($this->db); 4936 $result = $interface->run_triggers($triggerName, $this, $user, $langs, $conf); 4937 4938 if ($result < 0) 4939 { 4940 if (!empty($this->errors)) 4941 { 4942 $this->errors = array_unique(array_merge($this->errors, $interface->errors)); // We use array_unique because when a trigger call another trigger on same object, this->errors is added twice. 4943 } else { 4944 $this->errors = $interface->errors; 4945 } 4946 } 4947 return $result; 4948 } 4949 4950 4951 /* Functions for data in other language */ 4952 4953 4954 /** 4955 * Function to get alternative languages of a data into $this->array_languages 4956 * This method is NOT called by method fetch of objects but must be called separately. 4957 * 4958 * @return int <0 if error, 0 if no values of alternative languages to find nor found, 1 if a value was found and loaded 4959 * @see fetch_optionnals() 4960 */ 4961 public function fetchValuesForExtraLanguages() 4962 { 4963 // To avoid SQL errors. Probably not the better solution though 4964 if (!$this->element) { 4965 return 0; 4966 } 4967 if (!($this->id > 0)) { 4968 return 0; 4969 } 4970 if (is_array($this->array_languages)) { 4971 return 1; 4972 } 4973 4974 $this->array_languages = array(); 4975 4976 $element = $this->element; 4977 if ($element == 'categorie') $element = 'categories'; // For compatibility 4978 4979 // Request to get translation values for object 4980 $sql = "SELECT rowid, property, lang , value"; 4981 $sql .= " FROM ".MAIN_DB_PREFIX."object_lang"; 4982 $sql .= " WHERE type_object = '".$this->db->escape($element)."'"; 4983 $sql .= " AND fk_object = ".$this->id; 4984 4985 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose 4986 $resql = $this->db->query($sql); 4987 if ($resql) 4988 { 4989 $numrows = $this->db->num_rows($resql); 4990 if ($numrows) 4991 { 4992 $i = 0; 4993 while ($i < $numrows) { 4994 $obj = $this->db->fetch_object($resql); 4995 $key = $obj->property; 4996 $value = $obj->value; 4997 $codelang = $obj->lang; 4998 $type = $this->fields[$key]['type']; 4999 5000 // we can add this attribute to object 5001 if (preg_match('/date/', $type)) 5002 { 5003 $this->array_languages[$key][$codelang] = $this->db->jdate($value); 5004 } else { 5005 $this->array_languages[$key][$codelang] = $value; 5006 } 5007 5008 $i++; 5009 } 5010 } 5011 5012 $this->db->free($resql); 5013 5014 if ($numrows) return $numrows; 5015 else return 0; 5016 } else { 5017 dol_print_error($this->db); 5018 return -1; 5019 } 5020 } 5021 5022 /** 5023 * Fill array_options property of object by extrafields value (using for data sent by forms) 5024 * 5025 * @param string $onlykey Only the following key is filled. When we make update of only one language field ($action = 'update_languages'), calling page must set this to avoid to have other languages being reset. 5026 * @return int 1 if array_options set, 0 if no value, -1 if error (field required missing for example) 5027 */ 5028 public function setValuesForExtraLanguages($onlykey = '') 5029 { 5030 global $_POST, $langs; 5031 5032 // Get extra fields 5033 foreach ($_POST as $postfieldkey => $postfieldvalue) { 5034 $tmparray = explode('-', $postfieldkey); 5035 if ($tmparray[0] != 'field') continue; 5036 5037 $element = $tmparray[1]; 5038 $key = $tmparray[2]; 5039 $codelang = $tmparray[3]; 5040 //var_dump("postfieldkey=".$postfieldkey." element=".$element." key=".$key." codelang=".$codelang); 5041 5042 if (!empty($onlykey) && $key != $onlykey) continue; 5043 if ($element != $this->element) continue; 5044 5045 $key_type = $this->fields[$key]['type']; 5046 5047 $enabled = 1; 5048 if (isset($this->fields[$key]['enabled'])) 5049 { 5050 $enabled = dol_eval($this->fields[$key]['enabled'], 1); 5051 } 5052 /*$perms = 1; 5053 if (isset($this->fields[$key]['perms'])) 5054 { 5055 $perms = dol_eval($this->fields[$key]['perms'], 1); 5056 }*/ 5057 if (empty($enabled)) continue; 5058 //if (empty($perms)) continue; 5059 5060 if (in_array($key_type, array('date'))) 5061 { 5062 // Clean parameters 5063 // TODO GMT date in memory must be GMT so we should add gm=true in parameters 5064 $value_key = dol_mktime(0, 0, 0, $_POST[$postfieldkey."month"], $_POST[$postfieldkey."day"], $_POST[$postfieldkey."year"]); 5065 } elseif (in_array($key_type, array('datetime'))) 5066 { 5067 // Clean parameters 5068 // TODO GMT date in memory must be GMT so we should add gm=true in parameters 5069 $value_key = dol_mktime($_POST[$postfieldkey."hour"], $_POST[$postfieldkey."min"], 0, $_POST[$postfieldkey."month"], $_POST[$postfieldkey."day"], $_POST[$postfieldkey."year"]); 5070 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) 5071 { 5072 $value_arr = GETPOST($postfieldkey, 'array'); // check if an array 5073 if (!empty($value_arr)) { 5074 $value_key = implode(',', $value_arr); 5075 } else { 5076 $value_key = ''; 5077 } 5078 } elseif (in_array($key_type, array('price', 'double'))) 5079 { 5080 $value_arr = GETPOST($postfieldkey, 'alpha'); 5081 $value_key = price2num($value_arr); 5082 } else { 5083 $value_key = GETPOST($postfieldkey); 5084 if (in_array($key_type, array('link')) && $value_key == '-1') $value_key = ''; 5085 } 5086 5087 $this->array_languages[$key][$codelang] = $value_key; 5088 5089 /*if ($nofillrequired) { 5090 $langs->load('errors'); 5091 setEventMessages($langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required), null, 'errors'); 5092 return -1; 5093 }*/ 5094 } 5095 5096 return 1; 5097 } 5098 5099 5100 /* Functions for extrafields */ 5101 5102 /** 5103 * Function to make a fetch but set environment to avoid to load computed values before. 5104 * 5105 * @param int $id ID of object 5106 * @return int >0 if OK, 0 if not found, <0 if KO 5107 */ 5108 public function fetchNoCompute($id) 5109 { 5110 global $conf; 5111 5112 $savDisableCompute = $conf->disable_compute; 5113 $conf->disable_compute = 1; 5114 5115 $ret = $this->fetch($id); 5116 5117 $conf->disable_compute = $savDisableCompute; 5118 5119 return $ret; 5120 } 5121 5122 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 5123 /** 5124 * Function to get extra fields of an object into $this->array_options 5125 * This method is in most cases called by method fetch of objects but you can call it separately. 5126 * 5127 * @param int $rowid Id of line. Use the id of object if not defined. Deprecated. Function must be called without parameters. 5128 * @param array $optionsArray Array resulting of call of extrafields->fetch_name_optionals_label(). Deprecated. Function must be called without parameters. 5129 * @return int <0 if error, 0 if no values of extrafield to find nor found, 1 if an attribute is found and value loaded 5130 * @see fetchValuesForExtraLanguages() 5131 */ 5132 public function fetch_optionals($rowid = null, $optionsArray = null) 5133 { 5134 // phpcs:enable 5135 global $conf, $extrafields; 5136 5137 if (empty($rowid)) $rowid = $this->id; 5138 if (empty($rowid) && isset($this->rowid)) $rowid = $this->rowid; // deprecated 5139 5140 // To avoid SQL errors. Probably not the better solution though 5141 if (!$this->table_element) { 5142 return 0; 5143 } 5144 5145 $this->array_options = array(); 5146 5147 if (!is_array($optionsArray)) 5148 { 5149 // If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page. 5150 if (!isset($extrafields) || !is_object($extrafields)) 5151 { 5152 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; 5153 $extrafields = new ExtraFields($this->db); 5154 } 5155 5156 // Load array of extrafields for elementype = $this->table_element 5157 if (empty($extrafields->attributes[$this->table_element]['loaded'])) 5158 { 5159 $extrafields->fetch_name_optionals_label($this->table_element); 5160 } 5161 $optionsArray = (!empty($extrafields->attributes[$this->table_element]['label']) ? $extrafields->attributes[$this->table_element]['label'] : null); 5162 } else { 5163 global $extrafields; 5164 dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING); 5165 } 5166 5167 $table_element = $this->table_element; 5168 if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility 5169 5170 // Request to get complementary values 5171 if (is_array($optionsArray) && count($optionsArray) > 0) 5172 { 5173 $sql = "SELECT rowid"; 5174 foreach ($optionsArray as $name => $label) 5175 { 5176 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate') 5177 { 5178 $sql .= ", ".$name; 5179 } 5180 } 5181 $sql .= " FROM ".MAIN_DB_PREFIX.$table_element."_extrafields"; 5182 $sql .= " WHERE fk_object = ".((int) $rowid); 5183 5184 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose 5185 $resql = $this->db->query($sql); 5186 if ($resql) 5187 { 5188 $numrows = $this->db->num_rows($resql); 5189 if ($numrows) 5190 { 5191 $tab = $this->db->fetch_array($resql); 5192 5193 foreach ($tab as $key => $value) 5194 { 5195 // Test fetch_array ! is_int($key) because fetch_array result is a mix table with Key as alpha and Key as int (depend db engine) 5196 if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && !is_int($key)) 5197 { 5198 // we can add this attribute to object 5199 if (!empty($extrafields) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date', 'datetime'))) 5200 { 5201 //var_dump($extrafields->attributes[$this->table_element]['type'][$key]); 5202 $this->array_options["options_".$key] = $this->db->jdate($value); 5203 } else { 5204 $this->array_options["options_".$key] = $value; 5205 } 5206 5207 //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]); 5208 } 5209 } 5210 5211 // If field is a computed field, value must become result of compute 5212 foreach ($tab as $key => $value) { 5213 if (!empty($extrafields) && !empty($extrafields->attributes[$this->table_element]['computed'][$key])) 5214 { 5215 //var_dump($conf->disable_compute); 5216 if (empty($conf->disable_compute)) { 5217 $this->array_options["options_".$key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0); 5218 } 5219 } 5220 } 5221 } 5222 5223 $this->db->free($resql); 5224 5225 if ($numrows) return $numrows; 5226 else return 0; 5227 } else { 5228 dol_print_error($this->db); 5229 return -1; 5230 } 5231 } 5232 return 0; 5233 } 5234 5235 /** 5236 * Delete all extra fields values for the current object. 5237 * 5238 * @return int <0 if KO, >0 if OK 5239 * @see deleteExtraLanguages(), insertExtraField(), updateExtraField(), setValueFrom() 5240 */ 5241 public function deleteExtraFields() 5242 { 5243 global $conf; 5244 5245 if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0; 5246 5247 $this->db->begin(); 5248 5249 $table_element = $this->table_element; 5250 if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility 5251 5252 $sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id; 5253 dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG); 5254 $resql = $this->db->query($sql_del); 5255 if (!$resql) 5256 { 5257 $this->error = $this->db->lasterror(); 5258 $this->db->rollback(); 5259 return -1; 5260 } else { 5261 $this->db->commit(); 5262 return 1; 5263 } 5264 } 5265 5266 /** 5267 * Add/Update all extra fields values for the current object. 5268 * Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...) 5269 * This function delete record with all extrafields and insert them again from the array $this->array_options. 5270 * 5271 * @param string $trigger If defined, call also the trigger (for example COMPANY_MODIFY) 5272 * @param User $userused Object user 5273 * @return int -1=error, O=did nothing, 1=OK 5274 * @see insertExtraLanguages(), updateExtraField(), deleteExtraField(), setValueFrom() 5275 */ 5276 public function insertExtraFields($trigger = '', $userused = null) 5277 { 5278 global $conf, $langs, $user; 5279 5280 if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0; 5281 5282 if (empty($userused)) $userused = $user; 5283 5284 $error = 0; 5285 5286 if (!empty($this->array_options)) 5287 { 5288 // Check parameters 5289 $langs->load('admin'); 5290 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; 5291 $extrafields = new ExtraFields($this->db); 5292 $target_extrafields = $extrafields->fetch_name_optionals_label($this->table_element); 5293 5294 // Eliminate copied source object extra fields that do not exist in target object 5295 $new_array_options = array(); 5296 foreach ($this->array_options as $key => $value) { 5297 if (in_array(substr($key, 8), array_keys($target_extrafields))) // We remove the 'options_' from $key for test 5298 $new_array_options[$key] = $value; 5299 elseif (in_array($key, array_keys($target_extrafields))) // We test on $key that does not contains the 'options_' prefix 5300 $new_array_options['options_'.$key] = $value; 5301 } 5302 5303 foreach ($new_array_options as $key => $value) 5304 { 5305 $attributeKey = substr($key, 8); // Remove 'options_' prefix 5306 $attributeType = $extrafields->attributes[$this->table_element]['type'][$attributeKey]; 5307 $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$attributeKey]; 5308 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$attributeKey]; 5309 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey]; 5310 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$attributeKey]; 5311 5312 // Similar code than into insertExtraFields 5313 if ($attributeRequired) 5314 { 5315 $mandatorypb = false; 5316 if ($attributeType == 'link' && $this->array_options[$key] == '-1') $mandatorypb = true; 5317 if ($this->array_options[$key] === '') $mandatorypb = true; 5318 if ($attributeType == 'sellist' && $this->array_options[$key] == '0') $mandatorypb = true; 5319 if ($mandatorypb) 5320 { 5321 dol_syslog("Mandatory extra field ".$key." is empty"); 5322 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel); 5323 return -1; 5324 } 5325 } 5326 5327 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG); 5328 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG); 5329 5330 if (!empty($attrfieldcomputed)) 5331 { 5332 if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS)) 5333 { 5334 $value = dol_eval($attrfieldcomputed, 1, 0); 5335 dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG); 5336 $new_array_options[$key] = $value; 5337 } else { 5338 $new_array_options[$key] = null; 5339 } 5340 } 5341 5342 switch ($attributeType) 5343 { 5344 case 'int': 5345 if (!is_numeric($value) && $value != '') 5346 { 5347 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel); 5348 return -1; 5349 } elseif ($value == '') 5350 { 5351 $new_array_options[$key] = null; 5352 } 5353 break; 5354 case 'price': 5355 case 'double': 5356 $value = price2num($value); 5357 if (!is_numeric($value) && $value != '') 5358 { 5359 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." for ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG); 5360 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel); 5361 return -1; 5362 } elseif ($value == '') 5363 { 5364 $new_array_options[$key] = null; 5365 } 5366 //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG); 5367 $new_array_options[$key] = $value; 5368 break; 5369 /*case 'select': // Not required, we chosed value='0' for undefined values 5370 if ($value=='-1') 5371 { 5372 $this->array_options[$key] = null; 5373 } 5374 break;*/ 5375 case 'password': 5376 $algo = ''; 5377 if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) 5378 { 5379 // If there is an encryption choice, we use it to crypt data before insert 5380 $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']); 5381 $algo = reset($tmparrays); 5382 if ($algo != '') 5383 { 5384 //global $action; // $action may be 'create', 'update', 'update_extras'... 5385 //var_dump($action); 5386 //var_dump($this->oldcopy);exit; 5387 if (is_object($this->oldcopy)) // If this->oldcopy is not defined, we can't know if we change attribute or not, so we must keep value 5388 { 5389 //var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]); 5390 if ($this->array_options[$key] == $this->oldcopy->array_options[$key]) // If old value crypted in database is same than submited new value, it means we don't change it, so we don't update. 5391 { 5392 $new_array_options[$key] = $this->array_options[$key]; // Value is kept 5393 } else { 5394 // var_dump($algo); 5395 $newvalue = dol_hash($this->array_options[$key], $algo); 5396 $new_array_options[$key] = $newvalue; 5397 } 5398 } else { 5399 $new_array_options[$key] = $this->array_options[$key]; // Value is kept 5400 } 5401 } 5402 } else // Common usage 5403 { 5404 $new_array_options[$key] = $this->array_options[$key]; 5405 } 5406 break; 5407 case 'date': 5408 case 'datetime': 5409 // If data is a string instead of a timestamp, we convert it 5410 if (!is_int($this->array_options[$key])) { 5411 $this->array_options[$key] = strtotime($this->array_options[$key]); 5412 } 5413 $new_array_options[$key] = $this->db->idate($this->array_options[$key]); 5414 break; 5415 case 'link': 5416 $param_list = array_keys($attributeParam['options']); 5417 // 0 : ObjectName 5418 // 1 : classPath 5419 $InfoFieldList = explode(":", $param_list[0]); 5420 dol_include_once($InfoFieldList[1]); 5421 if ($InfoFieldList[0] && class_exists($InfoFieldList[0])) 5422 { 5423 if ($value == '-1') // -1 is key for no defined in combo list of objects 5424 { 5425 $new_array_options[$key] = ''; 5426 } elseif ($value) { 5427 $object = new $InfoFieldList[0]($this->db); 5428 if (is_numeric($value)) $res = $object->fetch($value); // Common case 5429 else $res = $object->fetch('', $value); // For compatibility 5430 5431 if ($res > 0) $new_array_options[$key] = $object->id; 5432 else { 5433 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found"; 5434 $this->db->rollback(); 5435 return -1; 5436 } 5437 } 5438 } else { 5439 dol_syslog('Error bad setup of extrafield', LOG_WARNING); 5440 } 5441 break; 5442 } 5443 } 5444 5445 $this->db->begin(); 5446 5447 $table_element = $this->table_element; 5448 if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility 5449 5450 dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG); 5451 5452 $sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id; 5453 $this->db->query($sql_del); 5454 5455 $sql = "INSERT INTO ".MAIN_DB_PREFIX.$table_element."_extrafields (fk_object"; 5456 foreach ($new_array_options as $key => $value) 5457 { 5458 $attributeKey = substr($key, 8); // Remove 'options_' prefix 5459 // Add field of attribut 5460 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator 5461 $sql .= ",".$attributeKey; 5462 } 5463 // We must insert a default value for fields for other entities that are mandatory to avoid not null error 5464 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) { 5465 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) { 5466 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously 5467 $sql .= ",".$tmpkey; 5468 } 5469 } 5470 } 5471 $sql .= ") VALUES (".$this->id; 5472 5473 foreach ($new_array_options as $key => $value) { 5474 $attributeKey = substr($key, 8); // Remove 'options_' prefix 5475 // Add field of attribute 5476 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator) 5477 if ($new_array_options[$key] != '' || $new_array_options[$key] == '0') { 5478 $sql .= ",'".$this->db->escape($new_array_options[$key])."'"; 5479 } else { 5480 $sql .= ",null"; 5481 } 5482 } 5483 } 5484 // We must insert a default value for fields for other entities that are mandatory to avoid not null error 5485 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) { 5486 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) { 5487 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously 5488 if (in_array($tmpval, array('int', 'double', 'price'))) $sql .= ", 0"; 5489 else $sql .= ", ''"; 5490 } 5491 } 5492 } 5493 5494 $sql .= ")"; 5495 5496 $resql = $this->db->query($sql); 5497 if (!$resql) 5498 { 5499 $this->error = $this->db->lasterror(); 5500 $error++; 5501 } 5502 5503 if (!$error && $trigger) 5504 { 5505 // Call trigger 5506 $this->context = array('extrafieldaddupdate'=>1); 5507 $result = $this->call_trigger($trigger, $userused); 5508 if ($result < 0) $error++; 5509 // End call trigger 5510 } 5511 5512 if ($error) 5513 { 5514 $this->db->rollback(); 5515 return -1; 5516 } else { 5517 $this->db->commit(); 5518 return 1; 5519 } 5520 } else return 0; 5521 } 5522 5523 /** 5524 * Add/Update all extra fields values for the current object. 5525 * Data to describe values to insert/update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...) 5526 * This function delete record with all extrafields and insert them again from the array $this->array_options. 5527 * 5528 * @param string $trigger If defined, call also the trigger (for example COMPANY_MODIFY) 5529 * @param User $userused Object user 5530 * @return int -1=error, O=did nothing, 1=OK 5531 * @see insertExtraFields(), updateExtraField(), setValueFrom() 5532 */ 5533 public function insertExtraLanguages($trigger = '', $userused = null) 5534 { 5535 global $conf, $langs, $user; 5536 5537 if (empty($userused)) $userused = $user; 5538 5539 $error = 0; 5540 5541 if (!empty($conf->global->MAIN_EXTRALANGUAGES_DISABLED)) return 0; // For avoid conflicts if trigger used 5542 5543 if (is_array($this->array_languages)) 5544 { 5545 $new_array_languages = $this->array_languages; 5546 5547 foreach ($new_array_languages as $key => $value) 5548 { 5549 $attributeKey = $key; 5550 $attributeType = $this->fields[$attributeKey]['type']; 5551 $attributeLabel = $this->fields[$attributeKey]['label']; 5552 5553 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG); 5554 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG); 5555 5556 switch ($attributeType) 5557 { 5558 case 'int': 5559 if (!is_numeric($value) && $value != '') 5560 { 5561 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel); 5562 return -1; 5563 } elseif ($value == '') 5564 { 5565 $new_array_languages[$key] = null; 5566 } 5567 break; 5568 case 'double': 5569 $value = price2num($value); 5570 if (!is_numeric($value) && $value != '') 5571 { 5572 dol_syslog($langs->trans("ExtraLanguageHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG); 5573 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel); 5574 return -1; 5575 } elseif ($value == '') 5576 { 5577 $new_array_languages[$key] = null; 5578 } 5579 //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG); 5580 $new_array_languages[$key] = $value; 5581 break; 5582 /*case 'select': // Not required, we chosed value='0' for undefined values 5583 if ($value=='-1') 5584 { 5585 $this->array_options[$key] = null; 5586 } 5587 break;*/ 5588 } 5589 } 5590 5591 $this->db->begin(); 5592 5593 $table_element = $this->table_element; 5594 if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility 5595 5596 dol_syslog(get_class($this)."::insertExtraLanguages delete then insert", LOG_DEBUG); 5597 5598 foreach ($new_array_languages as $key => $langcodearray) { // $key = 'name', 'town', ... 5599 foreach ($langcodearray as $langcode => $value) { 5600 $sql_del = "DELETE FROM ".MAIN_DB_PREFIX."object_lang"; 5601 $sql_del .= " WHERE fk_object = ".$this->id." AND property = '".$this->db->escape($key)."' AND type_object = '".$this->db->escape($table_element)."'"; 5602 $sql_del .= " AND lang = '".$this->db->escape($langcode)."'"; 5603 $this->db->query($sql_del); 5604 5605 if ($value !== '') { 5606 $sql = "INSERT INTO ".MAIN_DB_PREFIX."object_lang (fk_object, property, type_object, lang, value"; 5607 $sql .= ") VALUES (".$this->id.", '".$this->db->escape($key)."', '".$this->db->escape($table_element)."', '".$this->db->escape($langcode)."', '".$this->db->escape($value)."'"; 5608 $sql .= ")"; 5609 5610 $resql = $this->db->query($sql); 5611 if (!$resql) 5612 { 5613 $this->error = $this->db->lasterror(); 5614 $error++; 5615 break; 5616 } 5617 } 5618 } 5619 } 5620 5621 if (!$error && $trigger) 5622 { 5623 // Call trigger 5624 $this->context = array('extralanguagesaddupdate'=>1); 5625 $result = $this->call_trigger($trigger, $userused); 5626 if ($result < 0) $error++; 5627 // End call trigger 5628 } 5629 5630 if ($error) 5631 { 5632 $this->db->rollback(); 5633 return -1; 5634 } else { 5635 $this->db->commit(); 5636 return 1; 5637 } 5638 } else return 0; 5639 } 5640 5641 /** 5642 * Update 1 extra field value for the current object. Keep other fields unchanged. 5643 * Data to describe values to update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...) 5644 * 5645 * @param string $key Key of the extrafield to update (without starting 'options_') 5646 * @param string $trigger If defined, call also the trigger (for example COMPANY_MODIFY) 5647 * @param User $userused Object user 5648 * @return int -1=error, O=did nothing, 1=OK 5649 * @see updateExtraLanguages(), insertExtraFields(), deleteExtraFields(), setValueFrom() 5650 */ 5651 public function updateExtraField($key, $trigger = null, $userused = null) 5652 { 5653 global $conf, $langs, $user; 5654 5655 if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0; 5656 5657 if (empty($userused)) $userused = $user; 5658 5659 $error = 0; 5660 5661 if (!empty($this->array_options) && isset($this->array_options["options_".$key])) 5662 { 5663 // Check parameters 5664 $langs->load('admin'); 5665 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php'; 5666 $extrafields = new ExtraFields($this->db); 5667 $extrafields->fetch_name_optionals_label($this->table_element); 5668 5669 $value = $this->array_options["options_".$key]; 5670 5671 $attributeType = $extrafields->attributes[$this->table_element]['type'][$key]; 5672 $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key]; 5673 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key]; 5674 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key]; 5675 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$key]; 5676 5677 // Similar code than into insertExtraFields 5678 if ($attributeRequired) 5679 { 5680 $mandatorypb = false; 5681 if ($attributeType == 'link' && $this->array_options["options_".$key] == '-1') $mandatorypb = true; 5682 if ($this->array_options["options_".$key] === '') $mandatorypb = true; 5683 if ($mandatorypb) 5684 { 5685 dol_syslog("Mandatory extra field options_".$key." is empty"); 5686 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel); 5687 return -1; 5688 } 5689 } 5690 5691 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG); 5692 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG); 5693 5694 if (!empty($attrfieldcomputed)) 5695 { 5696 if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS)) 5697 { 5698 $value = dol_eval($attrfieldcomputed, 1, 0); 5699 dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG); 5700 $this->array_options["options_".$key] = $value; 5701 } else { 5702 $this->array_options["options_".$key] = null; 5703 } 5704 } 5705 5706 switch ($attributeType) 5707 { 5708 case 'int': 5709 if (!is_numeric($value) && $value != '') 5710 { 5711 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel); 5712 return -1; 5713 } elseif ($value === '') 5714 { 5715 $this->array_options["options_".$key] = null; 5716 } 5717 break; 5718 case 'double': 5719 $value = price2num($value); 5720 if (!is_numeric($value) && $value != '') 5721 { 5722 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG); 5723 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel); 5724 return -1; 5725 } elseif ($value === '') 5726 { 5727 $this->array_options["options_".$key] = null; 5728 } 5729 //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG); 5730 $this->array_options["options_".$key] = $value; 5731 break; 5732 /*case 'select': // Not required, we chosed value='0' for undefined values 5733 if ($value=='-1') 5734 { 5735 $this->array_options[$key] = null; 5736 } 5737 break;*/ 5738 case 'price': 5739 $this->array_options["options_".$key] = price2num($this->array_options["options_".$key]); 5740 break; 5741 case 'date': 5742 case 'datetime': 5743 if (empty($this->array_options["options_".$key])) { 5744 $this->array_options["options_".$key] = null; 5745 } else { 5746 $this->array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key]); 5747 } 5748 break; 5749 /* 5750 case 'link': 5751 $param_list = array_keys($attributeParam['options']); 5752 // 0 : ObjectName 5753 // 1 : classPath 5754 $InfoFieldList = explode(":", $param_list[0]); 5755 dol_include_once($InfoFieldList[1]); 5756 if ($InfoFieldList[0] && class_exists($InfoFieldList[0])) 5757 { 5758 if ($value == '-1') // -1 is key for no defined in combo list of objects 5759 { 5760 $new_array_options[$key] = ''; 5761 } elseif ($value) { 5762 $object = new $InfoFieldList[0]($this->db); 5763 if (is_numeric($value)) $res = $object->fetch($value); // Common case 5764 else $res = $object->fetch('', $value); // For compatibility 5765 5766 if ($res > 0) $new_array_options[$key] = $object->id; 5767 else { 5768 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found"; 5769 $this->db->rollback(); 5770 return -1; 5771 } 5772 } 5773 } else { 5774 dol_syslog('Error bad setup of extrafield', LOG_WARNING); 5775 } 5776 break; 5777 */ 5778 } 5779 5780 $this->db->begin(); 5781 5782 $linealreadyfound = 0; 5783 5784 // Check if there is already a line for this object (in most cases, it is, but sometimes it is not, for example when extra field has been created after), so we must keep this overload) 5785 $sql = "SELECT COUNT(rowid) as nb FROM ".MAIN_DB_PREFIX.$this->table_element."_extrafields WHERE fk_object = ".$this->id; 5786 $resql = $this->db->query($sql); 5787 if ($resql) { 5788 $tmpobj = $this->db->fetch_object($resql); 5789 if ($tmpobj) { 5790 $linealreadyfound = $tmpobj->nb; 5791 } 5792 } 5793 5794 if ($linealreadyfound) { 5795 if ($this->array_options["options_".$key] === null) { 5796 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element."_extrafields SET ".$key." = null"; 5797 } else { 5798 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element."_extrafields SET ".$key." = '".$this->db->escape($this->array_options["options_".$key])."'"; 5799 } 5800 $sql .= " WHERE fk_object = ".$this->id; 5801 } else { 5802 $result = $this->insertExtraFields('', $user); 5803 if ($result < 0) $error++; 5804 } 5805 5806 $resql = $this->db->query($sql); 5807 if (!$resql) 5808 { 5809 $error++; 5810 $this->error = $this->db->lasterror(); 5811 } 5812 if (!$error && $trigger) 5813 { 5814 // Call trigger 5815 $this->context = array('extrafieldupdate'=>1); 5816 $result = $this->call_trigger($trigger, $userused); 5817 if ($result < 0) $error++; 5818 // End call trigger 5819 } 5820 5821 if ($error) 5822 { 5823 dol_syslog(__METHOD__.$this->error, LOG_ERR); 5824 $this->db->rollback(); 5825 return -1; 5826 } else { 5827 $this->db->commit(); 5828 return 1; 5829 } 5830 } else return 0; 5831 } 5832 5833 /** 5834 * Update an extra language value for the current object. 5835 * Data to describe values to update are stored into $this->array_options=array('options_codeforfield1'=>'valueforfield1', 'options_codeforfield2'=>'valueforfield2', ...) 5836 * 5837 * @param string $key Key of the extrafield (without starting 'options_') 5838 * @param string $trigger If defined, call also the trigger (for example COMPANY_MODIFY) 5839 * @param User $userused Object user 5840 * @return int -1=error, O=did nothing, 1=OK 5841 * @see updateExtraFields(), insertExtraLanguages() 5842 */ 5843 public function updateExtraLanguages($key, $trigger = null, $userused = null) 5844 { 5845 global $conf, $langs, $user; 5846 5847 if (empty($userused)) $userused = $user; 5848 5849 $error = 0; 5850 5851 if (!empty($conf->global->MAIN_EXTRALANGUAGES_DISABLED)) return 0; // For avoid conflicts if trigger used 5852 5853 return 0; 5854 } 5855 5856 5857 /** 5858 * Return HTML string to put an input field into a page 5859 * Code very similar with showInputField of extra fields 5860 * 5861 * @param array $val Array of properties for field to show (used only if ->fields not defined) 5862 * @param string $key Key of attribute 5863 * @param string|array $value Preselected value to show (for date type it must be in timestamp format, for amount or price it must be a php numeric value, for array type must be array) 5864 * @param string $moreparam To add more parameters on html input tag 5865 * @param string $keysuffix Prefix string to add into name and id of field (can be used to avoid duplicate names) 5866 * @param string $keyprefix Suffix string to add into name and id of field (can be used to avoid duplicate names) 5867 * @param string|int $morecss Value for css to define style/length of field. May also be a numeric. 5868 * @param int $nonewbutton Force to not show the new button on field that are links to object 5869 * @return string 5870 */ 5871 public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0, $nonewbutton = 0) 5872 { 5873 global $conf, $langs, $form; 5874 5875 if (!is_object($form)) 5876 { 5877 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; 5878 $form = new Form($this->db); 5879 } 5880 5881 if (!empty($this->fields)) { 5882 $val = $this->fields[$key]; 5883 } 5884 5885 $out = ''; 5886 $type = ''; 5887 $isDependList=0; 5888 $param = array(); 5889 $param['options'] = array(); 5890 $reg = array(); 5891 $size = $this->fields[$key]['size']; 5892 // Because we work on extrafields 5893 if (preg_match('/^(integer|link):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) { 5894 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N'); 5895 $type = 'link'; 5896 } elseif (preg_match('/^(integer|link):(.*):(.*):(.*)/i', $val['type'], $reg)) { 5897 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N'); 5898 $type = 'link'; 5899 } elseif (preg_match('/^(integer|link):(.*):(.*)/i', $val['type'], $reg)) { 5900 $param['options'] = array($reg[2].':'.$reg[3] => 'N'); 5901 $type = 'link'; 5902 } elseif (preg_match('/^(sellist):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) { 5903 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N'); 5904 $type = 'sellist'; 5905 } elseif (preg_match('/^(sellist):(.*):(.*):(.*)/i', $val['type'], $reg)) { 5906 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N'); 5907 $type = 'sellist'; 5908 } elseif (preg_match('/^(sellist):(.*):(.*)/i', $val['type'], $reg)) { 5909 $param['options'] = array($reg[2].':'.$reg[3] => 'N'); 5910 $type = 'sellist'; 5911 } elseif (preg_match('/varchar\((\d+)\)/', $val['type'], $reg)) { 5912 $param['options'] = array(); 5913 $type = 'varchar'; 5914 $size = $reg[1]; 5915 } elseif (preg_match('/varchar/', $val['type'])) { 5916 $param['options'] = array(); 5917 $type = 'varchar'; 5918 } elseif (is_array($this->fields[$key]['arrayofkeyval'])) { 5919 $param['options'] = $this->fields[$key]['arrayofkeyval']; 5920 $type = $this->fields[$key]['type']; 5921 if (!in_array($type, array('select', 'checkbox', 'radio'))) { 5922 $type = 'select'; 5923 } else { 5924 $type = $this->fields[$key]['type']; 5925 } 5926 } else { 5927 $param['options'] = array(); 5928 $type = $this->fields[$key]['type']; 5929 } 5930 5931 5932 $label = $this->fields[$key]['label']; 5933 //$elementtype=$this->fields[$key]['elementtype']; // Seems not used 5934 $default = $this->fields[$key]['default']; 5935 $computed = $this->fields[$key]['computed']; 5936 $unique = $this->fields[$key]['unique']; 5937 $required = $this->fields[$key]['required']; 5938 $autofocusoncreate = $this->fields[$key]['autofocusoncreate']; 5939 5940 $langfile = $this->fields[$key]['langfile']; 5941 $list = $this->fields[$key]['list']; 5942 $hidden = (in_array(abs($this->fields[$key]['visible']), array(0, 2)) ? 1 : 0); 5943 5944 $objectid = $this->id; 5945 5946 if ($computed) { 5947 if (!preg_match('/^search_/', $keyprefix)) return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>'; 5948 else return ''; 5949 } 5950 5951 // Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine 5952 if (empty($morecss) && !empty($val['css'])) { 5953 $morecss = $val['css']; 5954 } elseif (empty($morecss)) { 5955 if ($type == 'date') { 5956 $morecss = 'minwidth100imp'; 5957 } elseif ($type == 'datetime' || $type == 'link') { // link means an foreign key to another primary id 5958 $morecss = 'minwidth200imp'; 5959 } elseif (in_array($type, array('int', 'integer', 'price')) || preg_match('/^double(\([0-9],[0-9]\)){0,1}/', $type)) { 5960 $morecss = 'maxwidth75'; 5961 } elseif ($type == 'url') { 5962 $morecss = 'minwidth400'; 5963 } elseif ($type == 'boolean') { 5964 $morecss = ''; 5965 } else { 5966 if (round($size) < 12) { 5967 $morecss = 'minwidth100'; 5968 } elseif (round($size) <= 48) { 5969 $morecss = 'minwidth200'; 5970 } else { 5971 $morecss = 'minwidth400'; 5972 } 5973 } 5974 } 5975 5976 if (in_array($type, array('date'))) { 5977 $tmp = explode(',', $size); 5978 $newsize = $tmp[0]; 5979 $showtime = 0; 5980 5981 // Do not show current date when field not required (see selectDate() method) 5982 if (!$required && $value == '') $value = '-1'; 5983 5984 // TODO Must also support $moreparam 5985 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1); 5986 } elseif (in_array($type, array('datetime'))) { 5987 $tmp = explode(',', $size); 5988 $newsize = $tmp[0]; 5989 $showtime = 1; 5990 5991 // Do not show current date when field not required (see selectDate() method) 5992 if (!$required && $value == '') $value = '-1'; 5993 5994 // TODO Must also support $moreparam 5995 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel'); 5996 } elseif (in_array($type, array('duration'))) { 5997 $out = $form->select_duration($keyprefix.$key.$keysuffix, $value, 0, 'text', 0, 1); 5998 } elseif (in_array($type, array('int', 'integer'))) { 5999 $tmp = explode(',', $size); 6000 $newsize = $tmp[0]; 6001 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$newsize.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>'; 6002 } elseif (in_array($type, array('real'))) { 6003 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>'; 6004 } elseif (preg_match('/varchar/', $type)) { 6005 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$size.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>'; 6006 } elseif (in_array($type, array('mail', 'phone', 'url'))) { 6007 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>'; 6008 } elseif (preg_match('/^text/', $type)) { 6009 if (!preg_match('/search_/', $keyprefix)) // If keyprefix is search_ or search_options_, we must just use a simple text field 6010 { 6011 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; 6012 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%'); 6013 $out = $doleditor->Create(1); 6014 } else { 6015 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>'; 6016 } 6017 } elseif (preg_match('/^html/', $type)) { 6018 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field 6019 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; 6020 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, !empty($conf->fckeditor->enabled) && $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_5, '90%'); 6021 $out = $doleditor->Create(1); 6022 } else { 6023 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>'; 6024 } 6025 } elseif ($type == 'boolean') { 6026 $checked = ''; 6027 if (!empty($value)) { 6028 $checked = ' checked value="1" '; 6029 } else { 6030 $checked = ' value="1" '; 6031 } 6032 $out = '<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>'; 6033 } elseif ($type == 'price') { 6034 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format. 6035 $value = price($value); 6036 } 6037 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '.$langs->getCurrencySymbol($conf->currency); 6038 } elseif (preg_match('/^double(\([0-9],[0-9]\)){0,1}/', $type)) { 6039 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format. 6040 $value = price($value); 6041 } 6042 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '; 6043 } elseif ($type == 'select') { 6044 $out = ''; 6045 if (!empty($conf->use_javascript_ajax) && !empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2)) 6046 { 6047 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; 6048 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0); 6049 } 6050 6051 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>'; 6052 if ((!isset($this->fields[$key]['default'])) || ($this->fields[$key]['notnull'] != 1))$out .= '<option value="0"> </option>'; 6053 foreach ($param['options'] as $key => $val) 6054 { 6055 if ((string) $key == '') continue; 6056 list($val, $parent) = explode('|', $val); 6057 $out .= '<option value="'.$key.'"'; 6058 $out .= (((string) $value == (string) $key) ? ' selected' : ''); 6059 $out .= (!empty($parent) ? ' parent="'.$parent.'"' : ''); 6060 $out .= '>'.$val.'</option>'; 6061 } 6062 $out .= '</select>'; 6063 } elseif ($type == 'sellist') { 6064 $out = ''; 6065 if (!empty($conf->use_javascript_ajax) && !empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2)) { 6066 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php'; 6067 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0); 6068 } 6069 6070 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>'; 6071 if (is_array($param['options'])) { 6072 $param_list = array_keys($param['options']); 6073 $InfoFieldList = explode(":", $param_list[0]); 6074 $parentName = ''; 6075 $parentField = ''; 6076 // 0 : tableName 6077 // 1 : label field name 6078 // 2 : key fields name (if differ of rowid) 6079 // 3 : key field parent (for dependent lists) 6080 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value 6081 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid'); 6082 6083 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) { 6084 if (strpos($InfoFieldList[4], 'extra.') !== false) { 6085 $keyList = 'main.'.$InfoFieldList[2].' as rowid'; 6086 } else { 6087 $keyList = $InfoFieldList[2].' as rowid'; 6088 } 6089 } 6090 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) { 6091 list($parentName, $parentField) = explode('|', $InfoFieldList[3]); 6092 $keyList .= ', '.$parentField; 6093 } 6094 6095 $fields_label = explode('|', $InfoFieldList[1]); 6096 if (is_array($fields_label)) { 6097 $keyList .= ', '; 6098 $keyList .= implode(', ', $fields_label); 6099 } 6100 6101 $sqlwhere = ''; 6102 $sql = 'SELECT '.$keyList; 6103 $sql .= ' FROM '.MAIN_DB_PREFIX.$InfoFieldList[0]; 6104 if (!empty($InfoFieldList[4])) 6105 { 6106 // can use SELECT request 6107 if (strpos($InfoFieldList[4], '$SEL$') !== false) { 6108 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]); 6109 } 6110 6111 // current object id can be use into filter 6112 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) { 6113 $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]); 6114 } else { 6115 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]); 6116 } 6117 //We have to join on extrafield table 6118 if (strpos($InfoFieldList[4], 'extra') !== false) 6119 { 6120 $sql .= ' as main, '.MAIN_DB_PREFIX.$InfoFieldList[0].'_extrafields as extra'; 6121 $sqlwhere .= ' WHERE extra.fk_object=main.'.$InfoFieldList[2].' AND '.$InfoFieldList[4]; 6122 } else { 6123 $sqlwhere .= ' WHERE '.$InfoFieldList[4]; 6124 } 6125 } else { 6126 $sqlwhere .= ' WHERE 1=1'; 6127 } 6128 // Some tables may have field, some other not. For the moment we disable it. 6129 if (in_array($InfoFieldList[0], array('tablewithentity'))) 6130 { 6131 $sqlwhere .= ' AND entity = '.$conf->entity; 6132 } 6133 $sql .= $sqlwhere; 6134 //print $sql; 6135 6136 $sql .= ' ORDER BY '.implode(', ', $fields_label); 6137 6138 dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG); 6139 $resql = $this->db->query($sql); 6140 if ($resql) 6141 { 6142 $out .= '<option value="0"> </option>'; 6143 $num = $this->db->num_rows($resql); 6144 $i = 0; 6145 while ($i < $num) 6146 { 6147 $labeltoshow = ''; 6148 $obj = $this->db->fetch_object($resql); 6149 6150 // Several field into label (eq table:code|libelle:rowid) 6151 $notrans = false; 6152 $fields_label = explode('|', $InfoFieldList[1]); 6153 if (count($fields_label) > 1) 6154 { 6155 $notrans = true; 6156 foreach ($fields_label as $field_toshow) 6157 { 6158 $labeltoshow .= $obj->$field_toshow.' '; 6159 } 6160 } else { 6161 $labeltoshow = $obj->{$InfoFieldList[1]}; 6162 } 6163 $labeltoshow = dol_trunc($labeltoshow, 45); 6164 6165 if ($value == $obj->rowid) 6166 { 6167 foreach ($fields_label as $field_toshow) 6168 { 6169 $translabel = $langs->trans($obj->$field_toshow); 6170 if ($translabel != $obj->$field_toshow) { 6171 $labeltoshow = dol_trunc($translabel, 18).' '; 6172 } else { 6173 $labeltoshow = dol_trunc($obj->$field_toshow, 18).' '; 6174 } 6175 } 6176 $out .= '<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>'; 6177 } else { 6178 if (!$notrans) 6179 { 6180 $translabel = $langs->trans($obj->{$InfoFieldList[1]}); 6181 if ($translabel != $obj->{$InfoFieldList[1]}) { 6182 $labeltoshow = dol_trunc($translabel, 18); 6183 } else { 6184 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18); 6185 } 6186 } 6187 if (empty($labeltoshow)) $labeltoshow = '(not defined)'; 6188 if ($value == $obj->rowid) 6189 { 6190 $out .= '<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>'; 6191 } 6192 6193 if (!empty($InfoFieldList[3]) && $parentField) 6194 { 6195 $parent = $parentName.':'.$obj->{$parentField}; 6196 $isDependList=1; 6197 } 6198 6199 $out .= '<option value="'.$obj->rowid.'"'; 6200 $out .= ($value == $obj->rowid ? ' selected' : ''); 6201 $out .= (!empty($parent) ? ' parent="'.$parent.'"' : ''); 6202 $out .= '>'.$labeltoshow.'</option>'; 6203 } 6204 6205 $i++; 6206 } 6207 $this->db->free($resql); 6208 } else { 6209 print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>'; 6210 } 6211 } 6212 $out .= '</select>'; 6213 } elseif ($type == 'checkbox') { 6214 $value_arr = explode(',', $value); 6215 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ?null:$param['options']), $value_arr, '', 0, '', 0, '100%'); 6216 } elseif ($type == 'radio') { 6217 $out = ''; 6218 foreach ($param['options'] as $keyopt => $val) 6219 { 6220 $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : ''); 6221 $out .= ' value="'.$keyopt.'"'; 6222 $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"'; 6223 $out .= ($value == $keyopt ? 'checked' : ''); 6224 $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$val.'</label><br>'; 6225 } 6226 } elseif ($type == 'chkbxlst') { 6227 if (is_array($value)) { 6228 $value_arr = $value; 6229 } else { 6230 $value_arr = explode(',', $value); 6231 } 6232 6233 if (is_array($param['options'])) { 6234 $param_list = array_keys($param['options']); 6235 $InfoFieldList = explode(":", $param_list[0]); 6236 $parentName = ''; 6237 $parentField = ''; 6238 // 0 : tableName 6239 // 1 : label field name 6240 // 2 : key fields name (if differ of rowid) 6241 // 3 : key field parent (for dependent lists) 6242 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value 6243 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid'); 6244 6245 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) { 6246 list ($parentName, $parentField) = explode('|', $InfoFieldList[3]); 6247 $keyList .= ', '.$parentField; 6248 } 6249 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) { 6250 if (strpos($InfoFieldList[4], 'extra.') !== false) { 6251 $keyList = 'main.'.$InfoFieldList[2].' as rowid'; 6252 } else { 6253 $keyList = $InfoFieldList[2].' as rowid'; 6254 } 6255 } 6256 6257 $fields_label = explode('|', $InfoFieldList[1]); 6258 if (is_array($fields_label)) { 6259 $keyList .= ', '; 6260 $keyList .= implode(', ', $fields_label); 6261 } 6262 6263 $sqlwhere = ''; 6264 $sql = 'SELECT '.$keyList; 6265 $sql .= ' FROM '.MAIN_DB_PREFIX.$InfoFieldList[0]; 6266 if (!empty($InfoFieldList[4])) { 6267 // can use SELECT request 6268 if (strpos($InfoFieldList[4], '$SEL$') !== false) { 6269 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]); 6270 } 6271 6272 // current object id can be use into filter 6273 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) { 6274 $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]); 6275 } else { 6276 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]); 6277 } 6278 6279 // We have to join on extrafield table 6280 if (strpos($InfoFieldList[4], 'extra') !== false) { 6281 $sql .= ' as main, '.MAIN_DB_PREFIX.$InfoFieldList[0].'_extrafields as extra'; 6282 $sqlwhere .= ' WHERE extra.fk_object=main.'.$InfoFieldList[2].' AND '.$InfoFieldList[4]; 6283 } else { 6284 $sqlwhere .= ' WHERE '.$InfoFieldList[4]; 6285 } 6286 } else { 6287 $sqlwhere .= ' WHERE 1=1'; 6288 } 6289 // Some tables may have field, some other not. For the moment we disable it. 6290 if (in_array($InfoFieldList[0], array('tablewithentity'))) 6291 { 6292 $sqlwhere .= ' AND entity = '.$conf->entity; 6293 } 6294 // $sql.=preg_replace('/^ AND /','',$sqlwhere); 6295 // print $sql; 6296 6297 $sql .= $sqlwhere; 6298 dol_syslog(get_class($this).'::showInputField type=chkbxlst', LOG_DEBUG); 6299 $resql = $this->db->query($sql); 6300 if ($resql) { 6301 $num = $this->db->num_rows($resql); 6302 $i = 0; 6303 6304 $data = array(); 6305 6306 while ($i < $num) { 6307 $labeltoshow = ''; 6308 $obj = $this->db->fetch_object($resql); 6309 6310 $notrans = false; 6311 // Several field into label (eq table:code|libelle:rowid) 6312 $fields_label = explode('|', $InfoFieldList[1]); 6313 if (count($fields_label) > 1) { 6314 $notrans = true; 6315 foreach ($fields_label as $field_toshow) { 6316 $labeltoshow .= $obj->$field_toshow.' '; 6317 } 6318 } else { 6319 $labeltoshow = $obj->{$InfoFieldList[1]}; 6320 } 6321 $labeltoshow = dol_trunc($labeltoshow, 45); 6322 6323 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) { 6324 foreach ($fields_label as $field_toshow) { 6325 $translabel = $langs->trans($obj->$field_toshow); 6326 if ($translabel != $obj->$field_toshow) { 6327 $labeltoshow = dol_trunc($translabel, 18).' '; 6328 } else { 6329 $labeltoshow = dol_trunc($obj->$field_toshow, 18).' '; 6330 } 6331 } 6332 6333 $data[$obj->rowid] = $labeltoshow; 6334 } else { 6335 if (!$notrans) { 6336 $translabel = $langs->trans($obj->{$InfoFieldList[1]}); 6337 if ($translabel != $obj->{$InfoFieldList[1]}) { 6338 $labeltoshow = dol_trunc($translabel, 18); 6339 } else { 6340 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18); 6341 } 6342 } 6343 if (empty($labeltoshow)) { 6344 $labeltoshow = '(not defined)'; 6345 } 6346 6347 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) { 6348 $data[$obj->rowid] = $labeltoshow; 6349 } 6350 6351 if (!empty($InfoFieldList[3]) && $parentField) { 6352 $parent = $parentName.':'.$obj->{$parentField}; 6353 $isDependList=1; 6354 } 6355 6356 $data[$obj->rowid] = $labeltoshow; 6357 } 6358 6359 $i++; 6360 } 6361 $this->db->free($resql); 6362 6363 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, '', 0, '', 0, '100%'); 6364 } else { 6365 print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>'; 6366 } 6367 } 6368 } elseif ($type == 'link') { 6369 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath[:AddCreateButtonOrNot[:Filter]]' 6370 $param_list_array = explode(':', $param_list[0]); 6371 $showempty = (($required && $default != '') ? 0 : 1); 6372 6373 if (!preg_match('/search_/', $keyprefix)) { 6374 if (!empty($param_list_array[2])) { // If the entry into $fields is set to add a create button 6375 if (!empty($this->fields[$key]['picto'])) { 6376 $morecss .= ' widthcentpercentminusxx'; 6377 } else { 6378 $morecss .= ' widthcentpercentminusx'; 6379 } 6380 } else { 6381 if (!empty($this->fields[$key]['picto'])) { 6382 $morecss .= ' widthcentpercentminusx'; 6383 } 6384 } 6385 } 6386 6387 $out = $form->selectForForms($param_list[0], $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, $moreparam, 0, empty($val['disabled']) ? 0 : 1); 6388 6389 if (!empty($param_list_array[2])) { // If the entry into $fields is set to add a create button 6390 if (!GETPOSTISSET('backtopage') && empty($val['disabled']) && empty($nonewbutton)) // To avoid to open several times the 'Create Object' button and to avoid to have button if field is protected by a "disabled". 6391 { 6392 list($class, $classfile) = explode(':', $param_list[0]); 6393 if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) $url_path = dol_buildpath(dirname(dirname($classfile)).'/card.php', 1); 6394 else $url_path = dol_buildpath(dirname(dirname($classfile)).'/'.strtolower($class).'_card.php', 1); 6395 $paramforthenewlink = ''; 6396 $paramforthenewlink .= (GETPOSTISSET('action') ? '&action='.GETPOST('action', 'aZ09') : ''); 6397 $paramforthenewlink .= (GETPOSTISSET('id') ? '&id='.GETPOST('id', 'int') : ''); 6398 $paramforthenewlink .= '&fk_'.strtolower($class).'=--IDFORBACKTOPAGE--'; 6399 // TODO Add Javascript code to add input fields already filled into $paramforthenewlink so we won't loose them when going back to main page 6400 $out .= '<a class="butActionNew" title="'.$langs->trans("New").'" href="'.$url_path.'?action=create&backtopage='.urlencode($_SERVER['PHP_SELF'].($paramforthenewlink ? '?'.$paramforthenewlink : '')).'"><span class="fa fa-plus-circle valignmiddle"></span></a>'; 6401 } 6402 } 6403 } elseif ($type == 'password') { 6404 // If prefix is 'search_', field is used as a filter, we use a common text field. 6405 $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'>'; 6406 } elseif ($type == 'array') { 6407 $newval = $val; 6408 $newval['type'] = 'varchar(256)'; 6409 6410 $out = ''; 6411 if (!empty($value)) { 6412 foreach ($value as $option) { 6413 $out .= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> '; 6414 $out .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $morecss).'<br></span>'; 6415 } 6416 } 6417 $out .= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>'; 6418 6419 $newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> '; 6420 $newInput .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $morecss).'<br></span>'; 6421 6422 if (!empty($conf->use_javascript_ajax)) { 6423 $out .= ' 6424 <script> 6425 $(document).ready(function() { 6426 $("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() { 6427 $("'.dol_escape_js($newInput).'").insertBefore(this); 6428 }); 6429 6430 $(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() { 6431 $(this).parent().remove(); 6432 }); 6433 }); 6434 </script>'; 6435 } 6436 } 6437 if (!empty($hidden)) { 6438 $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>'; 6439 } 6440 6441 if ($isDependList==1) { 6442 $out .= $this->getJSListDependancies('_common'); 6443 } 6444 /* Add comments 6445 if ($type == 'date') $out.=' (YYYY-MM-DD)'; 6446 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)'; 6447 */ 6448 return $out; 6449 } 6450 6451 /** 6452 * Return HTML string to show a field into a page 6453 * Code very similar with showOutputField of extra fields 6454 * 6455 * @param array $val Array of properties of field to show 6456 * @param string $key Key of attribute 6457 * @param string $value Preselected value to show (for date type it must be in timestamp format, for amount or price it must be a php numeric value) 6458 * @param string $moreparam To add more parametes on html input tag 6459 * @param string $keysuffix Prefix string to add into name and id of field (can be used to avoid duplicate names) 6460 * @param string $keyprefix Suffix string to add into name and id of field (can be used to avoid duplicate names) 6461 * @param mixed $morecss Value for css to define size. May also be a numeric. 6462 * @return string 6463 */ 6464 public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '') 6465 { 6466 global $conf, $langs, $form; 6467 6468 if (!is_object($form)) 6469 { 6470 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php'; 6471 $form = new Form($this->db); 6472 } 6473 6474 $objectid = $this->id; 6475 $label = $val['label']; 6476 $type = $val['type']; 6477 $size = $val['css']; 6478 $reg = array(); 6479 6480 // Convert var to be able to share same code than showOutputField of extrafields 6481 if (preg_match('/varchar\((\d+)\)/', $type, $reg)) 6482 { 6483 $type = 'varchar'; // convert varchar(xx) int varchar 6484 $size = $reg[1]; 6485 } elseif (preg_match('/varchar/', $type)) $type = 'varchar'; // convert varchar(xx) int varchar 6486 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) $type = 'select'; 6487 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) $type = 'link'; 6488 6489 $default = $val['default']; 6490 $computed = $val['computed']; 6491 $unique = $val['unique']; 6492 $required = $val['required']; 6493 $param = array(); 6494 $param['options'] = array(); 6495 6496 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) $param['options'] = $val['arrayofkeyval']; 6497 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) { 6498 $type = 'link'; 6499 $param['options'] = array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]); 6500 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) { 6501 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N'); 6502 $type = 'sellist'; 6503 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) { 6504 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N'); 6505 $type = 'sellist'; 6506 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) { 6507 $param['options'] = array($reg[1].':'.$reg[2] => 'N'); 6508 $type = 'sellist'; 6509 } 6510 6511 $langfile = $val['langfile']; 6512 $list = $val['list']; 6513 $help = $val['help']; 6514 $hidden = (($val['visible'] == 0) ? 1 : 0); // If zero, we are sure it is hidden, otherwise we show. If it depends on mode (view/create/edit form or list, this must be filtered by caller) 6515 6516 if ($hidden) return ''; 6517 6518 // If field is a computed field, value must become result of compute 6519 if ($computed) 6520 { 6521 // Make the eval of compute string 6522 //var_dump($computed); 6523 $value = dol_eval($computed, 1, 0); 6524 } 6525 6526 if (empty($morecss)) 6527 { 6528 if ($type == 'date') { 6529 $morecss = 'minwidth100imp'; 6530 } elseif ($type == 'datetime' || $type == 'timestamp') { 6531 $morecss = 'minwidth200imp'; 6532 } elseif (in_array($type, array('int', 'double', 'price'))) { 6533 $morecss = 'maxwidth75'; 6534 } elseif ($type == 'url') { 6535 $morecss = 'minwidth400'; 6536 } elseif ($type == 'boolean') { 6537 $morecss = ''; 6538 } else { 6539 if (round($size) < 12) { 6540 $morecss = 'minwidth100'; 6541 } elseif (round($size) <= 48) { 6542 $morecss = 'minwidth200'; 6543 } else { 6544 $morecss = 'minwidth400'; 6545 } 6546 } 6547 } 6548 6549 // Format output value differently according to properties of field 6550 if ($key == 'ref' && method_exists($this, 'getNomUrl')) $value = $this->getNomUrl(1, '', 0, '', 1); 6551 elseif ($key == 'status' && method_exists($this, 'getLibStatut')) $value = $this->getLibStatut(3); 6552 elseif ($type == 'date') { 6553 if (!empty($value)) { 6554 $value = dol_print_date($value, 'day'); // We suppose dates without time are always gmt (storage of course + output) 6555 } else { 6556 $value = ''; 6557 } 6558 } elseif ($type == 'datetime' || $type == 'timestamp') { 6559 if (!empty($value)) { 6560 $value = dol_print_date($value, 'dayhour', 'tzuserrel'); 6561 } else { 6562 $value = ''; 6563 } 6564 } elseif ($type == 'duration') { 6565 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; 6566 if (!is_null($value) && $value !== '') { 6567 $value = convertSecondToTime($value, 'allhourmin'); 6568 } 6569 } elseif ($type == 'double' || $type == 'real') { 6570 if (!is_null($value) && $value !== '') { 6571 $value = price($value); 6572 } 6573 } elseif ($type == 'boolean') { 6574 $checked = ''; 6575 if (!empty($value)) { 6576 $checked = ' checked '; 6577 } 6578 $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>'; 6579 } elseif ($type == 'mail') { 6580 $value = dol_print_email($value, 0, 0, 0, 64, 1, 1); 6581 } elseif ($type == 'url') { 6582 $value = dol_print_url($value, '_blank', 32, 1); 6583 } elseif ($type == 'phone') { 6584 $value = dol_print_phone($value, '', 0, 0, '', ' ', 1); 6585 } elseif ($type == 'price') 6586 { 6587 if (!is_null($value) && $value !== '') { 6588 $value = price($value, 0, $langs, 0, 0, -1, $conf->currency); 6589 } 6590 } elseif ($type == 'select') { 6591 $value = $param['options'][$value]; 6592 } elseif ($type == 'sellist') { 6593 $param_list = array_keys($param['options']); 6594 $InfoFieldList = explode(":", $param_list[0]); 6595 6596 $selectkey = "rowid"; 6597 $keyList = 'rowid'; 6598 6599 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) { 6600 $selectkey = $InfoFieldList[2]; 6601 $keyList = $InfoFieldList[2].' as rowid'; 6602 } 6603 6604 $fields_label = explode('|', $InfoFieldList[1]); 6605 if (is_array($fields_label)) { 6606 $keyList .= ', '; 6607 $keyList .= implode(', ', $fields_label); 6608 } 6609 6610 $sql = 'SELECT '.$keyList; 6611 $sql .= ' FROM '.MAIN_DB_PREFIX.$InfoFieldList[0]; 6612 if (strpos($InfoFieldList[4], 'extra') !== false) 6613 { 6614 $sql .= ' as main'; 6615 } 6616 if ($selectkey == 'rowid' && empty($value)) { 6617 $sql .= " WHERE ".$selectkey."=0"; 6618 } elseif ($selectkey == 'rowid') { 6619 $sql .= " WHERE ".$selectkey."=".$this->db->escape($value); 6620 } else { 6621 $sql .= " WHERE ".$selectkey."='".$this->db->escape($value)."'"; 6622 } 6623 6624 //$sql.= ' AND entity = '.$conf->entity; 6625 6626 dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG); 6627 $resql = $this->db->query($sql); 6628 if ($resql) 6629 { 6630 $value = ''; // value was used, so now we reste it to use it to build final output 6631 6632 $obj = $this->db->fetch_object($resql); 6633 6634 // Several field into label (eq table:code|libelle:rowid) 6635 $fields_label = explode('|', $InfoFieldList[1]); 6636 6637 if (is_array($fields_label) && count($fields_label) > 1) 6638 { 6639 foreach ($fields_label as $field_toshow) 6640 { 6641 $translabel = ''; 6642 if (!empty($obj->$field_toshow)) { 6643 $translabel = $langs->trans($obj->$field_toshow); 6644 } 6645 if ($translabel != $field_toshow) { 6646 $value .= dol_trunc($translabel, 18).' '; 6647 } else { 6648 $value .= $obj->$field_toshow.' '; 6649 } 6650 } 6651 } else { 6652 $translabel = ''; 6653 if (!empty($obj->{$InfoFieldList[1]})) { 6654 $translabel = $langs->trans($obj->{$InfoFieldList[1]}); 6655 } 6656 if ($translabel != $obj->{$InfoFieldList[1]}) { 6657 $value = dol_trunc($translabel, 18); 6658 } else { 6659 $value = $obj->{$InfoFieldList[1]}; 6660 } 6661 } 6662 } else dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING); 6663 } elseif ($type == 'radio') { 6664 $value = $param['options'][$value]; 6665 } elseif ($type == 'checkbox') { 6666 $value_arr = explode(',', $value); 6667 $value = ''; 6668 if (is_array($value_arr) && count($value_arr) > 0) 6669 { 6670 $toprint = array(); 6671 foreach ($value_arr as $keyval=>$valueval) { 6672 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$param['options'][$valueval].'</li>'; 6673 } 6674 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>'; 6675 } 6676 } elseif ($type == 'chkbxlst') { 6677 $value_arr = explode(',', $value); 6678 6679 $param_list = array_keys($param['options']); 6680 $InfoFieldList = explode(":", $param_list[0]); 6681 6682 $selectkey = "rowid"; 6683 $keyList = 'rowid'; 6684 6685 if (count($InfoFieldList) >= 3) { 6686 $selectkey = $InfoFieldList[2]; 6687 $keyList = $InfoFieldList[2].' as rowid'; 6688 } 6689 6690 $fields_label = explode('|', $InfoFieldList[1]); 6691 if (is_array($fields_label)) { 6692 $keyList .= ', '; 6693 $keyList .= implode(', ', $fields_label); 6694 } 6695 6696 $sql = 'SELECT '.$keyList; 6697 $sql .= ' FROM '.MAIN_DB_PREFIX.$InfoFieldList[0]; 6698 if (strpos($InfoFieldList[4], 'extra') !== false) { 6699 $sql .= ' as main'; 6700 } 6701 // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'"; 6702 // $sql.= ' AND entity = '.$conf->entity; 6703 6704 dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG); 6705 $resql = $this->db->query($sql); 6706 if ($resql) { 6707 $value = ''; // value was used, so now we reste it to use it to build final output 6708 $toprint = array(); 6709 while ($obj = $this->db->fetch_object($resql)) { 6710 // Several field into label (eq table:code|libelle:rowid) 6711 $fields_label = explode('|', $InfoFieldList[1]); 6712 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) { 6713 if (is_array($fields_label) && count($fields_label) > 1) { 6714 foreach ($fields_label as $field_toshow) { 6715 $translabel = ''; 6716 if (!empty($obj->$field_toshow)) { 6717 $translabel = $langs->trans($obj->$field_toshow); 6718 } 6719 if ($translabel != $field_toshow) { 6720 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.dol_trunc($translabel, 18).'</li>'; 6721 } else { 6722 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$obj->$field_toshow.'</li>'; 6723 } 6724 } 6725 } else { 6726 $translabel = ''; 6727 if (!empty($obj->{$InfoFieldList[1]})) { 6728 $translabel = $langs->trans($obj->{$InfoFieldList[1]}); 6729 } 6730 if ($translabel != $obj->{$InfoFieldList[1]}) { 6731 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.dol_trunc($translabel, 18).'</li>'; 6732 } else { 6733 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$obj->{$InfoFieldList[1]}.'</li>'; 6734 } 6735 } 6736 } 6737 } 6738 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>'; 6739 } else { 6740 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING); 6741 } 6742 } elseif ($type == 'link') { 6743 $out = ''; 6744 6745 // only if something to display (perf) 6746 if ($value) 6747 { 6748 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath' 6749 6750 $InfoFieldList = explode(":", $param_list[0]); 6751 $classname = $InfoFieldList[0]; 6752 $classpath = $InfoFieldList[1]; 6753 $getnomurlparam = (empty($InfoFieldList[2]) ? 3 : $InfoFieldList[2]); 6754 if (!empty($classpath)) 6755 { 6756 dol_include_once($InfoFieldList[1]); 6757 if ($classname && class_exists($classname)) 6758 { 6759 $object = new $classname($this->db); 6760 $object->fetch($value); 6761 $value = $object->getNomUrl($getnomurlparam); 6762 } 6763 } else { 6764 dol_syslog('Error bad setup of extrafield', LOG_WARNING); 6765 return 'Error bad setup of extrafield'; 6766 } 6767 } else $value = ''; 6768 } elseif (preg_match('/^(text|html)/', $type)) { 6769 $value = dol_htmlentitiesbr($value); 6770 } elseif ($type == 'password') { 6771 $value = preg_replace('/./i', '*', $value); 6772 } elseif ($type == 'array') { 6773 $value = implode('<br>', $value); 6774 } 6775 6776 //print $type.'-'.$size.'-'.$value; 6777 $out = $value; 6778 6779 return $out; 6780 } 6781 6782 6783 /** 6784 * Function to show lines of extrafields with output datas. 6785 * This function is responsible to output the <tr> and <td> according to correct number of columns received into $params['colspan'] 6786 * 6787 * @param Extrafields $extrafields Extrafield Object 6788 * @param string $mode Show output ('view') or input ('create' or 'edit') for extrafield 6789 * @param array $params Optional parameters. Example: array('style'=>'class="oddeven"', 'colspan'=>$colspan) 6790 * @param string $keysuffix Suffix string to add after name and id of field (can be used to avoid duplicate names) 6791 * @param string $keyprefix Prefix string to add before name and id of field (can be used to avoid duplicate names) 6792 * @param string $onetrtd All fields in same tr td. Used by objectline_create.tpl.php for example. 6793 * @return string 6794 */ 6795 public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = 0) 6796 { 6797 global $db, $conf, $langs, $action, $form, $hookmanager; 6798 6799 if (!is_object($form)) $form = new Form($db); 6800 6801 $out = ''; 6802 6803 $parameters = array(); 6804 $reshook = $hookmanager->executeHooks('showOptionals', $parameters, $this, $action); // Note that $action and $object may have been modified by hook 6805 if (empty($reshook)) 6806 { 6807 if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label']) > 0) 6808 { 6809 $out .= "\n"; 6810 $out .= '<!-- showOptionals --> '; 6811 $out .= "\n"; 6812 6813 $extrafields_collapse_num = ''; 6814 $e = 0; 6815 foreach ($extrafields->attributes[$this->table_element]['label'] as $key=>$label) 6816 { 6817 // Show only the key field in params 6818 if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) continue; 6819 6820 // Test on 'enabled' ('enabled' is different than 'list' = 'visibility') 6821 $enabled = 1; 6822 if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) 6823 { 6824 $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1); 6825 } 6826 if (empty($enabled)) continue; 6827 6828 $visibility = 1; 6829 if ($visibility && isset($extrafields->attributes[$this->table_element]['list'][$key])) 6830 { 6831 $visibility = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1); 6832 } 6833 6834 $perms = 1; 6835 if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) 6836 { 6837 $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1); 6838 } 6839 6840 if (($mode == 'create') && abs($visibility) != 1 && abs($visibility) != 3) continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list 6841 elseif (($mode == 'edit') && abs($visibility) != 1 && abs($visibility) != 3 && abs($visibility) != 4) continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list and <> 4 = not visible at the creation 6842 elseif ($mode == 'view' && empty($visibility)) continue; 6843 if (empty($perms)) continue; 6844 // Load language if required 6845 if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) { 6846 $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]); 6847 } 6848 6849 $colspan = ''; 6850 if (is_array($params) && count($params) > 0) { 6851 if (array_key_exists('cols', $params)) { 6852 $colspan = $params['cols']; 6853 } elseif (array_key_exists('colspan', $params)) { // For backward compatibility. Use cols instead now. 6854 $reg = array(); 6855 if (preg_match('/colspan="(\d+)"/', $params['colspan'], $reg)) { 6856 $colspan = $reg[1]; 6857 } else { 6858 $colspan = $params['colspan']; 6859 } 6860 } 6861 } 6862 6863 switch ($mode) { 6864 case "view": 6865 $value = $this->array_options["options_".$key.$keysuffix]; // Value may be clean or formated later 6866 break; 6867 case "create": 6868 case "edit": 6869 // We get the value of property found with GETPOST so it takes into account: 6870 // default values overwrite, restore back to list link, ... (but not 'default value in database' of field) 6871 $check = 'alphanohtml'; 6872 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text'))) { 6873 $check = 'restricthtml'; 6874 } 6875 $getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, $check, 3); // GETPOST can get value from GET, POST or setup of default values overwrite. 6876 // GETPOST("options_" . $key) can be 'abc' or array(0=>'abc') 6877 if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)) 6878 { 6879 if (is_array($getposttemp)) { 6880 // $getposttemp is an array but following code expects a comma separated string 6881 $value = implode(",", $getposttemp); 6882 } else { 6883 $value = $getposttemp; 6884 } 6885 } else { 6886 $value = $this->array_options["options_".$key]; // No GET, no POST, no default value, so we take value of object. 6887 } 6888 //var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value); 6889 break; 6890 } 6891 6892 // Output value of the current field 6893 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') 6894 { 6895 $extrafields_collapse_num = ''; 6896 $extrafield_param = $extrafields->attributes[$this->table_element]['param'][$key]; 6897 if (!empty($extrafield_param) && is_array($extrafield_param)) { 6898 $extrafield_param_list = array_keys($extrafield_param['options']); 6899 6900 if (count($extrafield_param_list) > 0) { 6901 $extrafield_collapse_display_value = intval($extrafield_param_list[0]); 6902 6903 if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) { 6904 $extrafields_collapse_num = $extrafields->attributes[$this->table_element]['pos'][$key]; 6905 } 6906 } 6907 } 6908 6909 $out .= $extrafields->showSeparator($key, $this, ($colspan + 1)); 6910 } else { 6911 $class = (!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : ''); 6912 $csstyle = ''; 6913 if (is_array($params) && count($params) > 0) { 6914 if (array_key_exists('class', $params)) { 6915 $class .= $params['class'].' '; 6916 } 6917 if (array_key_exists('style', $params)) { 6918 $csstyle = $params['style']; 6919 } 6920 } 6921 6922 // add html5 elements 6923 $domData = ' data-element="extrafield"'; 6924 $domData .= ' data-targetelement="'.$this->element.'"'; 6925 $domData .= ' data-targetid="'.$this->id.'"'; 6926 6927 $html_id = (empty($this->id) ? '' : 'extrarow-'.$this->element.'_'.$key.'_'.$this->id); 6928 6929 if (!empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0) { $colspan = '0'; } 6930 6931 if ($action == 'selectlines') { $colspan++; } 6932 6933 // Convert date into timestamp format (value in memory must be a timestamp) 6934 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date'))) 6935 { 6936 $datenotinstring = $this->array_options['options_'.$key]; 6937 if (!is_numeric($this->array_options['options_'.$key])) // For backward compatibility 6938 { 6939 $datenotinstring = $this->db->jdate($datenotinstring); 6940 } 6941 $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)) ? dol_mktime(12, 0, 0, GETPOST($keyprefix.'options_'.$key.$keysuffix."month", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."day", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."year", 'int', 3)) : $datenotinstring; 6942 } 6943 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('datetime'))) 6944 { 6945 $datenotinstring = $this->array_options['options_'.$key]; 6946 if (!is_numeric($this->array_options['options_'.$key])) // For backward compatibility 6947 { 6948 $datenotinstring = $this->db->jdate($datenotinstring); 6949 } 6950 $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)) ? dol_mktime(GETPOST($keyprefix.'options_'.$key.$keysuffix."hour", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."min", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."sec", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."month", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."day", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."year", 'int', 3), 'tzuserrel') : $datenotinstring; 6951 } 6952 // Convert float submited string into real php numeric (value in memory must be a php numeric) 6953 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price', 'double'))) 6954 { 6955 $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) ? price2num($value) : $this->array_options['options_'.$key]; 6956 } 6957 6958 // HTML, text, select, integer and varchar: take into account default value in database if in create mode 6959 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text', 'varchar', 'select', 'int'))) 6960 { 6961 if ($action == 'create') $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) ? $value : $extrafields->attributes[$this->table_element]['default'][$key]; 6962 } 6963 6964 $labeltoshow = $langs->trans($label); 6965 $helptoshow = $langs->trans($extrafields->attributes[$this->table_element]['help'][$key]); 6966 6967 $out .= '<tr '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="'.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$extrafields_collapse_num.'" '.$domData.' >'; 6968 if (!empty($conf->global->MAIN_VIEW_LINE_NUMBER) && $action == 'view') { 6969 $out .= '<td></td>'; 6970 } 6971 $out .= '<td class="wordbreak'; 6972 //$out .= "titlefield"; 6973 //if (GETPOST('action', 'restricthtml') == 'create') $out.='create'; 6974 // BUG #11554 : For public page, use red dot for required fields, instead of bold label 6975 $tpl_context = isset($params["tpl_context"]) ? $params["tpl_context"] : "none"; 6976 if ($tpl_context == "public") { // Public page : red dot instead of fieldrequired characters 6977 $out .= '">'; 6978 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) $out .= $form->textwithpicto($labeltoshow, $helptoshow); 6979 else $out .= $labeltoshow; 6980 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) $out .= ' <font color="red">*</font>'; 6981 } else { 6982 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) $out .= ' fieldrequired'; 6983 $out .= '">'; 6984 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) $out .= $form->textwithpicto($labeltoshow, $helptoshow); 6985 else $out .= $labeltoshow; 6986 } 6987 $out .= '</td>'; 6988 6989 $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : ''; 6990 6991 $out .= '<td '.($html_id ? 'id="'.$html_id.'" ' : '').'class="'.$this->element.'_extras_'.$key.'" '.($colspan ? ' colspan="'.$colspan.'"' : '').'>'; 6992 6993 switch ($mode) { 6994 case "view": 6995 $out .= $extrafields->showOutputField($key, $value); 6996 break; 6997 case "create": 6998 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element); 6999 break; 7000 case "edit": 7001 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element); 7002 break; 7003 } 7004 7005 $out .= '</td>'; 7006 7007 /*for($ii = 0; $ii < ($colspan - 1); $ii++) 7008 { 7009 $out .='<td class="'.$this->element.'_extras_'.$key.'"></td>'; 7010 }*/ 7011 7012 if (!empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) $out .= '</tr>'; 7013 else $out .= '</tr>'; 7014 $e++; 7015 } 7016 } 7017 $out .= "\n"; 7018 // Add code to manage list depending on others 7019 if (!empty($conf->use_javascript_ajax)) { 7020 $out .= $this->getJSListDependancies(); 7021 } 7022 7023 $out .= '<!-- /showOptionals --> '."\n"; 7024 } 7025 } 7026 7027 $out .= $hookmanager->resPrint; 7028 7029 return $out; 7030 } 7031 7032 /** 7033 * @param string $type Type for prefix 7034 * @return string Javacript code to manage dependency 7035 */ 7036 public function getJSListDependancies($type = '_extra') 7037 { 7038 $out .= ' 7039 <script> 7040 jQuery(document).ready(function() { 7041 function showOptions'.$type.'(child_list, parent_list, orig_select) 7042 { 7043 var val = $("select[name=\""+parent_list+"\"]").val(); 7044 var parentVal = parent_list + ":" + val; 7045 if(val > 0) { 7046 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone(); 7047 $("select[name=\""+child_list+"\"] option[parent]").remove(); 7048 $("select[name=\""+child_list+"\"]").append(options); 7049 } else { 7050 var options = orig_select.find("option[parent]").clone(); 7051 $("select[name=\""+child_list+"\"] option[parent]").remove(); 7052 $("select[name=\""+child_list+"\"]").append(options); 7053 } 7054 } 7055 function setListDependencies'.$type.'() { 7056 jQuery("select option[parent]").parent().each(function() { 7057 var orig_select = {}; 7058 var child_list = $(this).attr("name"); 7059 orig_select[child_list] = $(this).clone(); 7060 var parent = $(this).find("option[parent]:first").attr("parent"); 7061 var infos = parent.split(":"); 7062 var parent_list = infos[0]; 7063 $("select[name=\""+parent_list+"\"]").change(function() { 7064 showOptions'.$type.'(child_list, parent_list, orig_select[child_list]); 7065 }); 7066 }); 7067 } 7068 7069 setListDependencies'.$type.'(); 7070 }); 7071 </script>'."\n"; 7072 return $out; 7073 } 7074 7075 /** 7076 * Returns the rights used for this class 7077 * @return stdClass 7078 */ 7079 public function getRights() 7080 { 7081 global $user; 7082 7083 $element = $this->element; 7084 if ($element == 'facturerec') $element = 'facture'; 7085 7086 return $user->rights->{$element}; 7087 } 7088 7089 /** 7090 * Function used to replace a thirdparty id with another one. 7091 * This function is meant to be called from replaceThirdparty with the appropiate tables 7092 * Column name fk_soc MUST be used to identify thirdparties 7093 * 7094 * @param DoliDB $db Database handler 7095 * @param int $origin_id Old thirdparty id (the thirdparty to delete) 7096 * @param int $dest_id New thirdparty id (the thirdparty that will received element of the other) 7097 * @param string[] $tables Tables that need to be changed 7098 * @param int $ignoreerrors Ignore errors. Return true even if errors. We need this when replacement can fails like for categories (categorie of old thirdparty may already exists on new one) 7099 * @return bool True if success, False if error 7100 */ 7101 public static function commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors = 0) 7102 { 7103 foreach ($tables as $table) 7104 { 7105 $sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_soc = '.$dest_id.' WHERE fk_soc = '.$origin_id; 7106 7107 if (!$db->query($sql)) 7108 { 7109 if ($ignoreerrors) return true; // TODO Not enough. If there is A-B on kept thirdarty and B-C on old one, we must get A-B-C after merge. Not A-B. 7110 //$this->errors = $db->lasterror(); 7111 return false; 7112 } 7113 } 7114 7115 return true; 7116 } 7117 7118 /** 7119 * Get buy price to use for margin calculation. This function is called when buy price is unknown. 7120 * Set buy price = sell price if ForceBuyingPriceIfNull configured, 7121 * else if calculation MARGIN_TYPE = 'costprice' and costprice is defined, use costprice as buyprice 7122 * else if calculation MARGIN_TYPE = 'pmp' and pmp is calculated, use pmp as buyprice 7123 * else set min buy price as buy price 7124 * 7125 * @param float $unitPrice Product unit price 7126 * @param float $discountPercent Line discount percent 7127 * @param int $fk_product Product id 7128 * @return float <0 if KO, buyprice if OK 7129 */ 7130 public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0) 7131 { 7132 global $conf; 7133 7134 $buyPrice = 0; 7135 7136 if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull > 0)) // In most cases, test here is false 7137 { 7138 $buyPrice = $unitPrice * (1 - $discountPercent / 100); 7139 } else { 7140 // Get cost price for margin calculation 7141 if (!empty($fk_product)) 7142 { 7143 if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice') 7144 { 7145 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; 7146 $product = new Product($this->db); 7147 $result = $product->fetch($fk_product); 7148 if ($result <= 0) 7149 { 7150 $this->errors[] = 'ErrorProductIdDoesNotExists'; 7151 return -1; 7152 } 7153 if ($product->cost_price > 0) 7154 { 7155 $buyPrice = $product->cost_price; 7156 } elseif ($product->pmp > 0) 7157 { 7158 $buyPrice = $product->pmp; 7159 } 7160 } elseif (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp') 7161 { 7162 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php'; 7163 $product = new Product($this->db); 7164 $result = $product->fetch($fk_product); 7165 if ($result <= 0) 7166 { 7167 $this->errors[] = 'ErrorProductIdDoesNotExists'; 7168 return -1; 7169 } 7170 if ($product->pmp > 0) 7171 { 7172 $buyPrice = $product->pmp; 7173 } 7174 } 7175 7176 if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1', 'pmp', 'costprice'))) 7177 { 7178 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php'; 7179 $productFournisseur = new ProductFournisseur($this->db); 7180 if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0) 7181 { 7182 $buyPrice = $productFournisseur->fourn_unitprice; 7183 } elseif ($result < 0) 7184 { 7185 $this->errors[] = $productFournisseur->error; 7186 return -2; 7187 } 7188 } 7189 } 7190 } 7191 return $buyPrice; 7192 } 7193 7194 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps 7195 /** 7196 * Show photos of an object (nbmax maximum), into several columns 7197 * 7198 * @param string $modulepart 'product', 'ticket', ... 7199 * @param string $sdir Directory to scan (full absolute path) 7200 * @param int $size 0=original size, 1='small' use thumbnail if possible 7201 * @param int $nbmax Nombre maximum de photos (0=pas de max) 7202 * @param int $nbbyrow Number of image per line or -1 to use div. Used only if size=1. 7203 * @param int $showfilename 1=Show filename 7204 * @param int $showaction 1=Show icon with action links (resize, delete) 7205 * @param int $maxHeight Max height of original image when size='small' (so we can use original even if small requested). If 0, always use 'small' thumb image. 7206 * @param int $maxWidth Max width of original image when size='small' 7207 * @param int $nolink Do not add a href link to view enlarged imaged into a new tab 7208 * @param int $notitle Do not add title tag on image 7209 * @param int $usesharelink Use the public shared link of image (if not available, the 'nophoto' image will be shown instead) 7210 * @return string Html code to show photo. Number of photos shown is saved in this->nbphoto 7211 */ 7212 public function show_photos($modulepart, $sdir, $size = 0, $nbmax = 0, $nbbyrow = 5, $showfilename = 0, $showaction = 0, $maxHeight = 120, $maxWidth = 160, $nolink = 0, $notitle = 0, $usesharelink = 0) 7213 { 7214 // phpcs:enable 7215 global $conf, $user, $langs; 7216 7217 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; 7218 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php'; 7219 7220 $sortfield = 'position_name'; 7221 $sortorder = 'asc'; 7222 7223 $dir = $sdir.'/'; 7224 $pdir = '/'; 7225 7226 $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart); 7227 $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart); 7228 7229 // For backward compatibility 7230 if ($modulepart == 'product') { 7231 if (!empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) { 7232 $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/"; 7233 $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/"; 7234 } 7235 } 7236 7237 // Defined relative dir to DOL_DATA_ROOT 7238 $relativedir = ''; 7239 if ($dir) { 7240 $relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir); 7241 $relativedir = preg_replace('/^[\\/]/', '', $relativedir); 7242 $relativedir = preg_replace('/[\\/]$/', '', $relativedir); 7243 } 7244 7245 $dirthumb = $dir.'thumbs/'; 7246 $pdirthumb = $pdir.'thumbs/'; 7247 7248 $return = '<!-- Photo -->'."\n"; 7249 $nbphoto = 0; 7250 7251 $filearray = dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ?SORT_DESC:SORT_ASC), 1); 7252 7253 /*if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) // For backward compatiblity, we scan also old dirs 7254 { 7255 $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1); 7256 $filearray=array_merge($filearray, $filearrayold); 7257 }*/ 7258 7259 completeFileArrayWithDatabaseInfo($filearray, $relativedir); 7260 7261 if (count($filearray)) { 7262 if ($sortfield && $sortorder) { 7263 $filearray = dol_sort_array($filearray, $sortfield, $sortorder); 7264 } 7265 7266 foreach ($filearray as $key => $val) { 7267 $photo = ''; 7268 $file = $val['name']; 7269 7270 //if (! utf8_check($file)) $file=utf8_encode($file); // To be sure file is stored in UTF8 in memory 7271 7272 //if (dol_is_file($dir.$file) && image_format_supported($file) >= 0) 7273 if (image_format_supported($file) >= 0) { 7274 $nbphoto++; 7275 $photo = $file; 7276 $viewfilename = $file; 7277 7278 if ($size == 1 || $size == 'small') { // Format vignette 7279 // Find name of thumb file 7280 $photo_vignette = basename(getImageFileNameForSize($dir.$file, '_small')); 7281 if (!dol_is_file($dirthumb.$photo_vignette)) $photo_vignette = ''; 7282 7283 // Get filesize of original file 7284 $imgarray = dol_getImageSize($dir.$photo); 7285 7286 if ($nbbyrow > 0) 7287 { 7288 if ($nbphoto == 1) $return .= '<table class="valigntop center centpercent" style="border: 0; padding: 2px; border-spacing: 2px; border-collapse: separate;">'; 7289 7290 if ($nbphoto % $nbbyrow == 1) $return .= '<tr class="center valignmiddle" style="border: 1px">'; 7291 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%" class="photo">'; 7292 } elseif ($nbbyrow < 0) $return .= '<div class="inline-block">'; 7293 7294 $return .= "\n"; 7295 7296 $relativefile = preg_replace('/^\//', '', $pdir.$photo); 7297 if (empty($nolink)) 7298 { 7299 $urladvanced = getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity); 7300 if ($urladvanced) $return .= '<a href="'.$urladvanced.'">'; 7301 else $return .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank">'; 7302 } 7303 7304 // Show image (width height=$maxHeight) 7305 // Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine 7306 $alt = $langs->transnoentitiesnoconv('File').': '.$relativefile; 7307 $alt .= ' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height']; 7308 if ($notitle) $alt = ''; 7309 7310 if ($usesharelink) { 7311 if ($val['share']) { 7312 if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight) { 7313 $return .= '<!-- Show original file (thumb not yet available with shared links) -->'; 7314 $return .= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">'; 7315 } else { 7316 $return .= '<!-- Show original file -->'; 7317 $return .= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">'; 7318 } 7319 } else { 7320 $return .= '<!-- Show nophoto file (because file is not shared) -->'; 7321 $return .= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">'; 7322 } 7323 } else { 7324 if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight) { 7325 $return .= '<!-- Show thumb -->'; 7326 $return .= '<img class="photo photowithmargin maxwidth150onsmartphone" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdirthumb.$photo_vignette).'" title="'.dol_escape_htmltag($alt).'">'; 7327 } else { 7328 $return .= '<!-- Show original file -->'; 7329 $return .= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" title="'.dol_escape_htmltag($alt).'">'; 7330 } 7331 } 7332 7333 if (empty($nolink)) $return .= '</a>'; 7334 $return .= "\n"; 7335 7336 if ($showfilename) $return .= '<br>'.$viewfilename; 7337 if ($showaction) 7338 { 7339 $return .= '<br>'; 7340 // On propose la generation de la vignette si elle n'existe pas et si la taille est superieure aux limites 7341 if ($photo_vignette && (image_format_supported($photo) > 0) && ($this->imgWidth > $maxWidth || $this->imgHeight > $maxHeight)) 7342 { 7343 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=addthumb&file='.urlencode($pdir.$viewfilename).'">'.img_picto($langs->trans('GenerateThumb'), 'refresh').' </a>'; 7344 } 7345 // Special cas for product 7346 if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer)) 7347 { 7348 // Link to resize 7349 $return .= '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?modulepart='.urlencode('produit|service').'&id='.$this->id.'&file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"), 'resize', '').'</a> '; 7350 7351 // Link to delete 7352 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">'; 7353 $return .= img_delete().'</a>'; 7354 } 7355 } 7356 $return .= "\n"; 7357 7358 if ($nbbyrow > 0) 7359 { 7360 $return .= '</td>'; 7361 if (($nbphoto % $nbbyrow) == 0) $return .= '</tr>'; 7362 } elseif ($nbbyrow < 0) $return .= '</div>'; 7363 } 7364 7365 if (empty($size)) { // Format origine 7366 $return .= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">'; 7367 7368 if ($showfilename) $return .= '<br>'.$viewfilename; 7369 if ($showaction) 7370 { 7371 // Special case for product 7372 if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer)) 7373 { 7374 // Link to resize 7375 $return .= '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?modulepart='.urlencode('produit|service').'&id='.$this->id.'&file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"), 'resize', '').'</a> '; 7376 7377 // Link to delete 7378 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">'; 7379 $return .= img_delete().'</a>'; 7380 } 7381 } 7382 } 7383 7384 // On continue ou on arrete de boucler ? 7385 if ($nbmax && $nbphoto >= $nbmax) break; 7386 } 7387 } 7388 7389 if ($size == 1 || $size == 'small') 7390 { 7391 if ($nbbyrow > 0) 7392 { 7393 // Ferme tableau 7394 while ($nbphoto % $nbbyrow) 7395 { 7396 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%"> </td>'; 7397 $nbphoto++; 7398 } 7399 7400 if ($nbphoto) $return .= '</table>'; 7401 } 7402 } 7403 } 7404 7405 $this->nbphoto = $nbphoto; 7406 7407 return $return; 7408 } 7409 7410 7411 /** 7412 * Function test if type is array 7413 * 7414 * @param array $info content informations of field 7415 * @return bool true if array 7416 */ 7417 protected function isArray($info) 7418 { 7419 if (is_array($info)) { 7420 if (isset($info['type']) && $info['type'] == 'array') return true; 7421 else return false; 7422 } 7423 return false; 7424 } 7425 7426 /** 7427 * Function test if type is date 7428 * 7429 * @param array $info content informations of field 7430 * @return bool true if date 7431 */ 7432 public function isDate($info) 7433 { 7434 if (isset($info['type']) && ($info['type'] == 'date' || $info['type'] == 'datetime' || $info['type'] == 'timestamp')) return true; 7435 return false; 7436 } 7437 7438 /** 7439 * Function test if type is duration 7440 * 7441 * @param array $info content informations of field 7442 * @return bool true if field of type duration 7443 */ 7444 public function isDuration($info) 7445 { 7446 if (is_array($info)) { 7447 if (isset($info['type']) && ($info['type'] == 'duration')) return true; 7448 else return false; 7449 } else return false; 7450 } 7451 7452 /** 7453 * Function test if type is integer 7454 * 7455 * @param array $info content informations of field 7456 * @return bool true if integer 7457 */ 7458 public function isInt($info) 7459 { 7460 if (is_array($info)) { 7461 if (isset($info['type']) && ($info['type'] == 'int' || preg_match('/^integer/i', $info['type']))) return true; 7462 else return false; 7463 } else return false; 7464 } 7465 7466 /** 7467 * Function test if type is float 7468 * 7469 * @param array $info content informations of field 7470 * @return bool true if float 7471 */ 7472 public function isFloat($info) 7473 { 7474 if (is_array($info)) { 7475 if (isset($info['type']) && (preg_match('/^(double|real|price)/i', $info['type']))) return true; 7476 else return false; 7477 } 7478 return false; 7479 } 7480 7481 /** 7482 * Function test if type is text 7483 * 7484 * @param array $info content informations of field 7485 * @return bool true if type text 7486 */ 7487 public function isText($info) 7488 { 7489 if (is_array($info)) { 7490 if (isset($info['type']) && $info['type'] == 'text') return true; 7491 else return false; 7492 } 7493 return false; 7494 } 7495 7496 /** 7497 * Function test if field can be null 7498 * 7499 * @param array $info content informations of field 7500 * @return bool true if it can be null 7501 */ 7502 protected function canBeNull($info) 7503 { 7504 if (is_array($info)) { 7505 if (isset($info['notnull']) && $info['notnull'] != '1') return true; 7506 else return false; 7507 } 7508 return true; 7509 } 7510 7511 /** 7512 * Function test if field is forced to null if zero or empty 7513 * 7514 * @param array $info content informations of field 7515 * @return bool true if forced to null 7516 */ 7517 protected function isForcedToNullIfZero($info) 7518 { 7519 if (is_array($info)) { 7520 if (isset($info['notnull']) && $info['notnull'] == '-1') return true; 7521 else return false; 7522 } 7523 return false; 7524 } 7525 7526 /** 7527 * Function test if is indexed 7528 * 7529 * @param array $info content informations of field 7530 * @return bool 7531 */ 7532 protected function isIndex($info) 7533 { 7534 if (is_array($info)) { 7535 if (isset($info['index']) && $info['index'] == true) return true; 7536 else return false; 7537 } 7538 return false; 7539 } 7540 7541 7542 /** 7543 * Function to prepare a part of the query for insert. 7544 * Note $this->${field} are set by the page that make the createCommon or the updateCommon. 7545 * $this->${field} should be a clean value. The page can run 7546 * 7547 * @return array 7548 */ 7549 protected function setSaveQuery() 7550 { 7551 global $conf; 7552 7553 $queryarray = array(); 7554 foreach ($this->fields as $field => $info) // Loop on definition of fields 7555 { 7556 // Depending on field type ('datetime', ...) 7557 if ($this->isDate($info)) 7558 { 7559 if (empty($this->{$field})) { 7560 $queryarray[$field] = null; 7561 } else { 7562 $queryarray[$field] = $this->db->idate($this->{$field}); 7563 } 7564 } elseif ($this->isArray($info)) 7565 { 7566 if (!empty($this->{$field})) { 7567 if (!is_array($this->{$field})) { 7568 $this->{$field} = array($this->{$field}); 7569 } 7570 $queryarray[$field] = serialize($this->{$field}); 7571 } else { 7572 $queryarray[$field] = null; 7573 } 7574 } elseif ($this->isDuration($info)) 7575 { 7576 // $this->{$field} may be null, '', 0, '0', 123, '123' 7577 if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) { 7578 if (!isset($this->{$field})) { 7579 $queryarray[$field] = 0; 7580 } else { 7581 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1 7582 } 7583 } 7584 else $queryarray[$field] = null; 7585 } elseif ($this->isInt($info) || $this->isFloat($info)) 7586 { 7587 if ($field == 'entity' && is_null($this->{$field})) $queryarray[$field] = $conf->entity; 7588 else { 7589 // $this->{$field} may be null, '', 0, '0', 123, '123' 7590 if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) { 7591 if (!isset($this->{$field})) { 7592 $queryarray[$field] = 0; 7593 } elseif ($this->isInt($info)) { 7594 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1 7595 } elseif ($this->isFloat($info)) { 7596 $queryarray[$field] = (double) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1 7597 } 7598 } else $queryarray[$field] = null; 7599 } 7600 } else { 7601 $queryarray[$field] = $this->{$field}; 7602 } 7603 7604 if ($info['type'] == 'timestamp' && empty($queryarray[$field])) unset($queryarray[$field]); 7605 if (!empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) $queryarray[$field] = null; // May force 0 to null 7606 } 7607 7608 return $queryarray; 7609 } 7610 7611 /** 7612 * Function to load data from a SQL pointer into properties of current object $this 7613 * 7614 * @param stdClass $obj Contain data of object from database 7615 * @return void 7616 */ 7617 public function setVarsFromFetchObj(&$obj) 7618 { 7619 global $db; 7620 7621 foreach ($this->fields as $field => $info) 7622 { 7623 if ($this->isDate($info)) { 7624 if (is_null($obj->{$field}) || $obj->{$field} === '' || $obj->{$field} === '0000-00-00 00:00:00' || $obj->{$field} === '1000-01-01 00:00:00') $this->{$field} = ''; 7625 else $this->{$field} = $db->jdate($obj->{$field}); 7626 } elseif ($this->isArray($info)) 7627 { 7628 if (!empty($obj->{$field})) { 7629 $this->{$field} = @unserialize($obj->{$field}); 7630 // Hack for data not in UTF8 7631 if ($this->{$field } === false) @unserialize(utf8_decode($obj->{$field})); 7632 } else { 7633 $this->{$field} = array(); 7634 } 7635 } elseif ($this->isInt($info)) { 7636 if ($field == 'rowid') $this->id = (int) $obj->{$field}; 7637 else { 7638 if ($this->isForcedToNullIfZero($info)) { 7639 if (empty($obj->{$field})) $this->{$field} = null; 7640 else $this->{$field} = (double) $obj->{$field}; 7641 } else { 7642 if (!is_null($obj->{$field}) || (isset($info['notnull']) && $info['notnull'] == 1)) { 7643 $this->{$field} = (int) $obj->{$field}; 7644 } else { 7645 $this->{$field} = null; 7646 } 7647 } 7648 } 7649 } elseif ($this->isFloat($info)) { 7650 if ($this->isForcedToNullIfZero($info)) { 7651 if (empty($obj->{$field})) $this->{$field} = null; 7652 else $this->{$field} = (double) $obj->{$field}; 7653 } else { 7654 if (!is_null($obj->{$field}) || (isset($info['notnull']) && $info['notnull'] == 1)) { 7655 $this->{$field} = (double) $obj->{$field}; 7656 } else { 7657 $this->{$field} = null; 7658 } 7659 } 7660 } else { 7661 $this->{$field} = $obj->{$field}; 7662 } 7663 } 7664 7665 // If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions. 7666 if (!isset($this->fields['ref']) && isset($this->id)) $this->ref = $this->id; 7667 } 7668 7669 /** 7670 * Function to concat keys of fields 7671 * 7672 * @return string 7673 */ 7674 protected function getFieldList() 7675 { 7676 $keys = array_keys($this->fields); 7677 return implode(',', $keys); 7678 } 7679 7680 /** 7681 * Add quote to field value if necessary 7682 * 7683 * @param string|int $value Value to protect 7684 * @param array $fieldsentry Properties of field 7685 * @return string 7686 */ 7687 protected function quote($value, $fieldsentry) 7688 { 7689 if (is_null($value)) return 'NULL'; 7690 elseif (preg_match('/^(int|double|real|price)/i', $fieldsentry['type'])) return $this->db->escape("$value"); 7691 elseif ($fieldsentry['type'] == 'boolean') { 7692 if ($value) return 'true'; 7693 else return 'false'; 7694 } 7695 else return "'".$this->db->escape($value)."'"; 7696 } 7697 7698 7699 /** 7700 * Create object into database 7701 * 7702 * @param User $user User that creates 7703 * @param bool $notrigger false=launch triggers after, true=disable triggers 7704 * @return int <0 if KO, Id of created object if OK 7705 */ 7706 public function createCommon(User $user, $notrigger = false) 7707 { 7708 global $langs; 7709 dol_syslog(get_class($this)."::createCommon create", LOG_DEBUG); 7710 7711 $error = 0; 7712 7713 $now = dol_now(); 7714 7715 $fieldvalues = $this->setSaveQuery(); 7716 7717 if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) $fieldvalues['date_creation'] = $this->db->idate($now); 7718 if (array_key_exists('fk_user_creat', $fieldvalues) && !($fieldvalues['fk_user_creat'] > 0)) $fieldvalues['fk_user_creat'] = $user->id; 7719 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert. 7720 if (array_key_exists('ref', $fieldvalues)) $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data 7721 7722 $keys = array(); 7723 $values = array(); // Array to store string forged for SQL syntax 7724 foreach ($fieldvalues as $k => $v) { 7725 $keys[$k] = $k; 7726 $value = $this->fields[$k]; 7727 $values[$k] = $this->quote($v, $value); // May return string 'NULL' if $value is null 7728 } 7729 7730 // Clean and check mandatory 7731 foreach ($keys as $key) 7732 { 7733 // If field is an implicit foreign key field 7734 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key] = ''; 7735 if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key] = ''; 7736 7737 if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && is_null($this->fields[$key]['default'])) 7738 { 7739 $error++; 7740 $this->errors[] = $langs->trans("ErrorFieldRequired", $this->fields[$key]['label']); 7741 } 7742 7743 // If value is null and there is a default value for field 7744 if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && !is_null($this->fields[$key]['default'])) 7745 { 7746 $values[$key] = $this->fields[$key]['default']; 7747 } 7748 7749 // If field is an implicit foreign key field 7750 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) { 7751 if (isset($this->fields[$key]['default'])) $values[$key] = $this->fields[$key]['default']; 7752 else $values[$key] = 'null'; 7753 } 7754 if (!empty($this->fields[$key]['foreignkey']) && empty($values[$key])) $values[$key] = 'null'; 7755 } 7756 7757 if ($error) return -1; 7758 7759 $this->db->begin(); 7760 7761 if (!$error) 7762 { 7763 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element; 7764 $sql .= ' ('.implode(", ", $keys).')'; 7765 $sql .= ' VALUES ('.implode(", ", $values).')'; 7766 7767 $res = $this->db->query($sql); 7768 if ($res === false) { 7769 $error++; 7770 $this->errors[] = $this->db->lasterror(); 7771 } 7772 } 7773 7774 if (!$error) 7775 { 7776 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element); 7777 } 7778 7779 // If we have a field ref with a default value of (PROV) 7780 if (!$error) 7781 { 7782 if (key_exists('ref', $this->fields) && $this->fields['ref']['notnull'] > 0 && !is_null($this->fields['ref']['default']) && $this->fields['ref']['default'] == '(PROV)') 7783 { 7784 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ref = '(PROV".$this->id.")' WHERE (ref = '(PROV)' OR ref = '') AND rowid = ".$this->id; 7785 $resqlupdate = $this->db->query($sql); 7786 7787 if ($resqlupdate === false) 7788 { 7789 $error++; 7790 $this->errors[] = $this->db->lasterror(); 7791 } else { 7792 $this->ref = '(PROV'.$this->id.')'; 7793 } 7794 } 7795 } 7796 7797 // Create extrafields 7798 if (!$error) 7799 { 7800 $result = $this->insertExtraFields(); 7801 if ($result < 0) $error++; 7802 } 7803 7804 // Create lines 7805 if (!empty($this->table_element_line) && !empty($this->fk_element)) 7806 { 7807 $num = (is_array($this->lines) ? count($this->lines) : 0); 7808 for ($i = 0; $i < $num; $i++) 7809 { 7810 $line = $this->lines[$i]; 7811 7812 $keyforparent = $this->fk_element; 7813 $line->$keyforparent = $this->id; 7814 7815 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array 7816 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object. 7817 if (!is_object($line)) $line = (object) $line; 7818 7819 $result = $line->create($user, 1); 7820 if ($result < 0) 7821 { 7822 $this->error = $this->db->lasterror(); 7823 $this->db->rollback(); 7824 return -1; 7825 } 7826 } 7827 } 7828 7829 // Triggers 7830 if (!$error && !$notrigger) 7831 { 7832 // Call triggers 7833 $result = $this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user); 7834 if ($result < 0) { $error++; } 7835 // End call triggers 7836 } 7837 7838 // Commit or rollback 7839 if ($error) { 7840 $this->db->rollback(); 7841 return -1; 7842 } else { 7843 $this->db->commit(); 7844 return $this->id; 7845 } 7846 } 7847 7848 7849 /** 7850 * Load object in memory from the database 7851 * 7852 * @param int $id Id object 7853 * @param string $ref Ref 7854 * @param string $morewhere More SQL filters (' AND ...') 7855 * @return int <0 if KO, 0 if not found, >0 if OK 7856 */ 7857 public function fetchCommon($id, $ref = null, $morewhere = '') 7858 { 7859 if (empty($id) && empty($ref) && empty($morewhere)) return -1; 7860 7861 $fieldlist = $this->getFieldList(); 7862 if (empty($fieldlist)) return 0; 7863 7864 $sql = 'SELECT '.$fieldlist; 7865 $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element; 7866 7867 if (!empty($id)) $sql .= ' WHERE rowid = '.$id; 7868 elseif (!empty($ref)) $sql .= " WHERE ref = ".$this->quote($ref, $this->fields['ref']); 7869 else $sql .= ' WHERE 1 = 1'; // usage with empty id and empty ref is very rare 7870 if (empty($id) && isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) $sql .= ' AND entity IN ('.getEntity($this->table_element).')'; 7871 if ($morewhere) $sql .= $morewhere; 7872 $sql .= ' LIMIT 1'; // This is a fetch, to be sure to get only one record 7873 7874 $res = $this->db->query($sql); 7875 if ($res) 7876 { 7877 $obj = $this->db->fetch_object($res); 7878 if ($obj) 7879 { 7880 $this->setVarsFromFetchObj($obj); 7881 7882 // Retrieve all extrafield 7883 // fetch optionals attributes and labels 7884 $this->fetch_optionals(); 7885 7886 return $this->id; 7887 } else { 7888 return 0; 7889 } 7890 } else { 7891 $this->error = $this->db->lasterror(); 7892 $this->errors[] = $this->error; 7893 return -1; 7894 } 7895 } 7896 7897 /** 7898 * Load object in memory from the database 7899 * 7900 * @param string $morewhere More SQL filters (' AND ...') 7901 * @return int <0 if KO, 0 if not found, >0 if OK 7902 */ 7903 public function fetchLinesCommon($morewhere = '') 7904 { 7905 $objectlineclassname = get_class($this).'Line'; 7906 if (!class_exists($objectlineclassname)) 7907 { 7908 $this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon'; 7909 return -1; 7910 } 7911 7912 $objectline = new $objectlineclassname($this->db); 7913 7914 $sql = 'SELECT '.$objectline->getFieldList(); 7915 $sql .= ' FROM '.MAIN_DB_PREFIX.$objectline->table_element; 7916 $sql .= ' WHERE fk_'.$this->element.' = '.$this->id; 7917 if ($morewhere) $sql .= $morewhere; 7918 if (isset($objectline->fields['position'])) { 7919 $sql .= $this->db->order('position', 'ASC'); 7920 } 7921 7922 $resql = $this->db->query($sql); 7923 if ($resql) 7924 { 7925 $num_rows = $this->db->num_rows($resql); 7926 $i = 0; 7927 while ($i < $num_rows) 7928 { 7929 $obj = $this->db->fetch_object($resql); 7930 if ($obj) 7931 { 7932 $newline = new $objectlineclassname($this->db); 7933 $newline->setVarsFromFetchObj($obj); 7934 7935 $this->lines[] = $newline; 7936 } 7937 $i++; 7938 } 7939 7940 return 1; 7941 } else { 7942 $this->error = $this->db->lasterror(); 7943 $this->errors[] = $this->error; 7944 return -1; 7945 } 7946 } 7947 7948 /** 7949 * Update object into database 7950 * 7951 * @param User $user User that modifies 7952 * @param bool $notrigger false=launch triggers after, true=disable triggers 7953 * @return int <0 if KO, >0 if OK 7954 */ 7955 public function updateCommon(User $user, $notrigger = false) 7956 { 7957 global $conf, $langs; 7958 dol_syslog(get_class($this)."::updateCommon update", LOG_DEBUG); 7959 7960 $error = 0; 7961 7962 $now = dol_now(); 7963 7964 $fieldvalues = $this->setSaveQuery(); 7965 7966 if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) $fieldvalues['date_modification'] = $this->db->idate($now); 7967 if (array_key_exists('fk_user_modif', $fieldvalues) && !($fieldvalues['fk_user_modif'] > 0)) $fieldvalues['fk_user_modif'] = $user->id; 7968 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into update. 7969 if (array_key_exists('ref', $fieldvalues)) $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data 7970 7971 // Add quotes and escape on fields with type string 7972 $keys = array(); 7973 $values = array(); 7974 $tmp = array(); 7975 foreach ($fieldvalues as $k => $v) { 7976 $keys[$k] = $k; 7977 $value = $this->fields[$k]; 7978 $values[$k] = $this->quote($v, $value); 7979 $tmp[] = $k.'='.$this->quote($v, $this->fields[$k]); 7980 } 7981 7982 // Clean and check mandatory fields 7983 foreach ($keys as $key) 7984 { 7985 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key] = ''; // This is an implicit foreign key field 7986 if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key] = ''; // This is an explicit foreign key field 7987 7988 //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1)); 7989 /* 7990 if ($this->fields[$key]['notnull'] == 1 && empty($values[$key])) 7991 { 7992 $error++; 7993 $this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']); 7994 }*/ 7995 } 7996 7997 $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET '.implode(', ', $tmp).' WHERE rowid='.$this->id; 7998 7999 $this->db->begin(); 8000 if (!$error) 8001 { 8002 $res = $this->db->query($sql); 8003 if ($res === false) 8004 { 8005 $error++; 8006 $this->errors[] = $this->db->lasterror(); 8007 } 8008 } 8009 8010 // Update extrafield 8011 if (!$error) 8012 { 8013 $result = $this->insertExtraFields(); 8014 if ($result < 0) 8015 { 8016 $error++; 8017 } 8018 } 8019 8020 // Triggers 8021 if (!$error && !$notrigger) 8022 { 8023 // Call triggers 8024 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user); 8025 if ($result < 0) { $error++; } //Do also here what you must do to rollback action if trigger fail 8026 // End call triggers 8027 } 8028 8029 // Commit or rollback 8030 if ($error) { 8031 $this->db->rollback(); 8032 return -1; 8033 } else { 8034 $this->db->commit(); 8035 return $this->id; 8036 } 8037 } 8038 8039 /** 8040 * Delete object in database 8041 * 8042 * @param User $user User that deletes 8043 * @param bool $notrigger false=launch triggers after, true=disable triggers 8044 * @param int $forcechilddeletion 0=no, 1=Force deletion of children 8045 * @return int <=0 if KO, >0 if OK 8046 */ 8047 public function deleteCommon(User $user, $notrigger = false, $forcechilddeletion = 0) 8048 { 8049 dol_syslog(get_class($this)."::deleteCommon delete", LOG_DEBUG); 8050 8051 $error = 0; 8052 8053 $this->db->begin(); 8054 8055 if ($forcechilddeletion) // Force also delete of childtables that should lock deletion in standard case when option force is off 8056 { 8057 foreach ($this->childtables as $table) 8058 { 8059 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$table.' WHERE '.$this->fk_element.' = '.$this->id; 8060 $resql = $this->db->query($sql); 8061 if (!$resql) 8062 { 8063 $this->error = $this->db->lasterror(); 8064 $this->errors[] = $this->error; 8065 $this->db->rollback(); 8066 return -1; 8067 } 8068 } 8069 } elseif (!empty($this->fk_element) && !empty($this->childtables)) // If object has childs linked with a foreign key field, we check all child tables. 8070 { 8071 $objectisused = $this->isObjectUsed($this->id); 8072 if (!empty($objectisused)) 8073 { 8074 dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING); 8075 $this->error = 'ErrorRecordHasChildren'; 8076 $this->errors[] = $this->error; 8077 $this->db->rollback(); 8078 return 0; 8079 } 8080 } 8081 8082 // Delete cascade first 8083 if (is_array($this->childtablesoncascade) && !empty($this->childtablesoncascade)) { 8084 foreach ($this->childtablesoncascade as $table) 8085 { 8086 $deleteFromObject = explode(':', $table); 8087 if (count($deleteFromObject) >= 2) { 8088 $className = str_replace('@', '', $deleteFromObject[0]); 8089 $filePath = $deleteFromObject[1]; 8090 $columnName = $deleteFromObject[2]; 8091 if (dol_include_once($filePath)) { 8092 $childObject = new $className($this->db); 8093 if (method_exists($childObject, 'deleteByParentField')) { 8094 $result = $childObject->deleteByParentField($this->id, $columnName); 8095 if ($result < 0) { 8096 $error++; 8097 $this->errors[] = $childObject->error; 8098 break; 8099 } 8100 } else { 8101 $error++; 8102 $this->errors[] = "You defined a cascade delete on an object $childObject but there is no method deleteByParentField for it"; 8103 break; 8104 } 8105 } else { 8106 $error++; 8107 $this->errors[] = 'Cannot include child class file '.$filePath; 8108 break; 8109 } 8110 } else { 8111 // Delete record in child table 8112 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$table.' WHERE '.$this->fk_element.' = '.$this->id; 8113 8114 $resql = $this->db->query($sql); 8115 if (!$resql) { 8116 $error++; 8117 $this->error = $this->db->lasterror(); 8118 $this->errors[] = $this->error; 8119 break; 8120 } 8121 } 8122 } 8123 } 8124 8125 if (!$error) { 8126 if (!$notrigger) { 8127 // Call triggers 8128 $result = $this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user); 8129 if ($result < 0) { $error++; } // Do also here what you must do to rollback action if trigger fail 8130 // End call triggers 8131 } 8132 } 8133 8134 // Delete llx_ecm_files 8135 if (!$error) { 8136 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive 8137 if (!$res) { 8138 $error++; 8139 } 8140 } 8141 8142 if (!$error && !empty($this->isextrafieldmanaged)) 8143 { 8144 $result = $this->deleteExtraFields(); 8145 if ($result < 0) { $error++; } 8146 } 8147 8148 if (!$error) 8149 { 8150 $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid='.$this->id; 8151 8152 $res = $this->db->query($sql); 8153 if ($res === false) { 8154 $error++; 8155 $this->errors[] = $this->db->lasterror(); 8156 } 8157 } 8158 8159 // Commit or rollback 8160 if ($error) { 8161 $this->db->rollback(); 8162 return -1; 8163 } else { 8164 $this->db->commit(); 8165 return 1; 8166 } 8167 } 8168 8169 /** 8170 * Delete all child object from a parent ID 8171 * 8172 * @param int $parentId Parent Id 8173 * @param string $parentField Name of Foreign key parent column 8174 * @return int <0 if KO, >0 if OK 8175 * @throws Exception 8176 */ 8177 public function deleteByParentField($parentId = 0, $parentField = '') 8178 { 8179 global $user; 8180 8181 $error = 0; 8182 $deleted = 0; 8183 8184 if (!empty($parentId) && !empty($parentField)) { 8185 $this->db->begin(); 8186 8187 $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$this->table_element; 8188 $sql .= ' WHERE '.$parentField.' = '.(int) $parentId; 8189 8190 $resql = $this->db->query($sql); 8191 if (!$resql) { 8192 $this->errors[] = $this->db->lasterror(); 8193 $error++; 8194 } else { 8195 while ($obj = $this->db->fetch_object($resql)) { 8196 $result = $this->fetch($obj->rowid); 8197 if ($result < 0) { 8198 $error++; 8199 $this->errors[] = $this->error; 8200 } else { 8201 if (get_class($this) == 'Contact') { // TODO special code because delete() for contact has not been standardized like other delete. 8202 $result = $this->delete(); 8203 } else { 8204 $result = $this->delete($user); 8205 } 8206 if ($result < 0) { 8207 $error++; 8208 $this->errors[] = $this->error; 8209 } else { 8210 $deleted++; 8211 } 8212 } 8213 } 8214 } 8215 8216 if (empty($error)) { 8217 $this->db->commit(); 8218 return $deleted; 8219 } else { 8220 $this->error = implode(', ', $this->errors); 8221 $this->db->rollback(); 8222 return $error * -1; 8223 } 8224 } 8225 8226 return $deleted; 8227 } 8228 8229 /** 8230 * Delete a line of object in database 8231 * 8232 * @param User $user User that delete 8233 * @param int $idline Id of line to delete 8234 * @param bool $notrigger false=launch triggers after, true=disable triggers 8235 * @return int >0 if OK, <0 if KO 8236 */ 8237 public function deleteLineCommon(User $user, $idline, $notrigger = false) 8238 { 8239 global $conf; 8240 8241 $error = 0; 8242 8243 $tmpforobjectclass = get_class($this); 8244 $tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line'; 8245 8246 // Call trigger 8247 $result = $this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user); 8248 if ($result < 0) return -1; 8249 // End call triggers 8250 8251 $this->db->begin(); 8252 8253 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element_line; 8254 $sql .= " WHERE rowid=".$idline; 8255 8256 dol_syslog(get_class($this)."::deleteLineCommon", LOG_DEBUG); 8257 $resql = $this->db->query($sql); 8258 if (!$resql) 8259 { 8260 $this->error = "Error ".$this->db->lasterror(); 8261 $error++; 8262 } 8263 8264 if (empty($error)) { 8265 // Remove extrafields 8266 $tmpobjectline = new $tmpforobjectlineclass($this->db); 8267 if (!isset($tmpobjectline->isextrafieldmanaged) || !empty($tmpobjectline->isextrafieldmanaged)) { 8268 $tmpobjectline->id = $idline; 8269 $result = $tmpobjectline->deleteExtraFields(); 8270 if ($result < 0) 8271 { 8272 $error++; 8273 $this->error = "Error ".get_class($this)."::deleteLineCommon deleteExtraFields error -4 ".$tmpobjectline->error; 8274 } 8275 } 8276 } 8277 8278 if (empty($error)) { 8279 $this->db->commit(); 8280 return 1; 8281 } else { 8282 dol_syslog(get_class($this)."::deleteLineCommon ERROR:".$this->error, LOG_ERR); 8283 $this->db->rollback(); 8284 return -1; 8285 } 8286 } 8287 8288 8289 /** 8290 * Set to a status 8291 * 8292 * @param User $user Object user that modify 8293 * @param int $status New status to set (often a constant like self::STATUS_XXX) 8294 * @param int $notrigger 1=Does not execute triggers, 0=Execute triggers 8295 * @param string $triggercode Trigger code to use 8296 * @return int <0 if KO, >0 if OK 8297 */ 8298 public function setStatusCommon($user, $status, $notrigger = 0, $triggercode = '') 8299 { 8300 $error = 0; 8301 8302 $this->db->begin(); 8303 8304 $statusfield = 'status'; 8305 if ($this->element == 'don' || $this->element == 'donation') $statusfield = 'fk_statut'; 8306 8307 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element; 8308 $sql .= " SET ".$statusfield." = ".((int) $status); 8309 $sql .= " WHERE rowid = ".$this->id; 8310 8311 if ($this->db->query($sql)) 8312 { 8313 if (!$error) 8314 { 8315 $this->oldcopy = clone $this; 8316 } 8317 8318 if (!$error && !$notrigger) { 8319 // Call trigger 8320 $result = $this->call_trigger($triggercode, $user); 8321 if ($result < 0) $error++; 8322 } 8323 8324 if (!$error) { 8325 $this->status = $status; 8326 $this->db->commit(); 8327 return 1; 8328 } else { 8329 $this->db->rollback(); 8330 return -1; 8331 } 8332 } else { 8333 $this->error = $this->db->error(); 8334 $this->db->rollback(); 8335 return -1; 8336 } 8337 } 8338 8339 8340 /** 8341 * Initialise object with example values 8342 * Id must be 0 if object instance is a specimen 8343 * 8344 * @return int 8345 */ 8346 public function initAsSpecimenCommon() 8347 { 8348 global $user; 8349 8350 $this->id = 0; 8351 $this->specimen = 1; 8352 $fields = array( 8353 'label' => 'This is label', 8354 'ref' => 'ABCD1234', 8355 'description' => 'This is a description', 8356 'qty' => 123.12, 8357 'note_public' => 'Public note', 8358 'note_private' => 'Private note', 8359 'date_creation' => (dol_now() - 3600 * 48), 8360 'date_modification' => (dol_now() - 3600 * 24), 8361 'fk_user_creat' => $user->id, 8362 'fk_user_modif' => $user->id, 8363 'date' => dol_now(), 8364 ); 8365 foreach ($fields as $key => $value) { 8366 if (array_key_exists($key, $this->fields)) $this->{$key} = $value; 8367 } 8368 return 1; 8369 } 8370 8371 8372 /* Part for comments */ 8373 8374 /** 8375 * Load comments linked with current task 8376 * @return boolean 1 if ok 8377 */ 8378 public function fetchComments() 8379 { 8380 require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php'; 8381 8382 $comment = new Comment($this->db); 8383 $result = $comment->fetchAllFor($this->element, $this->id); 8384 if ($result < 0) { 8385 $this->errors = array_merge($this->errors, $comment->errors); 8386 return -1; 8387 } else { 8388 $this->comments = $comment->comments; 8389 } 8390 return count($this->comments); 8391 } 8392 8393 /** 8394 * Return nb comments already posted 8395 * 8396 * @return int 8397 */ 8398 public function getNbComments() 8399 { 8400 return count($this->comments); 8401 } 8402 8403 /** 8404 * Trim object parameters 8405 * 8406 * @param string[] $parameters array of parameters to trim 8407 * @return void 8408 */ 8409 public function trimParameters($parameters) 8410 { 8411 if (!is_array($parameters)) return; 8412 foreach ($parameters as $parameter) { 8413 if (isset($this->$parameter)) { 8414 $this->$parameter = trim($this->$parameter); 8415 } 8416 } 8417 } 8418 8419 /* Part for categories/tags */ 8420 8421 /** 8422 * Sets object to given categories. 8423 * 8424 * Deletes object from existing categories not supplied. 8425 * Adds it to non existing supplied categories. 8426 * Existing categories are left untouch. 8427 * 8428 * @param string $type_categ Category type ('customer', 'supplier', 'website_page', ...) 8429 * @return int Array of category objects or < 0 if KO 8430 */ 8431 public function getCategoriesCommon($type_categ) 8432 { 8433 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; 8434 8435 // Get current categories 8436 $c = new Categorie($this->db); 8437 $existing = $c->containing($this->id, $type_categ, 'id'); 8438 8439 return $existing; 8440 } 8441 8442 /** 8443 * Sets object to given categories. 8444 * 8445 * Deletes object from existing categories not supplied. 8446 * Adds it to non existing supplied categories. 8447 * Existing categories are left untouch. 8448 * 8449 * @param int[]|int $categories Category ID or array of Categories IDs 8450 * @param string $type_categ Category type ('customer', 'supplier', 'website_page', ...) 8451 * @return int <0 if KO, >0 if OK 8452 */ 8453 public function setCategoriesCommon($categories, $type_categ) 8454 { 8455 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; 8456 8457 // Handle single category 8458 if (!is_array($categories)) { 8459 $categories = array($categories); 8460 } 8461 8462 // Get current categories 8463 $c = new Categorie($this->db); 8464 $existing = $c->containing($this->id, $type_categ, 'id'); 8465 8466 // Diff 8467 if (is_array($existing)) { 8468 $to_del = array_diff($existing, $categories); 8469 $to_add = array_diff($categories, $existing); 8470 } else { 8471 $to_del = array(); // Nothing to delete 8472 $to_add = $categories; 8473 } 8474 8475 $error = 0; 8476 8477 // Process 8478 foreach ($to_del as $del) { 8479 if ($c->fetch($del) > 0) { 8480 $c->del_type($this, $type_categ); 8481 } 8482 } 8483 foreach ($to_add as $add) { 8484 if ($c->fetch($add) > 0) 8485 { 8486 $result = $c->add_type($this, $type_categ); 8487 if ($result < 0) 8488 { 8489 $error++; 8490 $this->error = $c->error; 8491 $this->errors = $c->errors; 8492 break; 8493 } 8494 } 8495 } 8496 8497 return $error ? -1 : 1; 8498 } 8499 8500 /** 8501 * Copy related categories to another object 8502 * 8503 * @param int $fromId Id object source 8504 * @param int $toId Id object cible 8505 * @param string $type Type of category ('product', ...) 8506 * @return int < 0 if error, > 0 if ok 8507 */ 8508 public function cloneCategories($fromId, $toId, $type = '') 8509 { 8510 $this->db->begin(); 8511 8512 if (empty($type)) $type = $this->table_element; 8513 8514 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php'; 8515 $categorystatic = new Categorie($this->db); 8516 8517 $sql = "INSERT INTO ".MAIN_DB_PREFIX."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type])." (fk_categorie, fk_product)"; 8518 $sql .= " SELECT fk_categorie, $toId FROM ".MAIN_DB_PREFIX."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type]); 8519 $sql .= " WHERE fk_product = ".((int) $fromId); 8520 8521 if (!$this->db->query($sql)) 8522 { 8523 $this->error = $this->db->lasterror(); 8524 $this->db->rollback(); 8525 return -1; 8526 } 8527 8528 $this->db->commit(); 8529 return 1; 8530 } 8531 8532 /** 8533 * Delete related files of object in database 8534 * 8535 * @param integer $mode 0=Use path to find record, 1=Use src_object_xxx fields (Mode 1 is recommanded for new objects) 8536 * @return bool True if OK, False if KO 8537 */ 8538 public function deleteEcmFiles($mode = 0) 8539 { 8540 global $conf; 8541 8542 $this->db->begin(); 8543 8544 // Delete in database with mode 0 8545 if ($mode == 0) { 8546 switch ($this->element) { 8547 case 'propal': 8548 $element = 'propale'; 8549 break; 8550 case 'product': 8551 $element = 'produit'; 8552 break; 8553 case 'order_supplier': 8554 $element = 'fournisseur/commande'; 8555 break; 8556 case 'invoice_supplier': 8557 $element = 'fournisseur/facture/'.get_exdir($this->id, 2, 0, 1, $this, 'invoice_supplier'); 8558 break; 8559 case 'shipping': 8560 $element = 'expedition/sending'; 8561 break; 8562 default: 8563 $element = $this->element; 8564 } 8565 8566 // Delete ecm_files extrafields 8567 $sql = "DELETE FROM ".MAIN_DB_PREFIX."ecm_files_extrafields WHERE fk_object IN ("; 8568 $sql .= " SELECT rowid FROM ".MAIN_DB_PREFIX."ecm_files WHERE filename LIKE '".$this->db->escape($this->ref)."%'"; 8569 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".$conf->entity; // No need of getEntity here 8570 $sql .= ")"; 8571 8572 if (!$this->db->query($sql)) { 8573 $this->error = $this->db->lasterror(); 8574 $this->db->rollback(); 8575 return false; 8576 } 8577 8578 // Delete ecm_files 8579 $sql = "DELETE FROM ".MAIN_DB_PREFIX."ecm_files"; 8580 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%'"; 8581 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".$conf->entity; // No need of getEntity here 8582 8583 if (!$this->db->query($sql)) { 8584 $this->error = $this->db->lasterror(); 8585 $this->db->rollback(); 8586 return false; 8587 } 8588 } 8589 8590 // Delete in database with mode 1 8591 if ($mode == 1) { 8592 $sql = 'DELETE FROM '.MAIN_DB_PREFIX."ecm_files_extrafields"; 8593 $sql .= " WHERE fk_object IN (SELECT rowid FROM ".MAIN_DB_PREFIX."ecm_files WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? '' : '@'.$this->module))."' AND src_object_id = ".$this->id.")"; 8594 $resql = $this->db->query($sql); 8595 if (!$resql) { 8596 $this->error = $this->db->lasterror(); 8597 $this->db->rollback(); 8598 return false; 8599 } 8600 8601 $sql = 'DELETE FROM '.MAIN_DB_PREFIX."ecm_files"; 8602 $sql .= " WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? '' : '@'.$this->module))."' AND src_object_id = ".$this->id; 8603 $resql = $this->db->query($sql); 8604 if (!$resql) { 8605 $this->error = $this->db->lasterror(); 8606 $this->db->rollback(); 8607 return false; 8608 } 8609 } 8610 8611 $this->db->commit(); 8612 return true; 8613 } 8614} 8615