dolibarr  19.0.0-dev
commonobject.class.php
Go to the documentation of this file.
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-2022 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-2022 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-2023 Frédéric France <frederic.france@netlogic.fr>
16  * Copyright (C) 2018 Josep Lluís Amador <joseplluis@lliuretic.cat>
17  * Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
18  * Copyright (C) 2021 Grégory Blémand <gregory.blemand@atm-consulting.fr>
19  * Copyright (C) 2023 Lenin Rivas <lenin.rivas777@gmail.com>
20  *
21  * This program is free software; you can redistribute it and/or modify
22  * it under the terms of the GNU General Public License as published by
23  * the Free Software Foundation; either version 3 of the License, or
24  * (at your option) any later version.
25  *
26  * This program is distributed in the hope that it will be useful,
27  * but WITHOUT ANY WARRANTY; without even the implied warranty of
28  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29  * GNU General Public License for more details.
30  *
31  * You should have received a copy of the GNU General Public License
32  * along with this program. If not, see <https://www.gnu.org/licenses/>.
33  */
34 
45 abstract class CommonObject
46 {
47  const TRIGGER_PREFIX = ''; // to be overriden in child class implementations, i.e. 'BILL', 'TASK', 'PROPAL', etc.
48 
52  public $module;
53 
57  public $db;
58 
62  public $id;
63 
67  public $entity;
68 
73  public $error;
74 
78  public $errorhidden;
79 
83  public $errors = array();
84 
88  private $validateFieldsErrors = array();
89 
93  public $element;
94 
98  public $fk_element;
99 
104  public $element_for_permission;
105 
109  public $table_element;
110 
114  public $table_element_line = '';
115 
119  public $ismultientitymanaged;
120 
124  public $import_key;
125 
129  public $array_options = array();
130 
134  public $fields = array();
135 
139  public $array_languages = null; // Value is array() when load already tried
140 
144  public $contacts_ids;
145 
149  public $linked_objects;
150 
154  public $linkedObjectsIds;
155 
159  public $linkedObjects;
160 
164  private $linkedObjectsFullLoaded = array();
165 
169  public $oldcopy;
173  public $oldref;
174 
178  protected $table_ref_field = '';
179 
183  public $restrictiononfksoc = 0;
184 
185 
186  // Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them.
187 
191  public $context = array();
192 
193  // Properties set and used by Agenda trigger
194  public $actionmsg;
195  public $actionmsg2;
196 
200  public $canvas;
201 
206  public $project;
207 
212  public $fk_project;
213 
219  public $projet;
220 
225  public $fk_projet;
226 
231  public $contact;
232 
237  public $contact_id;
238 
243  public $thirdparty;
244 
249  public $user;
250 
255  public $origin;
256 
261  public $origin_id;
262 
266  public $ref;
267 
271  public $ref_ext;
272 
276  public $ref_previous;
277 
281  public $ref_next;
282 
286  public $newref;
287 
293  public $statut;
294 
299  public $status;
300 
301 
306  public $country;
307 
312  public $country_id;
313 
318  public $country_code;
319 
324  public $state;
325 
330  public $state_id;
331 
336  public $state_code;
337 
342  public $region_id;
343 
348  public $region_code;
349 
354  public $region;
355 
356 
361  public $barcode_type;
362 
367  public $barcode_type_code;
368 
373  public $barcode_type_label;
374 
379  public $barcode_type_coder;
380 
385  public $mode_reglement_id;
386 
391  public $cond_reglement_id;
392 
396  public $demand_reason_id;
397 
402  public $transport_mode_id;
403 
409  public $cond_reglement;
410 
416  public $fk_delivery_address;
417 
422  public $shipping_method_id;
423 
428  public $shipping_method;
429 
433  public $multicurrency_code;
434 
438  public $multicurrency_tx;
439 
444  public $model_pdf;
445 
451  public $modelpdf;
452 
457  public $last_main_doc;
458 
464  public $fk_bank;
465 
470  public $fk_account;
471 
475  public $openid;
476 
481  public $note_public;
482 
487  public $note_private;
488 
493  public $note;
494 
499  public $total_ht;
500 
505  public $total_tva;
506 
511  public $total_localtax1;
512 
517  public $total_localtax2;
518 
523  public $total_ttc;
524 
528  public $lines;
529 
534  public $comments = array();
535 
539  public $name;
540 
544  public $lastname;
545 
549  public $firstname;
550 
554  public $civility_id;
555 
556  // Dates
560  public $date_creation;
561 
565  public $date_validation; // Date validation
566 
570  public $date_modification; // Date last change (tms field)
571 
576  public $date_update;
577 
581  public $date_cloture; // Date closing (tms field)
582 
587  public $user_author;
588 
593  public $user_creation;
594 
598  public $user_creation_id;
599 
604  public $user_valid;
605 
610  public $user_validation;
611 
615  public $user_validation_id;
616 
620  public $user_closing_id;
621 
626  public $user_modification;
627 
631  public $user_modification_id;
632 
633 
634  public $next_prev_filter;
635 
639  public $specimen = 0;
640 
644  public $sendtoid;
645 
649  public $alreadypaid;
650 
651  public $labelStatus;
652 
653  protected $labelStatusShort;
654 
658  public $showphoto_on_popup;
659 
663  public $nb = array();
664 
668  public $output;
669 
673  public $extraparams = array();
674 
678  protected $childtables = array();
679 
685  protected $childtablesoncascade = array();
686 
687 
688  // No constructor as it is an abstract class
689 
690 
701  public static function isExistingObject($element, $id, $ref = '', $ref_ext = '')
702  {
703  global $db, $conf;
704 
705  $sql = "SELECT rowid, ref, ref_ext";
706  $sql .= " FROM ".$db->prefix().$element;
707  $sql .= " WHERE entity IN (".getEntity($element).")";
708 
709  if ($id > 0) {
710  $sql .= " AND rowid = ".((int) $id);
711  } elseif ($ref) {
712  $sql .= " AND ref = '".$db->escape($ref)."'";
713  } elseif ($ref_ext) {
714  $sql .= " AND ref_ext = '".$db->escape($ref_ext)."'";
715  } else {
716  $error = 'ErrorWrongParameters';
717  dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
718  return -1;
719  }
720  if ($ref || $ref_ext) { // Because the same ref can exists in 2 different entities, we force the current one in priority
721  $sql .= " AND entity = ".((int) $conf->entity);
722  }
723 
724  dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
725  $resql = $db->query($sql);
726  if ($resql) {
727  $num = $db->num_rows($resql);
728  if ($num > 0) {
729  return 1;
730  } else {
731  return 0;
732  }
733  }
734  return -1;
735  }
736 
743  public function setErrorsFromObject($object)
744  {
745  if (!empty($object->error)) {
746  $this->error = $object->error;
747  }
748  if (!empty($object->errors)) {
749  $this->errors = array_merge($this->errors, $object->errors);
750  }
751  }
752 
760  public function getTooltipContentArray($params)
761  {
762  return [];
763  }
764 
772  public function getTooltipContent($params)
773  {
774  global $action, $extrafields, $langs, $hookmanager;
775 
776  // If there is too much extrafields, we do not include them into tooltip
777  $MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP = getDolGlobalInt('MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP', 3);
778 
779  $datas = $this->getTooltipContentArray($params);
780  $count = 0;
781 
782  // Add extrafields
783  if (!empty($extrafields->attributes[$this->table_element]['label'])) {
784  foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
785  if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
786  continue;
787  }
788  if ($count >= abs($MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP)) {
789  $datas['more_extrafields'] = '<br>...';
790  break;
791  }
792  $enabled = 1;
793  if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
794  $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
795  }
796  if ($enabled && isset($extrafields->attributes[$this->table_element]['list'][$key])) {
797  $enabled = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
798  }
799  $perms = 1;
800  if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
801  $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
802  }
803  if (empty($enabled)) {
804  continue; // 0 = Never visible field
805  }
806  if (abs($enabled) != 1 && abs($enabled) != 3 && abs($enabled) != 5 && abs($enabled) != 4) {
807  continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list <> 4 = not visible at the creation
808  }
809  if (empty($perms)) {
810  continue; // 0 = Not visible
811  }
812  if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
813  $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
814  }
815  $labelextra = $langs->trans((string) $extrafields->attributes[$this->table_element]['label'][$key]);
816  if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
817  $datas[$key]= '<br><b><u>'. $labelextra . '</u></b>';
818  } else {
819  $value = (empty($this->array_options['options_' . $key]) ? '' : $this->array_options['options_' . $key]);
820  $datas[$key]= '<br><b>'. $labelextra . ':</b> ' . $extrafields->showOutputField($key, $value, '', $this->table_element);
821  $count++;
822  }
823  }
824  }
825 
826  $hookmanager->initHooks(array($this->element . 'dao'));
827  $parameters = array(
828  'tooltipcontentarray' => &$datas,
829  'params' => $params,
830  );
831  // Note that $action and $object may have been modified by some hooks
832  $hookmanager->executeHooks('getTooltipContent', $parameters, $this, $action);
833 
834  //var_dump($datas);
835  $label = implode($datas);
836 
837  return $label;
838  }
839 
840 
846  public function errorsToString()
847  {
848  return $this->error.(is_array($this->errors) ? (($this->error != '' ? ', ' : '').join(', ', $this->errors)) : '');
849  }
850 
851 
858  public function getFormatedCustomerRef($objref)
859  {
860  global $hookmanager;
861 
862  $parameters = array('objref'=>$objref);
863  $action = '';
864  $reshook = $hookmanager->executeHooks('getFormatedCustomerRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
865  if ($reshook > 0) {
866  return $hookmanager->resArray['objref'];
867  }
868  return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
869  }
870 
877  public function getFormatedSupplierRef($objref)
878  {
879  global $hookmanager;
880 
881  $parameters = array('objref'=>$objref);
882  $action = '';
883  $reshook = $hookmanager->executeHooks('getFormatedSupplierRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
884  if ($reshook > 0) {
885  return $hookmanager->resArray['objref'];
886  }
887  return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
888  }
889 
899  public function getFullAddress($withcountry = 0, $sep = "\n", $withregion = 0, $extralangcode = '')
900  {
901  if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country))) {
902  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
903  $tmparray = getCountry($this->country_id, 'all');
904  $this->country_code = $tmparray['code'];
905  $this->country = $tmparray['label'];
906  }
907 
908  if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_code))) {
909  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
910  $tmparray = getState($this->state_id, 'all', 0, 1);
911  $this->state_code = $tmparray['code'];
912  $this->state = $tmparray['label'];
913  $this->region_code = $tmparray['region_code'];
914  $this->region = $tmparray['region'];
915  }
916 
917  return dol_format_address($this, $withcountry, $sep, '', 0, $extralangcode);
918  }
919 
920 
929  public function getLastMainDocLink($modulepart, $initsharekey = 0, $relativelink = 0)
930  {
931  global $user, $dolibarr_main_url_root;
932 
933  if (empty($this->last_main_doc)) {
934  return ''; // No way to known which document name to use
935  }
936 
937  include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
938  $ecmfile = new EcmFiles($this->db);
939  $result = $ecmfile->fetch(0, '', $this->last_main_doc);
940  if ($result < 0) {
941  $this->error = $ecmfile->error;
942  $this->errors = $ecmfile->errors;
943  return -1;
944  }
945 
946  if (empty($ecmfile->id)) {
947  // Add entry into index
948  if ($initsharekey) {
949  require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
950 
951  // TODO We can't, we dont' have full path of file, only last_main_doc and ->element, so we must first rebuild full path $destfull
952  /*
953  $ecmfile->filepath = $rel_dir;
954  $ecmfile->filename = $filename;
955  $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
956  $ecmfile->fullpath_orig = '';
957  $ecmfile->gen_or_uploaded = 'generated';
958  $ecmfile->description = ''; // indexed content
959  $ecmfile->keywords = ''; // keyword content
960  $ecmfile->share = getRandomPassword(true);
961  $result = $ecmfile->create($user);
962  if ($result < 0)
963  {
964  $this->error = $ecmfile->error;
965  $this->errors = $ecmfile->errors;
966  }
967  */
968  } else {
969  return '';
970  }
971  } elseif (empty($ecmfile->share)) {
972  // Add entry into index
973  if ($initsharekey) {
974  require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
975  $ecmfile->share = getRandomPassword(true);
976  $ecmfile->update($user);
977  } else {
978  return '';
979  }
980  }
981  // Define $urlwithroot
982  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
983  // This is to use external domain name found into config file
984  //if (DOL_URL_ROOT && ! preg_match('/\/$/', $urlwithouturlroot) && ! preg_match('/^\//', DOL_URL_ROOT)) $urlwithroot=$urlwithouturlroot.'/'.DOL_URL_ROOT;
985  //else
986  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT;
987  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
988 
989  $forcedownload = 0;
990 
991  $paramlink = '';
992  //if (!empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart; // For sharing with hash (so public files), modulepart is not required.
993  //if (!empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; // For sharing with hash (so public files), entity is not required.
994  //$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath); // No need of name of file for public link, we will use the hash
995  if (!empty($ecmfile->share)) {
996  $paramlink .= ($paramlink ? '&' : '').'hashp='.$ecmfile->share; // Hash for public share
997  }
998  if ($forcedownload) {
999  $paramlink .= ($paramlink ? '&' : '').'attachment=1';
1000  }
1001 
1002  if ($relativelink) {
1003  $linktoreturn = 'document.php'.($paramlink ? '?'.$paramlink : '');
1004  } else {
1005  $linktoreturn = $urlwithroot.'/document.php'.($paramlink ? '?'.$paramlink : '');
1006  }
1007 
1008  // Here $ecmfile->share is defined
1009  return $linktoreturn;
1010  }
1011 
1012 
1013  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1023  public function add_contact($fk_socpeople, $type_contact, $source = 'external', $notrigger = 0)
1024  {
1025  // phpcs:enable
1026  global $user, $langs;
1027 
1028 
1029  dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
1030 
1031  // Check parameters
1032  if ($fk_socpeople <= 0) {
1033  $langs->load("errors");
1034  $this->error = $langs->trans("ErrorWrongValueForParameterX", "1");
1035  dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
1036  return -1;
1037  }
1038  if (!$type_contact) {
1039  $langs->load("errors");
1040  $this->error = $langs->trans("ErrorWrongValueForParameterX", "2");
1041  dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
1042  return -2;
1043  }
1044 
1045  $id_type_contact = 0;
1046  if (is_numeric($type_contact)) {
1047  $id_type_contact = $type_contact;
1048  } else {
1049  // We look for id type_contact
1050  $sql = "SELECT tc.rowid";
1051  $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1052  $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1053  $sql .= " AND tc.source='".$this->db->escape($source)."'";
1054  $sql .= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
1055  //print $sql;
1056  $resql = $this->db->query($sql);
1057  if ($resql) {
1058  $obj = $this->db->fetch_object($resql);
1059  if ($obj) {
1060  $id_type_contact = $obj->rowid;
1061  }
1062  }
1063  }
1064 
1065  if ($id_type_contact == 0) {
1066  $this->error = 'CODE_NOT_VALID_FOR_THIS_ELEMENT';
1067  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");
1068  return -3;
1069  }
1070 
1071  $datecreate = dol_now();
1072 
1073  // Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
1074  $TListeContacts = $this->liste_contact(-1, $source);
1075  $already_added = false;
1076  if (is_array($TListeContacts) && !empty($TListeContacts)) {
1077  foreach ($TListeContacts as $array_contact) {
1078  if ($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
1079  $already_added = true;
1080  break;
1081  }
1082  }
1083  }
1084 
1085  if (!$already_added) {
1086  $this->db->begin();
1087 
1088  // Insert into database
1089  $sql = "INSERT INTO ".$this->db->prefix()."element_contact";
1090  $sql .= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
1091  $sql .= " VALUES (".$this->id.", ".((int) $fk_socpeople)." , ";
1092  $sql .= "'".$this->db->idate($datecreate)."'";
1093  $sql .= ", 4, ".((int) $id_type_contact);
1094  $sql .= ")";
1095 
1096  $resql = $this->db->query($sql);
1097  if ($resql) {
1098  if (!$notrigger) {
1099  $result = $this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
1100  if ($result < 0) {
1101  $this->db->rollback();
1102  return -1;
1103  }
1104  }
1105 
1106  $this->db->commit();
1107  return 1;
1108  } else {
1109  if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1110  $this->error = $this->db->errno();
1111  $this->db->rollback();
1112  return -2;
1113  } else {
1114  $this->error = $this->db->lasterror();
1115  $this->db->rollback();
1116  return -1;
1117  }
1118  }
1119  } else {
1120  return 0;
1121  }
1122  }
1123 
1124  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1132  public function copy_linked_contact($objFrom, $source = 'internal')
1133  {
1134  // phpcs:enable
1135  $contacts = $objFrom->liste_contact(-1, $source);
1136  foreach ($contacts as $contact) {
1137  if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0) {
1138  return -1;
1139  }
1140  }
1141  return 1;
1142  }
1143 
1144  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1154  public function update_contact($rowid, $statut, $type_contact_id = 0, $fk_socpeople = 0)
1155  {
1156  // phpcs:enable
1157  // Insert into database
1158  $sql = "UPDATE ".$this->db->prefix()."element_contact set";
1159  $sql .= " statut = ".$statut;
1160  if ($type_contact_id) {
1161  $sql .= ", fk_c_type_contact = ".((int) $type_contact_id);
1162  }
1163  if ($fk_socpeople) {
1164  $sql .= ", fk_socpeople = ".((int) $fk_socpeople);
1165  }
1166  $sql .= " where rowid = ".((int) $rowid);
1167  $resql = $this->db->query($sql);
1168  if ($resql) {
1169  return 0;
1170  } else {
1171  $this->error = $this->db->lasterror();
1172  return -1;
1173  }
1174  }
1175 
1176  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1184  public function delete_contact($rowid, $notrigger = 0)
1185  {
1186  // phpcs:enable
1187  global $user;
1188 
1189 
1190  $this->db->begin();
1191 
1192  $sql = "DELETE FROM ".$this->db->prefix()."element_contact";
1193  $sql .= " WHERE rowid = ".((int) $rowid);
1194 
1195  dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
1196  if ($this->db->query($sql)) {
1197  if (!$notrigger) {
1198  $result = $this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
1199  if ($result < 0) {
1200  $this->db->rollback();
1201  return -1;
1202  }
1203  }
1204 
1205  $this->db->commit();
1206  return 1;
1207  } else {
1208  $this->error = $this->db->lasterror();
1209  $this->db->rollback();
1210  return -1;
1211  }
1212  }
1213 
1214  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1222  public function delete_linked_contact($source = '', $code = '')
1223  {
1224  // phpcs:enable
1225  $listId = '';
1226  $temp = array();
1227  $typeContact = $this->liste_type_contact($source, '', 0, 0, $code);
1228 
1229  if (!empty($typeContact)) {
1230  foreach ($typeContact as $key => $value) {
1231  array_push($temp, $key);
1232  }
1233  $listId = implode(",", $temp);
1234  }
1235 
1236  // If $listId is empty, we have not criteria on fk_c_type_contact so we will delete record on element_id for
1237  // any type or record instead of only the ones of the current object. So we do nothing in such a case.
1238  if (empty($listId)) {
1239  return 0;
1240  }
1241 
1242  $sql = "DELETE FROM ".$this->db->prefix()."element_contact";
1243  $sql .= " WHERE element_id = ".((int) $this->id);
1244  $sql .= " AND fk_c_type_contact IN (".$this->db->sanitize($listId).")";
1245 
1246  dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
1247  if ($this->db->query($sql)) {
1248  return 1;
1249  } else {
1250  $this->error = $this->db->lasterror();
1251  return -1;
1252  }
1253  }
1254 
1255  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1267  public function liste_contact($statusoflink = -1, $source = 'external', $list = 0, $code = '', $status = -1, $arrayoftcids = array())
1268  {
1269  // phpcs:enable
1270  global $langs;
1271 
1272  $tab = array();
1273 
1274  $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
1275  if ($source == 'internal') {
1276  $sql .= ", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
1277  }
1278  if ($source == 'external' || $source == 'thirdparty') {
1279  $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
1280  }
1281  $sql .= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
1282  $sql .= ", tc.source, tc.element, tc.code, tc.libelle";
1283  $sql .= " FROM ".$this->db->prefix()."c_type_contact tc,";
1284  $sql .= " ".$this->db->prefix()."element_contact ec";
1285  if ($source == 'internal') { // internal contact (user)
1286  $sql .= " LEFT JOIN ".$this->db->prefix()."user t on ec.fk_socpeople = t.rowid";
1287  }
1288  if ($source == 'external' || $source == 'thirdparty') { // external contact (socpeople)
1289  $sql .= " LEFT JOIN ".$this->db->prefix()."socpeople t on ec.fk_socpeople = t.rowid";
1290  }
1291  $sql .= " WHERE ec.element_id = ".((int) $this->id);
1292  $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1293  $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1294  if ($code) {
1295  $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1296  }
1297  if ($source == 'internal') {
1298  $sql .= " AND tc.source = 'internal'";
1299  if ($status >= 0) {
1300  $sql .= " AND t.statut = ".((int) $status);
1301  }
1302  }
1303  if ($source == 'external' || $source == 'thirdparty') {
1304  $sql .= " AND tc.source = 'external'";
1305  if ($status >= 0) {
1306  $sql .= " AND t.statut = ".((int) $status); // t is llx_socpeople
1307  }
1308  }
1309  $sql .= " AND tc.active = 1";
1310  if ($statusoflink >= 0) {
1311  $sql .= " AND ec.statut = ".((int) $statusoflink);
1312  }
1313  $sql .= " ORDER BY t.lastname ASC";
1314 
1315  dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
1316  $resql = $this->db->query($sql);
1317  if ($resql) {
1318  $num = $this->db->num_rows($resql);
1319  $i = 0;
1320  while ($i < $num) {
1321  $obj = $this->db->fetch_object($resql);
1322 
1323  if (!$list) {
1324  $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
1325  $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1326  $tab[$i] = array(
1327  'parentId' => $this->id,
1328  'source' => $obj->source,
1329  'socid' => $obj->socid,
1330  'id' => $obj->id,
1331  'nom' => $obj->lastname, // For backward compatibility
1332  'civility' => $obj->civility,
1333  'lastname' => $obj->lastname,
1334  'firstname' => $obj->firstname,
1335  'email'=>$obj->email,
1336  'login'=> (empty($obj->login) ? '' : $obj->login),
1337  'photo' => (empty($obj->photo) ? '' : $obj->photo),
1338  'statuscontact' => $obj->statuscontact,
1339  'rowid' => $obj->rowid,
1340  'code' => $obj->code,
1341  'libelle' => $libelle_type,
1342  'status' => $obj->statuslink,
1343  'fk_c_type_contact' => $obj->fk_c_type_contact
1344  );
1345  } else {
1346  $tab[$i] = $obj->id;
1347  }
1348 
1349  $i++;
1350  }
1351 
1352  return $tab;
1353  } else {
1354  $this->error = $this->db->lasterror();
1355  dol_print_error($this->db);
1356  return -1;
1357  }
1358  }
1359 
1360 
1367  public function swapContactStatus($rowid)
1368  {
1369  $sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1370  $sql .= " tc.code, tc.libelle";
1371  $sql .= " FROM (".$this->db->prefix()."element_contact as ec, ".$this->db->prefix()."c_type_contact as tc)";
1372  $sql .= " WHERE ec.rowid =".((int) $rowid);
1373  $sql .= " AND ec.fk_c_type_contact=tc.rowid";
1374  $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1375 
1376  dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1377  $resql = $this->db->query($sql);
1378  if ($resql) {
1379  $obj = $this->db->fetch_object($resql);
1380  $newstatut = ($obj->statut == 4) ? 5 : 4;
1381  $result = $this->update_contact($rowid, $newstatut);
1382  $this->db->free($resql);
1383  return $result;
1384  } else {
1385  $this->error = $this->db->error();
1386  dol_print_error($this->db);
1387  return -1;
1388  }
1389  }
1390 
1391  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1402  public function liste_type_contact($source = 'internal', $order = 'position', $option = 0, $activeonly = 0, $code = '')
1403  {
1404  // phpcs:enable
1405  global $langs;
1406 
1407  if (empty($order)) {
1408  $order = 'position';
1409  }
1410  if ($order == 'position') {
1411  $order .= ',code';
1412  }
1413 
1414  $tab = array();
1415  $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position";
1416  $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1417  $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1418  if ($activeonly == 1) {
1419  $sql .= " AND tc.active=1"; // only the active types
1420  }
1421  if (!empty($source) && $source != 'all') {
1422  $sql .= " AND tc.source='".$this->db->escape($source)."'";
1423  }
1424  if (!empty($code)) {
1425  $sql .= " AND tc.code='".$this->db->escape($code)."'";
1426  }
1427  $sql .= $this->db->order($order, 'ASC');
1428 
1429  //print "sql=".$sql;
1430  $resql = $this->db->query($sql);
1431  if ($resql) {
1432  $num = $this->db->num_rows($resql);
1433  $i = 0;
1434  while ($i < $num) {
1435  $obj = $this->db->fetch_object($resql);
1436 
1437  $transkey = "TypeContact_".$this->element."_".$source."_".$obj->code;
1438  $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1439  if (empty($option)) {
1440  $tab[$obj->rowid] = $libelle_type;
1441  } else {
1442  $tab[$obj->code] = $libelle_type;
1443  }
1444  $i++;
1445  }
1446  return $tab;
1447  } else {
1448  $this->error = $this->db->lasterror();
1449  //dol_print_error($this->db);
1450  return null;
1451  }
1452  }
1453 
1465  public function listeTypeContacts($source = 'internal', $option = 0, $activeonly = 0, $code = '', $element = '', $excludeelement = '')
1466  {
1467  global $langs, $conf;
1468 
1469  $langs->loadLangs(array('bills', 'contracts', 'interventions', 'orders', 'projects', 'propal', 'ticket', 'agenda'));
1470 
1471  $tab = array();
1472 
1473  $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position, tc.element";
1474  $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1475 
1476  $sqlWhere = array();
1477  if (!empty($element)) {
1478  $sqlWhere[] = " tc.element='".$this->db->escape($element)."'";
1479  }
1480  if (!empty($excludeelement)) {
1481  $sqlWhere[] = " tc.element <> '".$this->db->escape($excludeelement)."'";
1482  }
1483 
1484  if ($activeonly == 1) {
1485  $sqlWhere[] = " tc.active=1"; // only the active types
1486  }
1487 
1488  if (!empty($source) && $source != 'all') {
1489  $sqlWhere[] = " tc.source='".$this->db->escape($source)."'";
1490  }
1491 
1492  if (!empty($code)) {
1493  $sqlWhere[] = " tc.code='".$this->db->escape($code)."'";
1494  }
1495 
1496  if (count($sqlWhere) > 0) {
1497  $sql .= " WHERE ".implode(' AND ', $sqlWhere);
1498  }
1499 
1500  $sql .= $this->db->order('tc.element, tc.position', 'ASC');
1501 
1502  dol_syslog(__METHOD__, LOG_DEBUG);
1503  $resql = $this->db->query($sql);
1504  if ($resql) {
1505  $num = $this->db->num_rows($resql);
1506  if ($num > 0) {
1507  $langs->loadLangs(array("propal", "orders", "bills", "suppliers", "contracts", "supplier_proposal"));
1508 
1509  while ($obj = $this->db->fetch_object($resql)) {
1510  $modulename = $obj->element;
1511  if (strpos($obj->element, 'project') !== false) {
1512  $modulename = 'projet';
1513  } elseif ($obj->element == 'contrat') {
1514  $element = 'contract';
1515  } elseif ($obj->element == 'action') {
1516  $modulename = 'agenda';
1517  } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1518  $modulename = 'fournisseur';
1519  } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1520  $modulename = 'fournisseur';
1521  }
1522  if (!empty($conf->{$modulename}->enabled)) {
1523  $libelle_element = $langs->trans('ContactDefault_'.$obj->element);
1524  $tmpelement = $obj->element;
1525  $transkey = "TypeContact_".$tmpelement."_".$source."_".$obj->code;
1526  $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1527  if (empty($option)) {
1528  $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1529  } else {
1530  $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1531  }
1532  }
1533  }
1534  }
1535  return $tab;
1536  } else {
1537  $this->error = $this->db->lasterror();
1538  return null;
1539  }
1540  }
1541 
1553  public function getIdContact($source, $code, $status = 0)
1554  {
1555  global $conf;
1556 
1557  $result = array();
1558  $i = 0;
1559  //cas particulier pour les expeditions
1560  if ($this->element == 'shipping' && $this->origin_id != 0) {
1561  $id = $this->origin_id;
1562  $element = 'commande';
1563  } elseif ($this->element == 'reception' && $this->origin_id != 0) {
1564  $id = $this->origin_id;
1565  $element = 'order_supplier';
1566  } else {
1567  $id = $this->id;
1568  $element = $this->element;
1569  }
1570 
1571  $sql = "SELECT ec.fk_socpeople";
1572  $sql .= " FROM ".$this->db->prefix()."element_contact as ec,";
1573  if ($source == 'internal') {
1574  $sql .= " ".$this->db->prefix()."user as c,";
1575  }
1576  if ($source == 'external') {
1577  $sql .= " ".$this->db->prefix()."socpeople as c,";
1578  }
1579  $sql .= " ".$this->db->prefix()."c_type_contact as tc";
1580  $sql .= " WHERE ec.element_id = ".((int) $id);
1581  $sql .= " AND ec.fk_socpeople = c.rowid";
1582  if ($source == 'internal') {
1583  $sql .= " AND c.entity IN (".getEntity('user').")";
1584  }
1585  if ($source == 'external') {
1586  $sql .= " AND c.entity IN (".getEntity('societe').")";
1587  }
1588  $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1589  $sql .= " AND tc.element = '".$this->db->escape($element)."'";
1590  $sql .= " AND tc.source = '".$this->db->escape($source)."'";
1591  if ($code) {
1592  $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1593  }
1594  $sql .= " AND tc.active = 1";
1595  if ($status) {
1596  $sql .= " AND ec.statut = ".((int) $status);
1597  }
1598 
1599  dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1600  $resql = $this->db->query($sql);
1601  if ($resql) {
1602  while ($obj = $this->db->fetch_object($resql)) {
1603  $result[$i] = $obj->fk_socpeople;
1604  $i++;
1605  }
1606  } else {
1607  $this->error = $this->db->error();
1608  return null;
1609  }
1610 
1611  return $result;
1612  }
1613 
1614  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1621  public function fetch_contact($contactid = null)
1622  {
1623  // phpcs:enable
1624  if (empty($contactid)) {
1625  $contactid = $this->contact_id;
1626  }
1627 
1628  if (empty($contactid)) {
1629  return 0;
1630  }
1631 
1632  require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1633  $contact = new Contact($this->db);
1634  $result = $contact->fetch($contactid);
1635  $this->contact = $contact;
1636  return $result;
1637  }
1638 
1639  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1646  public function fetch_thirdparty($force_thirdparty_id = 0)
1647  {
1648  // phpcs:enable
1649  global $conf;
1650 
1651  if (empty($this->socid) && empty($this->fk_soc) && empty($force_thirdparty_id)) {
1652  return 0;
1653  }
1654 
1655  require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1656 
1657  $idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : 0);
1658  if ($force_thirdparty_id) {
1659  $idtofetch = $force_thirdparty_id;
1660  }
1661 
1662  if ($idtofetch) {
1663  $thirdparty = new Societe($this->db);
1664  $result = $thirdparty->fetch($idtofetch);
1665  if ($result<0) {
1666  $this->errors=array_merge($this->errors, $thirdparty->errors);
1667  }
1668  $this->thirdparty = $thirdparty;
1669 
1670  // Use first price level if level not defined for third party
1671  if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
1672  $this->thirdparty->price_level = 1;
1673  }
1674 
1675  return $result;
1676  } else {
1677  return -1;
1678  }
1679  }
1680 
1681 
1689  public function fetchOneLike($ref)
1690  {
1691  if (!$this->table_ref_field) {
1692  return 0;
1693  }
1694 
1695  $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element." WHERE ".$this->table_ref_field." LIKE '".$this->db->escape($ref)."' LIMIT 1";
1696 
1697  $query = $this->db->query($sql);
1698 
1699  if (!$this->db->num_rows($query)) {
1700  return 0;
1701  }
1702 
1703  $result = $this->db->fetch_object($query);
1704 
1705  return $this->fetch($result->rowid);
1706  }
1707 
1708  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1716  public function fetch_barcode()
1717  {
1718  // phpcs:enable
1719  global $conf;
1720 
1721  dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1722 
1723  $idtype = $this->barcode_type;
1724  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
1725  if ($this->element == 'product' && !empty($conf->global->PRODUIT_DEFAULT_BARCODE_TYPE)) {
1726  $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1727  } elseif ($this->element == 'societe') {
1728  $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1729  } else {
1730  dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1731  }
1732  }
1733 
1734  if ($idtype > 0) {
1735  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
1736  $sql = "SELECT rowid, code, libelle as label, coder";
1737  $sql .= " FROM ".$this->db->prefix()."c_barcode_type";
1738  $sql .= " WHERE rowid = ".((int) $idtype);
1739  dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1740  $resql = $this->db->query($sql);
1741  if ($resql) {
1742  $obj = $this->db->fetch_object($resql);
1743  $this->barcode_type = $obj->rowid;
1744  $this->barcode_type_code = $obj->code;
1745  $this->barcode_type_label = $obj->label;
1746  $this->barcode_type_coder = $obj->coder;
1747  return 1;
1748  } else {
1749  dol_print_error($this->db);
1750  return -1;
1751  }
1752  }
1753  }
1754  return 0;
1755  }
1756 
1757  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1763  public function fetch_project()
1764  {
1765  // phpcs:enable
1766  return $this->fetch_projet();
1767  }
1768 
1769  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1775  public function fetch_projet()
1776  {
1777  // phpcs:enable
1778  include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1779 
1780  if (empty($this->fk_project) && !empty($this->fk_projet)) {
1781  $this->fk_project = $this->fk_projet; // For backward compatibility
1782  }
1783  if (empty($this->fk_project)) {
1784  return 0;
1785  }
1786 
1787  $project = new Project($this->db);
1788  $result = $project->fetch($this->fk_project);
1789 
1790  $this->projet = $project; // deprecated
1791  $this->project = $project;
1792  return $result;
1793  }
1794 
1795  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1801  public function fetch_product()
1802  {
1803  // phpcs:enable
1804  include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1805 
1806  if (empty($this->fk_product)) {
1807  return 0;
1808  }
1809 
1810  $product = new Product($this->db);
1811  $result = $product->fetch($this->fk_product);
1812 
1813  $this->product = $product;
1814  return $result;
1815  }
1816 
1817  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1824  public function fetch_user($userid)
1825  {
1826  // phpcs:enable
1827  $user = new User($this->db);
1828  $result = $user->fetch($userid);
1829  $this->user = $user;
1830  return $result;
1831  }
1832 
1833  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1839  public function fetch_origin()
1840  {
1841  // phpcs:enable
1842  if ($this->origin == 'shipping') {
1843  $this->origin = 'expedition';
1844  }
1845  if ($this->origin == 'delivery') {
1846  $this->origin = 'livraison';
1847  }
1848  if ($this->origin == 'order_supplier') {
1849  $this->origin = 'commandeFournisseur';
1850  }
1851 
1852  $origin = $this->origin;
1853 
1854  $classname = ucfirst($origin);
1855  $this->$origin = new $classname($this->db);
1856  $this->$origin->fetch($this->origin_id);
1857  }
1858 
1868  public function fetchObjectFrom($table, $field, $key, $element = null)
1869  {
1870  global $conf;
1871 
1872  $result = false;
1873 
1874  $sql = "SELECT rowid FROM ".$this->db->prefix().$table;
1875  $sql .= " WHERE ".$field." = '".$this->db->escape($key)."'";
1876  if (!empty($element)) {
1877  $sql .= " AND entity IN (".getEntity($element).")";
1878  } else {
1879  $sql .= " AND entity = ".((int) $conf->entity);
1880  }
1881 
1882  dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
1883  $resql = $this->db->query($sql);
1884  if ($resql) {
1885  $row = $this->db->fetch_row($resql);
1886  // Test for avoid error -1
1887  if ($row[0] > 0) {
1888  $result = $this->fetch($row[0]);
1889  }
1890  }
1891 
1892  return $result;
1893  }
1894 
1903  public function getValueFrom($table, $id, $field)
1904  {
1905  $result = false;
1906  if (!empty($id) && !empty($field) && !empty($table)) {
1907  $sql = "SELECT ".$field." FROM ".$this->db->prefix().$table;
1908  $sql .= " WHERE rowid = ".((int) $id);
1909 
1910  dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
1911  $resql = $this->db->query($sql);
1912  if ($resql) {
1913  $row = $this->db->fetch_row($resql);
1914  $result = $row[0];
1915  }
1916  }
1917  return $result;
1918  }
1919 
1936  public function setValueFrom($field, $value, $table = '', $id = null, $format = '', $id_field = '', $fuser = null, $trigkey = '', $fk_user_field = 'fk_user_modif')
1937  {
1938  global $user, $langs, $conf;
1939 
1940  if (empty($table)) {
1941  $table = $this->table_element;
1942  }
1943  if (empty($id)) {
1944  $id = $this->id;
1945  }
1946  if (empty($format)) {
1947  $format = 'text';
1948  }
1949  if (empty($id_field)) {
1950  $id_field = 'rowid';
1951  }
1952 
1953  // Special case
1954  if ($table == 'product' && $field == 'note_private') {
1955  $field = 'note';
1956  }
1957  if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
1958  $fk_user_field = 'fk_user_mod';
1959  }
1960 
1961  if ($trigkey) {
1962  $oldvalue = null;
1963 
1964  $sql = "SELECT " . $field;
1965  $sql .= " FROM " . MAIN_DB_PREFIX . $table;
1966  $sql .= " WHERE " . $id_field . " = " . ((int) $id);
1967 
1968  $resql = $this->db->query($sql);
1969  if ($resql) {
1970  if ($obj = $this->db->fetch_object($resql)) {
1971  if ($format == 'date') {
1972  $oldvalue = $this->db->jdate($obj->$field);
1973  } else {
1974  $oldvalue = $obj->$field;
1975  }
1976  }
1977  } else {
1978  $this->error = $this->db->lasterror();
1979  return -1;
1980  }
1981  }
1982 
1983  $error = 0;
1984 
1985  dol_syslog(__METHOD__, LOG_DEBUG);
1986 
1987  $this->db->begin();
1988 
1989  $sql = "UPDATE ".$this->db->prefix().$table." SET ";
1990 
1991  if ($format == 'text') {
1992  $sql .= $field." = '".$this->db->escape($value)."'";
1993  } elseif ($format == 'int') {
1994  $sql .= $field." = ".((int) $value);
1995  } elseif ($format == 'date') {
1996  $sql .= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
1997  } elseif ($format == 'dategmt') {
1998  $sql .= $field." = ".($value ? "'".$this->db->idate($value, 'gmt')."'" : "null");
1999  }
2000 
2001  if ($fk_user_field) {
2002  if (!empty($fuser) && is_object($fuser)) {
2003  $sql .= ", ".$fk_user_field." = ".((int) $fuser->id);
2004  } elseif (empty($fuser) || $fuser != 'none') {
2005  $sql .= ", ".$fk_user_field." = ".((int) $user->id);
2006  }
2007  }
2008 
2009  $sql .= " WHERE ".$id_field." = ".((int) $id);
2010 
2011  $resql = $this->db->query($sql);
2012  if ($resql) {
2013  if ($trigkey) {
2014  // call trigger with updated object values
2015  if (method_exists($this, 'fetch')) {
2016  $result = $this->fetch($id);
2017  } else {
2018  $result = $this->fetchCommon($id);
2019  }
2020  $this->oldcopy = clone $this;
2021  if (property_exists($this->oldcopy, $field)) {
2022  $this->oldcopy->$field = $oldvalue;
2023  }
2024 
2025  if ($result >= 0) {
2026  $result = $this->call_trigger($trigkey, (!empty($fuser) && is_object($fuser)) ? $fuser : $user); // This may set this->errors
2027  }
2028  if ($result < 0) {
2029  $error++;
2030  }
2031  }
2032 
2033  if (!$error) {
2034  if (property_exists($this, $field)) {
2035  $this->$field = $value;
2036  }
2037  $this->db->commit();
2038  return 1;
2039  } else {
2040  $this->db->rollback();
2041  return -2;
2042  }
2043  } else {
2044  if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2045  $this->error = 'DB_ERROR_RECORD_ALREADY_EXISTS';
2046  } else {
2047  $this->error = $this->db->lasterror();
2048  }
2049  $this->db->rollback();
2050  return -1;
2051  }
2052  }
2053 
2054  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2063  public function load_previous_next_ref($filter, $fieldid, $nodbprefix = 0)
2064  {
2065  // phpcs:enable
2066  global $conf, $user;
2067 
2068  if (!$this->table_element) {
2069  dol_print_error('', get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
2070  return -1;
2071  }
2072  if ($fieldid == 'none') {
2073  return 1;
2074  }
2075 
2076  // For backward compatibility
2077  if ($this->table_element == 'facture_rec' && $fieldid == 'title') {
2078  $fieldid = 'titre';
2079  }
2080 
2081  // Security on socid
2082  $socid = 0;
2083  if ($user->socid > 0) {
2084  $socid = $user->socid;
2085  }
2086 
2087  // this->ismultientitymanaged contains
2088  // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
2089  $aliastablesociete = 's';
2090  if ($this->element == 'societe') {
2091  $aliastablesociete = 'te'; // te as table_element
2092  }
2093  $restrictiononfksoc = empty($this->restrictiononfksoc) ? 0 : $this->restrictiononfksoc;
2094  $sql = "SELECT MAX(te.".$fieldid.")";
2095  $sql .= " FROM ".(empty($nodbprefix) ?$this->db->prefix():'').$this->table_element." as te";
2096  if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2097  $sql .= ",".$this->db->prefix()."usergroup_user as ug";
2098  }
2099  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2100  $tmparray = explode('@', $this->ismultientitymanaged);
2101  $sql .= ", ".$this->db->prefix().$tmparray[1]." as ".($tmparray[1] == 'societe' ? 's' : 'parenttable'); // If we need to link to this table to limit select to entity
2102  } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2103  $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
2104  } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2105  $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON te.fk_soc = s.rowid"; // If we need to link to societe to limit select to socid
2106  }
2107  if ($restrictiononfksoc && empty($user->rights->societe->client->voir) && !$socid) {
2108  $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
2109  }
2110  if ($fieldid == 'rowid') {
2111  $sql .= " WHERE te.".$fieldid." < ".((int) $this->id);
2112  } else {
2113  $sql .= " WHERE te.".$fieldid." < '".$this->db->escape($this->ref)."'"; // ->ref must always be defined (set to id if field does not exists)
2114  }
2115  if ($restrictiononfksoc == 1 && empty($user->rights->societe->client->voir) && !$socid) {
2116  $sql .= " AND sc.fk_user = ".((int) $user->id);
2117  }
2118  if ($restrictiononfksoc == 2 && empty($user->rights->societe->client->voir) && !$socid) {
2119  $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
2120  }
2121  if (!empty($filter)) {
2122  if (!preg_match('/^\s*AND/i', $filter)) {
2123  $sql .= " AND "; // For backward compatibility
2124  }
2125  $sql .= $filter;
2126  }
2127  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2128  $tmparray = explode('@', $this->ismultientitymanaged);
2129  $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
2130  } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2131  $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
2132  }
2133  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2134  if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2135  if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
2136  $sql .= " AND te.entity IS NOT NULL"; // Show all users
2137  } else {
2138  $sql .= " AND ug.fk_user = te.rowid";
2139  $sql .= " AND ug.entity IN (".getEntity('usergroup').")";
2140  }
2141  } else {
2142  $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
2143  }
2144  }
2145  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
2146  $tmparray = explode('@', $this->ismultientitymanaged);
2147  $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
2148  }
2149  if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
2150  $sql .= ' AND te.fk_soc = '.((int) $socid);
2151  }
2152  if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
2153  $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
2154  }
2155  if ($restrictiononfksoc && $socid && $this->element == 'societe') {
2156  $sql .= ' AND te.rowid = '.((int) $socid);
2157  }
2158  //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
2159 
2160  $result = $this->db->query($sql);
2161  if (!$result) {
2162  $this->error = $this->db->lasterror();
2163  return -1;
2164  }
2165  $row = $this->db->fetch_row($result);
2166  $this->ref_previous = $row[0];
2167 
2168  $sql = "SELECT MIN(te.".$fieldid.")";
2169  $sql .= " FROM ".(empty($nodbprefix) ?$this->db->prefix():'').$this->table_element." as te";
2170  if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2171  $sql .= ",".$this->db->prefix()."usergroup_user as ug";
2172  }
2173  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2174  $tmparray = explode('@', $this->ismultientitymanaged);
2175  $sql .= ", ".$this->db->prefix().$tmparray[1]." as ".($tmparray[1] == 'societe' ? 's' : 'parenttable'); // If we need to link to this table to limit select to entity
2176  } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2177  $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
2178  } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2179  $sql .= " LEFT JOIN ".$this->db->prefix()."societe as s ON te.fk_soc = s.rowid"; // If we need to link to societe to limit select to socid
2180  }
2181  if ($restrictiononfksoc && empty($user->rights->societe->client->voir) && !$socid) {
2182  $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
2183  }
2184  if ($fieldid == 'rowid') {
2185  $sql .= " WHERE te.".$fieldid." > ".((int) $this->id);
2186  } else {
2187  $sql .= " WHERE te.".$fieldid." > '".$this->db->escape($this->ref)."'"; // ->ref must always be defined (set to id if field does not exists)
2188  }
2189  if ($restrictiononfksoc == 1 && empty($user->rights->societe->client->voir) && !$socid) {
2190  $sql .= " AND sc.fk_user = ".((int) $user->id);
2191  }
2192  if ($restrictiononfksoc == 2 && empty($user->rights->societe->client->voir) && !$socid) {
2193  $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
2194  }
2195  if (!empty($filter)) {
2196  if (!preg_match('/^\s*AND/i', $filter)) {
2197  $sql .= " AND "; // For backward compatibility
2198  }
2199  $sql .= $filter;
2200  }
2201  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2202  $tmparray = explode('@', $this->ismultientitymanaged);
2203  $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
2204  } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2205  $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
2206  }
2207  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2208  if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2209  if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
2210  $sql .= " AND te.entity IS NOT NULL"; // Show all users
2211  } else {
2212  $sql .= " AND ug.fk_user = te.rowid";
2213  $sql .= " AND ug.entity IN (".getEntity('usergroup').")";
2214  }
2215  } else {
2216  $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
2217  }
2218  }
2219  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
2220  $tmparray = explode('@', $this->ismultientitymanaged);
2221  $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
2222  }
2223  if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
2224  $sql .= ' AND te.fk_soc = '.((int) $socid);
2225  }
2226  if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
2227  $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
2228  }
2229  if ($restrictiononfksoc && $socid && $this->element == 'societe') {
2230  $sql .= ' AND te.rowid = '.((int) $socid);
2231  }
2232  //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
2233  // 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
2234 
2235  $result = $this->db->query($sql);
2236  if (!$result) {
2237  $this->error = $this->db->lasterror();
2238  return -2;
2239  }
2240  $row = $this->db->fetch_row($result);
2241  $this->ref_next = $row[0];
2242 
2243  return 1;
2244  }
2245 
2246 
2254  public function getListContactId($source = 'external')
2255  {
2256  $contactAlreadySelected = array();
2257  $tab = $this->liste_contact(-1, $source);
2258  $num = count($tab);
2259  $i = 0;
2260  while ($i < $num) {
2261  if ($source == 'thirdparty') {
2262  $contactAlreadySelected[$i] = $tab[$i]['socid'];
2263  } else {
2264  $contactAlreadySelected[$i] = $tab[$i]['id'];
2265  }
2266  $i++;
2267  }
2268  return $contactAlreadySelected;
2269  }
2270 
2271 
2279  public function setProject($projectid, $notrigger = 0)
2280  {
2281  global $user;
2282  $error = 0;
2283 
2284  if (!$this->table_element) {
2285  dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined", LOG_ERR);
2286  return -1;
2287  }
2288 
2289  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2290  if (!empty($this->fields['fk_project'])) { // Common case
2291  if ($projectid) {
2292  $sql .= " SET fk_project = ".((int) $projectid);
2293  } else {
2294  $sql .= " SET fk_project = NULL";
2295  }
2296  $sql .= ' WHERE rowid = '.((int) $this->id);
2297  } elseif ($this->table_element == 'actioncomm') { // Special case for actioncomm
2298  if ($projectid) {
2299  $sql .= " SET fk_project = ".((int) $projectid);
2300  } else {
2301  $sql .= " SET fk_project = NULL";
2302  }
2303  $sql .= ' WHERE id = '.((int) $this->id);
2304  } else // Special case for old architecture objects
2305  {
2306  if ($projectid) {
2307  $sql .= ' SET fk_projet = '.((int) $projectid);
2308  } else {
2309  $sql .= ' SET fk_projet = NULL';
2310  }
2311  $sql .= " WHERE rowid = ".((int) $this->id);
2312  }
2313 
2314  $this->db->begin();
2315 
2316  dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
2317  if ($this->db->query($sql)) {
2318  $this->fk_project = ((int) $projectid);
2319  } else {
2320  dol_print_error($this->db);
2321  $error++;
2322  }
2323 
2324  // Triggers
2325  if (!$error && !$notrigger) {
2326  // Call triggers
2327  $result = $this->call_trigger(strtoupper($this->element) . '_MODIFY', $user);
2328  if ($result < 0) {
2329  $error++;
2330  } //Do also here what you must do to rollback action if trigger fail
2331  // End call triggers
2332  }
2333 
2334  // Commit or rollback
2335  if ($error) {
2336  $this->db->rollback();
2337  return -1;
2338  } else {
2339  $this->db->commit();
2340  return 1;
2341  }
2342  }
2343 
2350  public function setPaymentMethods($id)
2351  {
2352  global $user;
2353 
2354  $error = 0; $notrigger = 0;
2355 
2356  dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
2357 
2358  if ($this->statut >= 0 || $this->element == 'societe') {
2359  // TODO uniformize field name
2360  $fieldname = 'fk_mode_reglement';
2361  if ($this->element == 'societe') {
2362  $fieldname = 'mode_reglement';
2363  }
2364  if (get_class($this) == 'Fournisseur') {
2365  $fieldname = 'mode_reglement_supplier';
2366  }
2367  if (get_class($this) == 'Tva') {
2368  $fieldname = 'fk_typepayment';
2369  }
2370  if (get_class($this) == 'Salary') {
2371  $fieldname = 'fk_typepayment';
2372  }
2373 
2374  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2375  $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2376  $sql .= ' WHERE rowid='.((int) $this->id);
2377 
2378  if ($this->db->query($sql)) {
2379  $this->mode_reglement_id = $id;
2380  // for supplier
2381  if (get_class($this) == 'Fournisseur') {
2382  $this->mode_reglement_supplier_id = $id;
2383  }
2384  // Triggers
2385  if (!$error && !$notrigger) {
2386  // Call triggers
2387  if (get_class($this) == 'Commande') {
2388  $result = $this->call_trigger('ORDER_MODIFY', $user);
2389  } else {
2390  $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
2391  }
2392  if ($result < 0) {
2393  $error++;
2394  }
2395  // End call triggers
2396  }
2397  return 1;
2398  } else {
2399  dol_syslog(get_class($this).'::setPaymentMethods Error '.$this->db->error());
2400  $this->error = $this->db->error();
2401  return -1;
2402  }
2403  } else {
2404  dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
2405  $this->error = 'Status of the object is incompatible '.$this->statut;
2406  return -2;
2407  }
2408  }
2409 
2416  public function setMulticurrencyCode($code)
2417  {
2418  dol_syslog(get_class($this).'::setMulticurrencyCode('.$code.')');
2419  if ($this->statut >= 0 || $this->element == 'societe') {
2420  $fieldname = 'multicurrency_code';
2421 
2422  $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2423  $sql .= " SET ".$fieldname." = '".$this->db->escape($code)."'";
2424  $sql .= ' WHERE rowid='.((int) $this->id);
2425 
2426  if ($this->db->query($sql)) {
2427  $this->multicurrency_code = $code;
2428 
2429  list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
2430  if ($rate) {
2431  $this->setMulticurrencyRate($rate, 2);
2432  }
2433 
2434  return 1;
2435  } else {
2436  dol_syslog(get_class($this).'::setMulticurrencyCode Error '.$sql.' - '.$this->db->error());
2437  $this->error = $this->db->error();
2438  return -1;
2439  }
2440  } else {
2441  dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
2442  $this->error = 'Status of the object is incompatible '.$this->statut;
2443  return -2;
2444  }
2445  }
2446 
2454  public function setMulticurrencyRate($rate, $mode = 1)
2455  {
2456  dol_syslog(get_class($this).'::setMulticurrencyRate('.$rate.','.$mode.')');
2457  if ($this->statut >= 0 || $this->element == 'societe') {
2458  $fieldname = 'multicurrency_tx';
2459 
2460  $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2461  $sql .= " SET ".$fieldname." = ".((float) $rate);
2462  $sql .= ' WHERE rowid='.((int) $this->id);
2463 
2464  if ($this->db->query($sql)) {
2465  $this->multicurrency_tx = $rate;
2466 
2467  // Update line price
2468  if (!empty($this->lines)) {
2469  foreach ($this->lines as &$line) {
2470  // Amounts in company currency will be recalculated
2471  if ($mode == 1) {
2472  $line->subprice = 0;
2473  }
2474 
2475  // Amounts in foreign currency will be recalculated
2476  if ($mode == 2) {
2477  $line->multicurrency_subprice = 0;
2478  }
2479 
2480  switch ($this->element) {
2481  case 'propal':
2484  $this->updateline(
2485  $line->id,
2486  $line->subprice,
2487  $line->qty,
2488  $line->remise_percent,
2489  $line->tva_tx,
2490  $line->localtax1_tx,
2491  $line->localtax2_tx,
2492  ($line->description ? $line->description : $line->desc),
2493  'HT',
2494  $line->info_bits,
2495  $line->special_code,
2496  $line->fk_parent_line,
2497  $line->skip_update_total,
2498  $line->fk_fournprice,
2499  $line->pa_ht,
2500  $line->label,
2501  $line->product_type,
2502  $line->date_start,
2503  $line->date_end,
2504  $line->array_options,
2505  $line->fk_unit,
2506  $line->multicurrency_subprice
2507  );
2508  break;
2509  case 'commande':
2512  $this->updateline(
2513  $line->id,
2514  ($line->description ? $line->description : $line->desc),
2515  $line->subprice,
2516  $line->qty,
2517  $line->remise_percent,
2518  $line->tva_tx,
2519  $line->localtax1_tx,
2520  $line->localtax2_tx,
2521  'HT',
2522  $line->info_bits,
2523  $line->date_start,
2524  $line->date_end,
2525  $line->product_type,
2526  $line->fk_parent_line,
2527  $line->skip_update_total,
2528  $line->fk_fournprice,
2529  $line->pa_ht,
2530  $line->label,
2531  $line->special_code,
2532  $line->array_options,
2533  $line->fk_unit,
2534  $line->multicurrency_subprice
2535  );
2536  break;
2537  case 'facture':
2540  $this->updateline(
2541  $line->id,
2542  ($line->description ? $line->description : $line->desc),
2543  $line->subprice,
2544  $line->qty,
2545  $line->remise_percent,
2546  $line->date_start,
2547  $line->date_end,
2548  $line->tva_tx,
2549  $line->localtax1_tx,
2550  $line->localtax2_tx,
2551  'HT',
2552  $line->info_bits,
2553  $line->product_type,
2554  $line->fk_parent_line,
2555  $line->skip_update_total,
2556  $line->fk_fournprice,
2557  $line->pa_ht,
2558  $line->label,
2559  $line->special_code,
2560  $line->array_options,
2561  $line->situation_percent,
2562  $line->fk_unit,
2563  $line->multicurrency_subprice
2564  );
2565  break;
2566  case 'supplier_proposal':
2569  $this->updateline(
2570  $line->id,
2571  $line->subprice,
2572  $line->qty,
2573  $line->remise_percent,
2574  $line->tva_tx,
2575  $line->localtax1_tx,
2576  $line->localtax2_tx,
2577  ($line->description ? $line->description : $line->desc),
2578  'HT',
2579  $line->info_bits,
2580  $line->special_code,
2581  $line->fk_parent_line,
2582  $line->skip_update_total,
2583  $line->fk_fournprice,
2584  $line->pa_ht,
2585  $line->label,
2586  $line->product_type,
2587  $line->array_options,
2588  $line->ref_fourn,
2589  $line->multicurrency_subprice
2590  );
2591  break;
2592  case 'order_supplier':
2595  $this->updateline(
2596  $line->id,
2597  ($line->description ? $line->description : $line->desc),
2598  $line->subprice,
2599  $line->qty,
2600  $line->remise_percent,
2601  $line->tva_tx,
2602  $line->localtax1_tx,
2603  $line->localtax2_tx,
2604  'HT',
2605  $line->info_bits,
2606  $line->product_type,
2607  false,
2608  $line->date_start,
2609  $line->date_end,
2610  $line->array_options,
2611  $line->fk_unit,
2612  $line->multicurrency_subprice,
2613  $line->ref_supplier
2614  );
2615  break;
2616  case 'invoice_supplier':
2619  $this->updateline(
2620  $line->id,
2621  ($line->description ? $line->description : $line->desc),
2622  $line->subprice,
2623  $line->tva_tx,
2624  $line->localtax1_tx,
2625  $line->localtax2_tx,
2626  $line->qty,
2627  0,
2628  'HT',
2629  $line->info_bits,
2630  $line->product_type,
2631  $line->remise_percent,
2632  false,
2633  $line->date_start,
2634  $line->date_end,
2635  $line->array_options,
2636  $line->fk_unit,
2637  $line->multicurrency_subprice,
2638  $line->ref_supplier
2639  );
2640  break;
2641  default:
2642  dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
2643  break;
2644  }
2645  }
2646  }
2647 
2648  return 1;
2649  } else {
2650  dol_syslog(get_class($this).'::setMulticurrencyRate Error '.$sql.' - '.$this->db->error());
2651  $this->error = $this->db->error();
2652  return -1;
2653  }
2654  } else {
2655  dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
2656  $this->error = 'Status of the object is incompatible '.$this->statut;
2657  return -2;
2658  }
2659  }
2660 
2668  public function setPaymentTerms($id, $deposit_percent = null)
2669  {
2670  dol_syslog(get_class($this).'::setPaymentTerms('.$id.', '.var_export($deposit_percent, true).')');
2671  if ($this->statut >= 0 || $this->element == 'societe') {
2672  // TODO uniformize field name
2673  $fieldname = 'fk_cond_reglement';
2674  if ($this->element == 'societe') {
2675  $fieldname = 'cond_reglement';
2676  }
2677  if (get_class($this) == 'Fournisseur') {
2678  $fieldname = 'cond_reglement_supplier';
2679  }
2680 
2681  if (empty($deposit_percent) || $deposit_percent < 0) {
2682  $deposit_percent = getDictionaryValue('c_payment_term', 'deposit_percent', $id);
2683  }
2684 
2685  if ($deposit_percent > 100) {
2686  $deposit_percent = 100;
2687  }
2688 
2689  $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2690  $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2691  if (in_array($this->table_element, array('propal', 'commande', 'societe'))) {
2692  $sql .= " , deposit_percent = " . (empty($deposit_percent) ? 'NULL' : "'".$this->db->escape($deposit_percent)."'");
2693  }
2694  $sql .= ' WHERE rowid='.((int) $this->id);
2695 
2696  if ($this->db->query($sql)) {
2697  $this->cond_reglement_id = $id;
2698  // for supplier
2699  if (get_class($this) == 'Fournisseur') {
2700  $this->cond_reglement_supplier_id = $id;
2701  }
2702  $this->cond_reglement = $id; // for compatibility
2703  $this->deposit_percent = $deposit_percent;
2704  return 1;
2705  } else {
2706  dol_syslog(get_class($this).'::setPaymentTerms Error '.$sql.' - '.$this->db->error());
2707  $this->error = $this->db->error();
2708  return -1;
2709  }
2710  } else {
2711  dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
2712  $this->error = 'Status of the object is incompatible '.$this->statut;
2713  return -2;
2714  }
2715  }
2716 
2723  public function setTransportMode($id)
2724  {
2725  dol_syslog(get_class($this).'::setTransportMode('.$id.')');
2726  if ($this->statut >= 0 || $this->element == 'societe') {
2727  $fieldname = 'fk_transport_mode';
2728  if ($this->element == 'societe') {
2729  $fieldname = 'transport_mode';
2730  }
2731  if (get_class($this) == 'Fournisseur') {
2732  $fieldname = 'transport_mode_supplier';
2733  }
2734 
2735  $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2736  $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2737  $sql .= ' WHERE rowid='.((int) $this->id);
2738 
2739  if ($this->db->query($sql)) {
2740  $this->transport_mode_id = $id;
2741  // for supplier
2742  if (get_class($this) == 'Fournisseur') {
2743  $this->transport_mode_supplier_id = $id;
2744  }
2745  return 1;
2746  } else {
2747  dol_syslog(get_class($this).'::setTransportMode Error '.$sql.' - '.$this->db->error());
2748  $this->error = $this->db->error();
2749  return -1;
2750  }
2751  } else {
2752  dol_syslog(get_class($this).'::setTransportMode, status of the object is incompatible');
2753  $this->error = 'Status of the object is incompatible '.$this->statut;
2754  return -2;
2755  }
2756  }
2757 
2764  public function setRetainedWarrantyPaymentTerms($id)
2765  {
2766  dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms('.$id.')');
2767  if ($this->statut >= 0 || $this->element == 'societe') {
2768  $fieldname = 'retained_warranty_fk_cond_reglement';
2769 
2770  $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2771  $sql .= " SET ".$fieldname." = ".((int) $id);
2772  $sql .= ' WHERE rowid='.((int) $this->id);
2773 
2774  if ($this->db->query($sql)) {
2775  $this->retained_warranty_fk_cond_reglement = $id;
2776  return 1;
2777  } else {
2778  dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms Error '.$sql.' - '.$this->db->error());
2779  $this->error = $this->db->error();
2780  return -1;
2781  }
2782  } else {
2783  dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms, status of the object is incompatible');
2784  $this->error = 'Status of the object is incompatible '.$this->statut;
2785  return -2;
2786  }
2787  }
2788 
2796  public function setDeliveryAddress($id)
2797  {
2798  $fieldname = 'fk_delivery_address';
2799  if ($this->element == 'delivery' || $this->element == 'shipping') {
2800  $fieldname = 'fk_address';
2801  }
2802 
2803  $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ".$fieldname." = ".((int) $id);
2804  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
2805 
2806  if ($this->db->query($sql)) {
2807  $this->fk_delivery_address = $id;
2808  return 1;
2809  } else {
2810  $this->error = $this->db->error();
2811  dol_syslog(get_class($this).'::setDeliveryAddress Error '.$this->error);
2812  return -1;
2813  }
2814  }
2815 
2816 
2826  public function setShippingMethod($shipping_method_id, $notrigger = false, $userused = null)
2827  {
2828  global $user;
2829 
2830  if (empty($userused)) {
2831  $userused = $user;
2832  }
2833 
2834  $error = 0;
2835 
2836  if (!$this->table_element) {
2837  dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined", LOG_ERR);
2838  return -1;
2839  }
2840 
2841  $this->db->begin();
2842 
2843  if ($shipping_method_id < 0) {
2844  $shipping_method_id = 'NULL';
2845  }
2846  dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
2847 
2848  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2849  $sql .= " SET fk_shipping_method = ".((int) $shipping_method_id);
2850  $sql .= " WHERE rowid=".((int) $this->id);
2851  $resql = $this->db->query($sql);
2852  if (!$resql) {
2853  dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
2854  $this->error = $this->db->lasterror();
2855  $error++;
2856  } else {
2857  if (!$notrigger) {
2858  // Call trigger
2859  $this->context = array('shippingmethodupdate'=>1);
2860  $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused);
2861  if ($result < 0) {
2862  $error++;
2863  }
2864  // End call trigger
2865  }
2866  }
2867  if ($error) {
2868  $this->db->rollback();
2869  return -1;
2870  } else {
2871  $this->shipping_method_id = ($shipping_method_id == 'NULL') ?null:$shipping_method_id;
2872  $this->db->commit();
2873  return 1;
2874  }
2875  }
2876 
2877 
2884  public function setWarehouse($warehouse_id)
2885  {
2886  if (!$this->table_element) {
2887  dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined", LOG_ERR);
2888  return -1;
2889  }
2890  if ($warehouse_id < 0) {
2891  $warehouse_id = 'NULL';
2892  }
2893  dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
2894 
2895  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2896  $sql .= " SET fk_warehouse = ".((int) $warehouse_id);
2897  $sql .= " WHERE rowid=".((int) $this->id);
2898 
2899  if ($this->db->query($sql)) {
2900  $this->warehouse_id = ($warehouse_id == 'NULL') ?null:$warehouse_id;
2901  return 1;
2902  } else {
2903  dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
2904  $this->error = $this->db->error();
2905  return 0;
2906  }
2907  }
2908 
2909 
2917  public function setDocModel($user, $modelpdf)
2918  {
2919  if (!$this->table_element) {
2920  dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined", LOG_ERR);
2921  return -1;
2922  }
2923 
2924  $newmodelpdf = dol_trunc($modelpdf, 255);
2925 
2926  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2927  $sql .= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
2928  $sql .= " WHERE rowid = ".((int) $this->id);
2929 
2930  dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
2931  $resql = $this->db->query($sql);
2932  if ($resql) {
2933  $this->model_pdf = $modelpdf;
2934  $this->modelpdf = $modelpdf; // For bakward compatibility
2935  return 1;
2936  } else {
2937  dol_print_error($this->db);
2938  return 0;
2939  }
2940  }
2941 
2942 
2951  public function setBankAccount($fk_account, $notrigger = false, $userused = null)
2952  {
2953  global $user;
2954 
2955  if (empty($userused)) {
2956  $userused = $user;
2957  }
2958 
2959  $error = 0;
2960 
2961  if (!$this->table_element) {
2962  dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined", LOG_ERR);
2963  return -1;
2964  }
2965  $this->db->begin();
2966 
2967  if ($fk_account < 0) {
2968  $fk_account = 'NULL';
2969  }
2970  dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
2971 
2972  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2973  $sql .= " SET fk_account = ".((int) $fk_account);
2974  $sql .= " WHERE rowid=".((int) $this->id);
2975 
2976  $resql = $this->db->query($sql);
2977  if (!$resql) {
2978  dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
2979  $this->error = $this->db->lasterror();
2980  $error++;
2981  } else {
2982  if (!$notrigger) {
2983  // Call trigger
2984  $this->context = array('bankaccountupdate'=>1);
2985  $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused);
2986  if ($result < 0) {
2987  $error++;
2988  }
2989  // End call trigger
2990  }
2991  }
2992  if ($error) {
2993  $this->db->rollback();
2994  return -1;
2995  } else {
2996  $this->fk_account = ($fk_account == 'NULL') ?null:$fk_account;
2997  $this->db->commit();
2998  return 1;
2999  }
3000  }
3001 
3002 
3003  // TODO: Move line related operations to CommonObjectLine?
3004 
3005  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3015  public function line_order($renum = false, $rowidorder = 'ASC', $fk_parent_line = true)
3016  {
3017  // phpcs:enable
3018  if (!$this->table_element_line) {
3019  dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined", LOG_ERR);
3020  return -1;
3021  }
3022  if (!$this->fk_element) {
3023  dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined", LOG_ERR);
3024  return -1;
3025  }
3026 
3027  $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3028  if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3029  $fieldposition = 'position';
3030  }
3031 
3032  // Count number of lines to reorder (according to choice $renum)
3033  $nl = 0;
3034  $sql = "SELECT count(rowid) FROM ".$this->db->prefix().$this->table_element_line;
3035  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3036  if (!$renum) {
3037  $sql .= " AND " . $fieldposition . " = 0";
3038  }
3039  if ($renum) {
3040  $sql .= " AND " . $fieldposition . " <> 0";
3041  }
3042 
3043  dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
3044  $resql = $this->db->query($sql);
3045  if ($resql) {
3046  $row = $this->db->fetch_row($resql);
3047  $nl = $row[0];
3048  } else {
3049  dol_print_error($this->db);
3050  }
3051  if ($nl > 0) {
3052  // The goal of this part is to reorder all lines, with all children lines sharing the same counter that parents.
3053  $rows = array();
3054 
3055  // We first search all lines that are parent lines (for multilevel details lines)
3056  $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3057  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3058  if ($fk_parent_line) {
3059  $sql .= ' AND fk_parent_line IS NULL';
3060  }
3061  $sql .= " ORDER BY " . $fieldposition . " ASC, rowid " . $rowidorder;
3062 
3063  dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
3064  $resql = $this->db->query($sql);
3065  if ($resql) {
3066  $i = 0;
3067  $num = $this->db->num_rows($resql);
3068  while ($i < $num) {
3069  $row = $this->db->fetch_row($resql);
3070  $rows[] = $row[0]; // Add parent line into array rows
3071  $childrens = $this->getChildrenOfLine($row[0]);
3072  if (!empty($childrens)) {
3073  foreach ($childrens as $child) {
3074  array_push($rows, $child);
3075  }
3076  }
3077  $i++;
3078  }
3079 
3080  // Now we set a new number for each lines (parent and children with children included into parent tree)
3081  if (!empty($rows)) {
3082  foreach ($rows as $key => $row) {
3083  $this->updateRangOfLine($row, ($key + 1));
3084  }
3085  }
3086  } else {
3087  dol_print_error($this->db);
3088  }
3089  }
3090  return 1;
3091  }
3092 
3100  public function getChildrenOfLine($id, $includealltree = 0)
3101  {
3102  $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3103  if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3104  $fieldposition = 'position';
3105  }
3106 
3107  $rows = array();
3108 
3109  $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3110  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3111  $sql .= ' AND fk_parent_line = '.((int) $id);
3112  $sql .= " ORDER BY " . $fieldposition . " ASC";
3113 
3114  dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id, LOG_DEBUG);
3115  $resql = $this->db->query($sql);
3116  if ($resql) {
3117  if ($this->db->num_rows($resql) > 0) {
3118  while ($row = $this->db->fetch_row($resql)) {
3119  $rows[] = $row[0];
3120  if (!empty($includealltree)) {
3121  $rows = array_merge($rows, $this->getChildrenOfLine($row[0]), $includealltree);
3122  }
3123  }
3124  }
3125  }
3126  return $rows;
3127  }
3128 
3129  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3137  public function line_up($rowid, $fk_parent_line = true)
3138  {
3139  // phpcs:enable
3140  $this->line_order(false, 'ASC', $fk_parent_line);
3141 
3142  // Get rang of line
3143  $rang = $this->getRangOfLine($rowid);
3144 
3145  // Update position of line
3146  $this->updateLineUp($rowid, $rang);
3147  }
3148 
3149  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3157  public function line_down($rowid, $fk_parent_line = true)
3158  {
3159  // phpcs:enable
3160  $this->line_order(false, 'ASC', $fk_parent_line);
3161 
3162  // Get rang of line
3163  $rang = $this->getRangOfLine($rowid);
3164 
3165  // Get max value for rang
3166  $max = $this->line_max();
3167 
3168  // Update position of line
3169  $this->updateLineDown($rowid, $rang, $max);
3170  }
3171 
3179  public function updateRangOfLine($rowid, $rang)
3180  {
3181  global $hookmanager;
3182  $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3183  if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3184  $fieldposition = 'position';
3185  }
3186 
3187  $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3188  $sql .= ' WHERE rowid = '.((int) $rowid);
3189 
3190  dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
3191  if (!$this->db->query($sql)) {
3192  dol_print_error($this->db);
3193  return -1;
3194  } else {
3195  $parameters=array('rowid'=>$rowid, 'rang'=>$rang, 'fieldposition' => $fieldposition);
3196  $action='';
3197  $reshook = $hookmanager->executeHooks('afterRankOfLineUpdate', $parameters, $this, $action);
3198  return 1;
3199  }
3200  }
3201 
3202  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3209  public function line_ajaxorder($rows)
3210  {
3211  // phpcs:enable
3212  $num = count($rows);
3213  for ($i = 0; $i < $num; $i++) {
3214  $this->updateRangOfLine($rows[$i], ($i + 1));
3215  }
3216  }
3217 
3225  public function updateLineUp($rowid, $rang)
3226  {
3227  if ($rang > 1) {
3228  $fieldposition = 'rang';
3229  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3230  $fieldposition = 'position';
3231  }
3232 
3233  $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3234  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3235  $sql .= " AND " . $fieldposition . " = " . ((int) ($rang - 1));
3236  if ($this->db->query($sql)) {
3237  $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang - 1));
3238  $sql .= ' WHERE rowid = '.((int) $rowid);
3239  if (!$this->db->query($sql)) {
3240  dol_print_error($this->db);
3241  }
3242  } else {
3243  dol_print_error($this->db);
3244  }
3245  }
3246  }
3247 
3256  public function updateLineDown($rowid, $rang, $max)
3257  {
3258  if ($rang < $max) {
3259  $fieldposition = 'rang';
3260  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3261  $fieldposition = 'position';
3262  }
3263 
3264  $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3265  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3266  $sql .= " AND " . $fieldposition . " = " . ((int) ($rang + 1));
3267  if ($this->db->query($sql)) {
3268  $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang + 1));
3269  $sql .= ' WHERE rowid = '.((int) $rowid);
3270  if (!$this->db->query($sql)) {
3271  dol_print_error($this->db);
3272  }
3273  } else {
3274  dol_print_error($this->db);
3275  }
3276  }
3277  }
3278 
3285  public function getRangOfLine($rowid)
3286  {
3287  $fieldposition = 'rang';
3288  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3289  $fieldposition = 'position';
3290  }
3291 
3292  $sql = "SELECT " . $fieldposition . " FROM ".$this->db->prefix().$this->table_element_line;
3293  $sql .= " WHERE rowid = ".((int) $rowid);
3294 
3295  dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
3296  $resql = $this->db->query($sql);
3297  if ($resql) {
3298  $row = $this->db->fetch_row($resql);
3299  return $row[0];
3300  }
3301 
3302  return 0;
3303  }
3304 
3311  public function getIdOfLine($rang)
3312  {
3313  $fieldposition = 'rang';
3314  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3315  $fieldposition = 'position';
3316  }
3317 
3318  $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3319  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3320  $sql .= " AND " . $fieldposition . " = ".((int) $rang);
3321  $resql = $this->db->query($sql);
3322  if ($resql) {
3323  $row = $this->db->fetch_row($resql);
3324  return $row[0];
3325  }
3326 
3327  return 0;
3328  }
3329 
3330  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3337  public function line_max($fk_parent_line = 0)
3338  {
3339  // phpcs:enable
3340  $positionfield = 'rang';
3341  if (in_array($this->table_element, array('bom_bom', 'product_attribute'))) {
3342  $positionfield = 'position';
3343  }
3344 
3345  // Search the last rang with fk_parent_line
3346  if ($fk_parent_line) {
3347  $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
3348  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3349  $sql .= " AND fk_parent_line = ".((int) $fk_parent_line);
3350 
3351  dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
3352  $resql = $this->db->query($sql);
3353  if ($resql) {
3354  $row = $this->db->fetch_row($resql);
3355  if (!empty($row[0])) {
3356  return $row[0];
3357  } else {
3358  return $this->getRangOfLine($fk_parent_line);
3359  }
3360  }
3361  } else {
3362  // If not, search the last rang of element
3363  $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
3364  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3365 
3366  dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
3367  $resql = $this->db->query($sql);
3368  if ($resql) {
3369  $row = $this->db->fetch_row($resql);
3370  return $row[0];
3371  }
3372  }
3373 
3374  return 0;
3375  }
3376 
3377  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3384  public function update_ref_ext($ref_ext)
3385  {
3386  // phpcs:enable
3387  if (!$this->table_element) {
3388  dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
3389  return -1;
3390  }
3391 
3392  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3393  $sql .= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
3394  $sql .= " WHERE ".(isset($this->table_rowid) ? $this->table_rowid : 'rowid')." = ".((int) $this->id);
3395 
3396  dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
3397  if ($this->db->query($sql)) {
3398  $this->ref_ext = $ref_ext;
3399  return 1;
3400  } else {
3401  $this->error = $this->db->error();
3402  return -1;
3403  }
3404  }
3405 
3406  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3414  public function update_note($note, $suffix = '')
3415  {
3416  // phpcs:enable
3417  global $user;
3418 
3419  if (!$this->table_element) {
3420  $this->error = 'update_note was called on objet with property table_element not defined';
3421  dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
3422  return -1;
3423  }
3424  if (!in_array($suffix, array('', '_public', '_private'))) {
3425  $this->error = 'update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
3426  dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
3427  return -2;
3428  }
3429 
3430  $newsuffix = $suffix;
3431 
3432  // Special cas
3433  if ($this->table_element == 'product' && $newsuffix == '_private') {
3434  $newsuffix = '';
3435  }
3436  if (in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
3437  $fieldusermod = "fk_user_mod";
3438  } elseif ($this->table_element == 'ecm_files') {
3439  $fieldusermod = "fk_user_m";
3440  } else {
3441  $fieldusermod = "fk_user_modif";
3442  }
3443  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3444  $sql .= " SET note".$newsuffix." = ".(!empty($note) ? ("'".$this->db->escape($note)."'") : "NULL");
3445  $sql .= ", ".$fieldusermod." = ".((int) $user->id);
3446  $sql .= " WHERE rowid = ".((int) $this->id);
3447 
3448  dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
3449  if ($this->db->query($sql)) {
3450  if ($suffix == '_public') {
3451  $this->note_public = $note;
3452  } elseif ($suffix == '_private') {
3453  $this->note_private = $note;
3454  } else {
3455  $this->note = $note; // deprecated
3456  $this->note_private = $note;
3457  }
3458  return 1;
3459  } else {
3460  $this->error = $this->db->lasterror();
3461  return -1;
3462  }
3463  }
3464 
3465  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3474  public function update_note_public($note)
3475  {
3476  // phpcs:enable
3477  return $this->update_note($note, '_public');
3478  }
3479 
3480  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3491  public function update_price($exclspec = 0, $roundingadjust = 'none', $nodatabaseupdate = 0, $seller = null)
3492  {
3493  // phpcs:enable
3494  global $conf, $hookmanager, $action;
3495 
3496  $parameters = array('exclspec' => $exclspec, 'roundingadjust' => $roundingadjust, 'nodatabaseupdate' => $nodatabaseupdate, 'seller' => $seller);
3497  $reshook = $hookmanager->executeHooks('updateTotalPrice', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3498  if ($reshook > 0) {
3499  return 1; // replacement code
3500  } elseif ($reshook < 0) {
3501  return -1; // failure
3502  } // reshook = 0 => execute normal code
3503 
3504  // Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
3505  $MODULE = "";
3506  if ($this->element == 'propal') {
3507  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
3508  } elseif ($this->element == 'commande' || $this->element == 'order') {
3509  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
3510  } elseif ($this->element == 'facture' || $this->element == 'invoice') {
3511  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
3512  } elseif ($this->element == 'facture_fourn' || $this->element == 'supplier_invoice' || $this->element == 'invoice_supplier' || $this->element == 'invoice_supplier_rec') {
3513  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
3514  } elseif ($this->element == 'order_supplier' || $this->element == 'supplier_order') {
3515  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
3516  } elseif ($this->element == 'supplier_proposal') {
3517  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
3518  }
3519 
3520  if (!empty($MODULE)) {
3521  if (!empty($conf->global->$MODULE)) {
3522  $modsactivated = explode(',', $conf->global->$MODULE);
3523  foreach ($modsactivated as $mod) {
3524  if (isModEnabled($mod)) {
3525  return 1; // update was disabled by specific setup
3526  }
3527  }
3528  }
3529  }
3530 
3531  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3532 
3533  $forcedroundingmode = $roundingadjust;
3534  if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) {
3535  $forcedroundingmode = getDolGlobalString('MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND');
3536  } elseif ($forcedroundingmode == 'auto') {
3537  $forcedroundingmode = '0';
3538  }
3539 
3540  $error = 0;
3541 
3542  $multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
3543 
3544  // Define constants to find lines to sum (field name int the table_element_line not into table_element)
3545  $fieldtva = 'total_tva';
3546  $fieldlocaltax1 = 'total_localtax1';
3547  $fieldlocaltax2 = 'total_localtax2';
3548  $fieldup = 'subprice';
3549  if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
3550  $fieldtva = 'tva';
3551  $fieldup = 'pu_ht';
3552  }
3553  if ($this->element == 'invoice_supplier_rec') {
3554  $fieldup = 'pu_ht';
3555  }
3556  if ($this->element == 'expensereport') {
3557  $fieldup = 'value_unit';
3558  }
3559 
3560  $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,";
3561  $sql .= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
3562  if ($this->table_element_line == 'facturedet') {
3563  $sql .= ', situation_percent';
3564  }
3565  $sql .= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
3566  $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
3567  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3568  if ($exclspec) {
3569  $product_field = 'product_type';
3570  if ($this->table_element_line == 'contratdet') {
3571  $product_field = ''; // contratdet table has no product_type field
3572  }
3573  if ($product_field) {
3574  $sql .= " AND ".$product_field." <> 9";
3575  }
3576  }
3577  $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
3578 
3579  dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3580 
3581  $resql = $this->db->query($sql);
3582  if ($resql) {
3583  $this->total_ht = 0;
3584  $this->total_tva = 0;
3585  $this->total_localtax1 = 0;
3586  $this->total_localtax2 = 0;
3587  $this->total_ttc = 0;
3588  $total_ht_by_vats = array();
3589  $total_tva_by_vats = array();
3590  $total_ttc_by_vats = array();
3591  $this->multicurrency_total_ht = 0;
3592  $this->multicurrency_total_tva = 0;
3593  $this->multicurrency_total_ttc = 0;
3594 
3595  $this->db->begin();
3596 
3597  $num = $this->db->num_rows($resql);
3598  $i = 0;
3599  while ($i < $num) {
3600  $obj = $this->db->fetch_object($resql);
3601 
3602  // Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
3603  $parameters = array('fk_element' => $obj->rowid);
3604  $reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3605 
3606  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'
3607  // This part of code is to fix data. We should not call it too often.
3608  $localtax_array = array($obj->localtax1_type, $obj->localtax1_tx, $obj->localtax2_type, $obj->localtax2_tx);
3609  $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);
3610 
3611  $diff_when_using_price_ht = price2num($tmpcal[1] - $obj->total_tva, 'MT', 1); // If price was set with tax price and unit price HT has a low number of digits, then we may have a diff on recalculation from unit price HT.
3612  $diff_on_current_total = price2num($obj->total_ttc - $obj->total_ht - $obj->total_tva - $obj->total_localtax1 - $obj->total_localtax2, 'MT', 1);
3613  //var_dump($obj->total_ht.' '.$obj->total_tva.' '.$obj->total_localtax1.' '.$obj->total_localtax2.' => '.$obj->total_ttc);
3614  //var_dump($diff_when_using_price_ht.' '.$diff_on_current_total);
3615 
3616  if ($diff_on_current_total) {
3617  // This should not happen, we should always have in table: total_ttc = total_ht + total_vat + total_localtax1 + total_localtax2
3618  $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldtva." = ".price2num((float) $tmpcal[1]).", total_ttc = ".price2num((float) $tmpcal[2])." WHERE rowid = ".((int) $obj->rowid);
3619  dol_syslog('We found unconsistent data into detailed line (diff_on_current_total = '.$diff_on_current_total.') for line rowid = '.$obj->rowid." (ht=".$obj->total_ht." vat=".$obj->total_tva." tax1=".$obj->total_localtax1." tax2=".$obj->total_localtax2." ttc=".$obj->total_ttc."). We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix, LOG_WARNING);
3620  $resqlfix = $this->db->query($sqlfix);
3621  if (!$resqlfix) {
3622  dol_print_error($this->db, 'Failed to update line');
3623  }
3624  $obj->total_tva = $tmpcal[1];
3625  $obj->total_ttc = $tmpcal[2];
3626  } elseif ($diff_when_using_price_ht && $roundingadjust == '0') {
3627  // After calculation from HT, total is consistent but we have found a difference between VAT part in calculation and into database and
3628  // we ask to force the use of rounding on line (like done on calculation) so we force update of line
3629  $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldtva." = ".price2num((float) $tmpcal[1]).", total_ttc = ".price2num((float) $tmpcal[2])." WHERE rowid = ".((int) $obj->rowid);
3630  dol_syslog('We found a line with different rounding 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);
3631  $resqlfix = $this->db->query($sqlfix);
3632  if (!$resqlfix) {
3633  dol_print_error($this->db, 'Failed to update line');
3634  }
3635  $obj->total_tva = $tmpcal[1];
3636  $obj->total_ttc = $tmpcal[2];
3637  }
3638  }
3639 
3640  $this->total_ht += $obj->total_ht; // The field visible at end of line detail
3641  $this->total_tva += $obj->total_tva;
3642  $this->total_localtax1 += $obj->total_localtax1;
3643  $this->total_localtax2 += $obj->total_localtax2;
3644  $this->total_ttc += $obj->total_ttc;
3645  $this->multicurrency_total_ht += $obj->multicurrency_total_ht; // The field visible at end of line detail
3646  $this->multicurrency_total_tva += $obj->multicurrency_total_tva;
3647  $this->multicurrency_total_ttc += $obj->multicurrency_total_ttc;
3648 
3649  if (!isset($total_ht_by_vats[$obj->vatrate])) {
3650  $total_ht_by_vats[$obj->vatrate] = 0;
3651  }
3652  if (!isset($total_tva_by_vats[$obj->vatrate])) {
3653  $total_tva_by_vats[$obj->vatrate] = 0;
3654  }
3655  if (!isset($total_ttc_by_vats[$obj->vatrate])) {
3656  $total_ttc_by_vats[$obj->vatrate] = 0;
3657  }
3658  $total_ht_by_vats[$obj->vatrate] += $obj->total_ht;
3659  $total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
3660  $total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
3661 
3662  if ($forcedroundingmode == '1') { // Check if we need adjustement onto line for vat. TODO This works on the company currency but not on foreign currency
3663  $tmpvat = price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
3664  $diff = price2num($total_tva_by_vats[$obj->vatrate] - $tmpvat, 'MT', 1);
3665  //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";
3666  if ($diff) {
3667  if (abs($diff) > (10 * pow(10, -1 * getDolGlobalInt('MAIN_MAX_DECIMALS_TOT', 0)))) {
3668  // If error is more than 10 times the accurancy of rounding. This should not happen.
3669  $errmsg = 'A rounding difference was detected into TOTAL but is too high to be corrected. Some data in your lines may be corrupted. Try to edit each line manually to fix this before restarting.';
3670  dol_syslog($errmsg, LOG_WARNING);
3671  $this->error = $errmsg;
3672  $error++;
3673  break;
3674  }
3675  $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldtva." = ".price2num($obj->total_tva - $diff).", total_ttc = ".price2num($obj->total_ttc - $diff)." WHERE rowid = ".((int) $obj->rowid);
3676  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);
3677 
3678  $resqlfix = $this->db->query($sqlfix);
3679 
3680  if (!$resqlfix) {
3681  dol_print_error($this->db, 'Failed to update line');
3682  }
3683 
3684  $this->total_tva = (float) price2num($this->total_tva - $diff, '', 1);
3685  $this->total_ttc = (float) price2num($this->total_ttc - $diff, '', 1);
3686  $total_tva_by_vats[$obj->vatrate] = (float) price2num($total_tva_by_vats[$obj->vatrate] - $diff, '', 1);
3687  $total_ttc_by_vats[$obj->vatrate] = (float) price2num($total_ttc_by_vats[$obj->vatrate] - $diff, '', 1);
3688  }
3689  }
3690 
3691  $i++;
3692  }
3693 
3694  // Add revenue stamp to total
3695  $this->total_ttc += isset($this->revenuestamp) ? $this->revenuestamp : 0;
3696  $this->multicurrency_total_ttc += isset($this->revenuestamp) ? ($this->revenuestamp * $multicurrency_tx) : 0;
3697 
3698  // Situations totals
3699  if (!empty($this->situation_cycle_ref) && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits') && $this->type != $this::TYPE_CREDIT_NOTE) {
3700  $prev_sits = $this->get_prev_sits();
3701 
3702  foreach ($prev_sits as $sit) { // $sit is an object Facture loaded with a fetch.
3703  $this->total_ht -= $sit->total_ht;
3704  $this->total_tva -= $sit->total_tva;
3705  $this->total_localtax1 -= $sit->total_localtax1;
3706  $this->total_localtax2 -= $sit->total_localtax2;
3707  $this->total_ttc -= $sit->total_ttc;
3708  $this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
3709  $this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
3710  $this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
3711  }
3712  }
3713 
3714  // Clean total
3715  $this->total_ht = (float) price2num($this->total_ht);
3716  $this->total_tva = (float) price2num($this->total_tva);
3717  $this->total_localtax1 = (float) price2num($this->total_localtax1);
3718  $this->total_localtax2 = (float) price2num($this->total_localtax2);
3719  $this->total_ttc = (float) price2num($this->total_ttc);
3720 
3721  $this->db->free($resql);
3722 
3723  // Now update global fields total_ht, total_ttc, total_tva, total_localtax1, total_localtax2, multicurrency_total_* of main object
3724  $fieldht = 'total_ht';
3725  $fieldtva = 'tva';
3726  $fieldlocaltax1 = 'localtax1';
3727  $fieldlocaltax2 = 'localtax2';
3728  $fieldttc = 'total_ttc';
3729  // Specific code for backward compatibility with old field names
3730  if (in_array($this->element, array('propal', 'commande', 'facture', 'facturerec', 'supplier_proposal', 'order_supplier', 'facture_fourn', 'invoice_supplier', 'invoice_supplier_rec', 'expensereport'))) {
3731  $fieldtva = 'total_tva';
3732  }
3733 
3734  if (!$error && empty($nodatabaseupdate)) {
3735  $sql = "UPDATE ".$this->db->prefix().$this->table_element.' SET';
3736  $sql .= " ".$fieldht." = ".((float) price2num($this->total_ht, 'MT', 1)).",";
3737  $sql .= " ".$fieldtva." = ".((float) price2num($this->total_tva, 'MT', 1)).",";
3738  $sql .= " ".$fieldlocaltax1." = ".((float) price2num($this->total_localtax1, 'MT', 1)).",";
3739  $sql .= " ".$fieldlocaltax2." = ".((float) price2num($this->total_localtax2, 'MT', 1)).",";
3740  $sql .= " ".$fieldttc." = ".((float) price2num($this->total_ttc, 'MT', 1));
3741  $sql .= ", multicurrency_total_ht = ".((float) price2num($this->multicurrency_total_ht, 'MT', 1));
3742  $sql .= ", multicurrency_total_tva = ".((float) price2num($this->multicurrency_total_tva, 'MT', 1));
3743  $sql .= ", multicurrency_total_ttc = ".((float) price2num($this->multicurrency_total_ttc, 'MT', 1));
3744  $sql .= " WHERE rowid = ".((int) $this->id);
3745 
3746  dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3747  $resql = $this->db->query($sql);
3748 
3749  if (!$resql) {
3750  $error++;
3751  $this->error = $this->db->lasterror();
3752  $this->errors[] = $this->db->lasterror();
3753  }
3754  }
3755 
3756  if (!$error) {
3757  $this->db->commit();
3758  return 1;
3759  } else {
3760  $this->db->rollback();
3761  return -1;
3762  }
3763  } else {
3764  dol_print_error($this->db, 'Bad request in update_price');
3765  return -1;
3766  }
3767  }
3768 
3769  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3780  public function add_object_linked($origin = null, $origin_id = null, $f_user = null, $notrigger = 0)
3781  {
3782  // phpcs:enable
3783  global $user, $hookmanager, $action;
3784  $origin = (!empty($origin) ? $origin : $this->origin);
3785  $origin_id = (!empty($origin_id) ? $origin_id : $this->origin_id);
3786  $f_user = isset($f_user) ? $f_user : $user;
3787 
3788  // Special case
3789  if ($origin == 'order') {
3790  $origin = 'commande';
3791  }
3792  if ($origin == 'invoice') {
3793  $origin = 'facture';
3794  }
3795  if ($origin == 'invoice_template') {
3796  $origin = 'facturerec';
3797  }
3798  if ($origin == 'supplierorder') {
3799  $origin = 'order_supplier';
3800  }
3801 
3802  // Elements of the core modules which have `$module` property but may to which we don't want to prefix module part to the element name for finding the linked object in llx_element_element.
3803  // It's because an entry for this element may be exist in llx_element_element before this modification (version <=14.2) and ave named only with their element name in fk_source or fk_target.
3804  $coremodule = array('knowledgemanagement', 'partnership', 'workstation', 'ticket', 'recruitment', 'eventorganization', 'asset');
3805  // Add module part to target type if object has $module property and isn't in core modules.
3806  $targettype = ((!empty($this->module) && ! in_array($this->module, $coremodule)) ? $this->module.'_' : '').$this->element;
3807 
3808  $parameters = array('targettype'=>$targettype);
3809  // Hook for explicitly set the targettype if it must be differtent than $this->element
3810  $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3811  if ($reshook > 0) {
3812  if (!empty($hookmanager->resArray['targettype'])) $targettype = $hookmanager->resArray['targettype'];
3813  }
3814 
3815  $this->db->begin();
3816  $error = 0;
3817 
3818  $sql = "INSERT INTO " . $this->db->prefix() . "element_element (";
3819  $sql .= "fk_source";
3820  $sql .= ", sourcetype";
3821  $sql .= ", fk_target";
3822  $sql .= ", targettype";
3823  $sql .= ") VALUES (";
3824  $sql .= ((int) $origin_id);
3825  $sql .= ", '" . $this->db->escape($origin) . "'";
3826  $sql .= ", " . ((int) $this->id);
3827  $sql .= ", '" . $this->db->escape($targettype) . "'";
3828  $sql .= ")";
3829 
3830  dol_syslog(get_class($this) . "::add_object_linked", LOG_DEBUG);
3831  if ($this->db->query($sql)) {
3832  if (!$notrigger) {
3833  // Call trigger
3834  $this->context['link_origin'] = $origin;
3835  $this->context['link_origin_id'] = $origin_id;
3836  $result = $this->call_trigger('OBJECT_LINK_INSERT', $f_user);
3837  if ($result < 0) {
3838  $error++;
3839  }
3840  // End call triggers
3841  }
3842  } else {
3843  $this->error = $this->db->lasterror();
3844  $error++;
3845  }
3846 
3847  if (!$error) {
3848  $this->db->commit();
3849  return 1;
3850  } else {
3851  $this->db->rollback();
3852  return 0;
3853  }
3854  }
3855 
3878  public function fetchObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1)
3879  {
3880  global $conf, $hookmanager, $action;
3881 
3882  // Important for pdf generation time reduction
3883  // This boolean is true if $this->linkedObjects has already been loaded with all objects linked without filter
3884  if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
3885  return 1;
3886  }
3887 
3888  $this->linkedObjectsIds = array();
3889  $this->linkedObjects = array();
3890 
3891  $justsource = false;
3892  $justtarget = false;
3893  $withtargettype = false;
3894  $withsourcetype = false;
3895 
3896  $parameters = array('sourcetype'=>$sourcetype, 'sourceid'=>$sourceid, 'targettype'=>$targettype, 'targetid'=>$targetid);
3897  // Hook for explicitly set the targettype if it must be differtent than $this->element
3898  $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3899  if ($reshook > 0) {
3900  if (!empty($hookmanager->resArray['sourcetype'])) $sourcetype = $hookmanager->resArray['sourcetype'];
3901  if (!empty($hookmanager->resArray['sourceid'])) $sourceid = $hookmanager->resArray['sourceid'];
3902  if (!empty($hookmanager->resArray['targettype'])) $targettype = $hookmanager->resArray['targettype'];
3903  if (!empty($hookmanager->resArray['targetid'])) $targetid = $hookmanager->resArray['targetid'];
3904  }
3905 
3906  if (!empty($sourceid) && !empty($sourcetype) && empty($targetid)) {
3907  $justsource = true; // the source (id and type) is a search criteria
3908  if (!empty($targettype)) {
3909  $withtargettype = true;
3910  }
3911  }
3912  if (!empty($targetid) && !empty($targettype) && empty($sourceid)) {
3913  $justtarget = true; // the target (id and type) is a search criteria
3914  if (!empty($sourcetype)) {
3915  $withsourcetype = true;
3916  }
3917  }
3918 
3919  $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
3920  $targetid = (!empty($targetid) ? $targetid : $this->id);
3921  $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
3922  $targettype = (!empty($targettype) ? $targettype : $this->element);
3923 
3924  /*if (empty($sourceid) && empty($targetid))
3925  {
3926  dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
3927  return -1;
3928  }*/
3929 
3930  // Links between objects are stored in table element_element
3931  $sql = "SELECT rowid, fk_source, sourcetype, fk_target, targettype";
3932  $sql .= " FROM ".$this->db->prefix()."element_element";
3933  $sql .= " WHERE ";
3934  if ($justsource || $justtarget) {
3935  if ($justsource) {
3936  $sql .= "fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."'";
3937  if ($withtargettype) {
3938  $sql .= " AND targettype = '".$this->db->escape($targettype)."'";
3939  }
3940  } elseif ($justtarget) {
3941  $sql .= "fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."'";
3942  if ($withsourcetype) {
3943  $sql .= " AND sourcetype = '".$this->db->escape($sourcetype)."'";
3944  }
3945  }
3946  } else {
3947  $sql .= "(fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."')";
3948  $sql .= " ".$clause." (fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."')";
3949  if ($loadalsoobjects && $this->id > 0 && $sourceid == $this->id && $sourcetype == $this->element && $targetid == $this->id && $targettype == $this->element && $clause == 'OR') {
3950  $this->linkedObjectsFullLoaded[$this->id] = true;
3951  }
3952  }
3953  $sql .= " ORDER BY ".$orderby;
3954 
3955  dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
3956  $resql = $this->db->query($sql);
3957  if ($resql) {
3958  $num = $this->db->num_rows($resql);
3959  $i = 0;
3960  while ($i < $num) {
3961  $obj = $this->db->fetch_object($resql);
3962  if ($justsource || $justtarget) {
3963  if ($justsource) {
3964  $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
3965  } elseif ($justtarget) {
3966  $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
3967  }
3968  } else {
3969  if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype) {
3970  $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
3971  }
3972  if ($obj->fk_target == $targetid && $obj->targettype == $targettype) {
3973  $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
3974  }
3975  }
3976  $i++;
3977  }
3978 
3979  if (!empty($this->linkedObjectsIds)) {
3980  $tmparray = $this->linkedObjectsIds;
3981  foreach ($tmparray as $objecttype => $objectids) { // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
3982  // Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
3983  $module = $element = $subelement = $objecttype;
3984  $regs = array();
3985  if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
3986  && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
3987  $module = $element = $regs[1];
3988  $subelement = $regs[2];
3989  }
3990 
3991  $classpath = $element.'/class';
3992  // To work with non standard classpath or module name
3993  if ($objecttype == 'facture') {
3994  $classpath = 'compta/facture/class';
3995  } elseif ($objecttype == 'facturerec') {
3996  $classpath = 'compta/facture/class';
3997  $module = 'facture';
3998  } elseif ($objecttype == 'propal') {
3999  $classpath = 'comm/propal/class';
4000  } elseif ($objecttype == 'supplier_proposal') {
4001  $classpath = 'supplier_proposal/class';
4002  } elseif ($objecttype == 'shipping') {
4003  $classpath = 'expedition/class';
4004  $subelement = 'expedition';
4005  $module = 'expedition';
4006  } elseif ($objecttype == 'delivery') {
4007  $classpath = 'delivery/class';
4008  $subelement = 'delivery';
4009  $module = 'delivery_note';
4010  } elseif ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier') {
4011  $classpath = 'fourn/class';
4012  $module = 'fournisseur';
4013  } elseif ($objecttype == 'fichinter') {
4014  $classpath = 'fichinter/class';
4015  $subelement = 'fichinter';
4016  $module = 'ficheinter';
4017  } elseif ($objecttype == 'subscription') {
4018  $classpath = 'adherents/class';
4019  $module = 'adherent';
4020  } elseif ($objecttype == 'contact') {
4021  $module = 'societe';
4022  }
4023  // Set classfile
4024  $classfile = strtolower($subelement);
4025  $classname = ucfirst($subelement);
4026 
4027  if ($objecttype == 'order') {
4028  $classfile = 'commande';
4029  $classname = 'Commande';
4030  } elseif ($objecttype == 'invoice_supplier') {
4031  $classfile = 'fournisseur.facture';
4032  $classname = 'FactureFournisseur';
4033  } elseif ($objecttype == 'order_supplier') {
4034  $classfile = 'fournisseur.commande';
4035  $classname = 'CommandeFournisseur';
4036  } elseif ($objecttype == 'supplier_proposal') {
4037  $classfile = 'supplier_proposal';
4038  $classname = 'SupplierProposal';
4039  } elseif ($objecttype == 'facturerec') {
4040  $classfile = 'facture-rec';
4041  $classname = 'FactureRec';
4042  } elseif ($objecttype == 'subscription') {
4043  $classfile = 'subscription';
4044  $classname = 'Subscription';
4045  } elseif ($objecttype == 'project' || $objecttype == 'projet') {
4046  $classpath = 'projet/class';
4047  $classfile = 'project';
4048  $classname = 'Project';
4049  } elseif ($objecttype == 'conferenceorboothattendee') {
4050  $classpath = 'eventorganization/class';
4051  $classfile = 'conferenceorboothattendee';
4052  $classname = 'ConferenceOrBoothAttendee';
4053  $module = 'eventorganization';
4054  } elseif ($objecttype == 'conferenceorbooth') {
4055  $classpath = 'eventorganization/class';
4056  $classfile = 'conferenceorbooth';
4057  $classname = 'ConferenceOrBooth';
4058  $module = 'eventorganization';
4059  } elseif ($objecttype == 'mo') {
4060  $classpath = 'mrp/class';
4061  $classfile = 'mo';
4062  $classname = 'Mo';
4063  $module = 'mrp';
4064  }
4065 
4066  // Here $module, $classfile and $classname are set, we can use them.
4067  if (isModEnabled($module) && (($element != $this->element) || $alsosametype)) {
4068  if ($loadalsoobjects && (is_numeric($loadalsoobjects) || ($loadalsoobjects === $objecttype))) {
4069  dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
4070  //print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
4071  if (class_exists($classname)) {
4072  foreach ($objectids as $i => $objectid) { // $i is rowid into llx_element_element
4073  $object = new $classname($this->db);
4074  $ret = $object->fetch($objectid);
4075  if ($ret >= 0) {
4076  $this->linkedObjects[$objecttype][$i] = $object;
4077  }
4078  }
4079  }
4080  }
4081  } else {
4082  unset($this->linkedObjectsIds[$objecttype]);
4083  }
4084  }
4085  }
4086  return 1;
4087  } else {
4088  dol_print_error($this->db);
4089  return -1;
4090  }
4091  }
4092 
4099  public function clearObjectLinkedCache()
4100  {
4101  if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
4102  unset($this->linkedObjectsFullLoaded[$this->id]);
4103  }
4104 
4105  return 1;
4106  }
4107 
4120  public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $f_user = null, $notrigger = 0)
4121  {
4122  global $user;
4123  $updatesource = false;
4124  $updatetarget = false;
4125  $f_user = isset($f_user) ? $f_user : $user;
4126 
4127  if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4128  $updatesource = true;
4129  } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4130  $updatetarget = true;
4131  }
4132 
4133  $this->db->begin();
4134  $error = 0;
4135 
4136  $sql = "UPDATE " . $this->db->prefix() . "element_element SET ";
4137  if ($updatesource) {
4138  $sql .= "fk_source = " . ((int) $sourceid);
4139  $sql .= ", sourcetype = '" . $this->db->escape($sourcetype) . "'";
4140  $sql .= " WHERE fk_target = " . ((int) $this->id);
4141  $sql .= " AND targettype = '" . $this->db->escape($this->element) . "'";
4142  } elseif ($updatetarget) {
4143  $sql .= "fk_target = " . ((int) $targetid);
4144  $sql .= ", targettype = '" . $this->db->escape($targettype) . "'";
4145  $sql .= " WHERE fk_source = " . ((int) $this->id);
4146  $sql .= " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4147  }
4148 
4149  dol_syslog(get_class($this) . "::updateObjectLinked", LOG_DEBUG);
4150  if ($this->db->query($sql)) {
4151  if (!$notrigger) {
4152  // Call trigger
4153  $this->context['link_source_id'] = $sourceid;
4154  $this->context['link_source_type'] = $sourcetype;
4155  $this->context['link_target_id'] = $targetid;
4156  $this->context['link_target_type'] = $targettype;
4157  $result = $this->call_trigger('OBJECT_LINK_MODIFY', $f_user);
4158  if ($result < 0) {
4159  $error++;
4160  }
4161  // End call triggers
4162  }
4163  } else {
4164  $this->error = $this->db->lasterror();
4165  $error++;
4166  }
4167 
4168  if (!$error) {
4169  $this->db->commit();
4170  return 1;
4171  } else {
4172  $this->db->rollback();
4173  return -1;
4174  }
4175  }
4176 
4190  public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = '', $f_user = null, $notrigger = 0)
4191  {
4192  global $user;
4193  $deletesource = false;
4194  $deletetarget = false;
4195  $f_user = isset($f_user) ? $f_user : $user;
4196 
4197  if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4198  $deletesource = true;
4199  } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4200  $deletetarget = true;
4201  }
4202 
4203  $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
4204  $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
4205  $targetid = (!empty($targetid) ? $targetid : $this->id);
4206  $targettype = (!empty($targettype) ? $targettype : $this->element);
4207  $this->db->begin();
4208  $error = 0;
4209 
4210  if (!$notrigger) {
4211  // Call trigger
4212  $this->context['link_id'] = $rowid;
4213  $this->context['link_source_id'] = $sourceid;
4214  $this->context['link_source_type'] = $sourcetype;
4215  $this->context['link_target_id'] = $targetid;
4216  $this->context['link_target_type'] = $targettype;
4217  $result = $this->call_trigger('OBJECT_LINK_DELETE', $f_user);
4218  if ($result < 0) {
4219  $error++;
4220  }
4221  // End call triggers
4222  }
4223 
4224  if (!$error) {
4225  $sql = "DELETE FROM " . $this->db->prefix() . "element_element";
4226  $sql .= " WHERE";
4227  if ($rowid > 0) {
4228  $sql .= " rowid = " . ((int) $rowid);
4229  } else {
4230  if ($deletesource) {
4231  $sql .= " fk_source = " . ((int) $sourceid) . " AND sourcetype = '" . $this->db->escape($sourcetype) . "'";
4232  $sql .= " AND fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "'";
4233  } elseif ($deletetarget) {
4234  $sql .= " fk_target = " . ((int) $targetid) . " AND targettype = '" . $this->db->escape($targettype) . "'";
4235  $sql .= " AND fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4236  } else {
4237  $sql .= " (fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "')";
4238  $sql .= " OR";
4239  $sql .= " (fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "')";
4240  }
4241  }
4242 
4243  dol_syslog(get_class($this) . "::deleteObjectLinked", LOG_DEBUG);
4244  if (!$this->db->query($sql)) {
4245  $this->error = $this->db->lasterror();
4246  $this->errors[] = $this->error;
4247  $error++;
4248  }
4249  }
4250 
4251  if (!$error) {
4252  $this->db->commit();
4253  return 1;
4254  } else {
4255  $this->db->rollback();
4256  return 0;
4257  }
4258  }
4259 
4269  public static function getAllItemsLinkedByObjectID($fk_object_where, $field_select, $field_where, $table_element)
4270  {
4271  if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4272  return -1;
4273  }
4274 
4275  global $db;
4276 
4277  $sql = "SELECT ".$field_select." FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4278  $resql = $db->query($sql);
4279 
4280  $TRes = array();
4281  if (!empty($resql)) {
4282  while ($res = $db->fetch_object($resql)) {
4283  $TRes[] = $res->{$field_select};
4284  }
4285  }
4286 
4287  return $TRes;
4288  }
4289 
4298  public static function deleteAllItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
4299  {
4300  if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4301  return -1;
4302  }
4303 
4304  global $db;
4305 
4306  $sql = "DELETE FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4307  $resql = $db->query($sql);
4308 
4309  if (empty($resql)) {
4310  return 0;
4311  }
4312 
4313  return 1;
4314  }
4315 
4326  public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '', $fieldstatus = 'fk_statut')
4327  {
4328  global $user, $langs, $conf;
4329 
4330  $savElementId = $elementId; // To be used later to know if we were using the method using the id of this or not.
4331 
4332  $elementId = (!empty($elementId) ? $elementId : $this->id);
4333  $elementTable = (!empty($elementType) ? $elementType : $this->table_element);
4334 
4335  $this->db->begin();
4336 
4337  if ($elementTable == 'facture_rec') {
4338  $fieldstatus = "suspended";
4339  }
4340  if ($elementTable == 'mailing') {
4341  $fieldstatus = "statut";
4342  }
4343  if ($elementTable == 'cronjob') {
4344  $fieldstatus = "status";
4345  }
4346  if ($elementTable == 'user') {
4347  $fieldstatus = "statut";
4348  }
4349  if ($elementTable == 'expensereport') {
4350  $fieldstatus = "fk_statut";
4351  }
4352  if ($elementTable == 'commande_fournisseur_dispatch') {
4353  $fieldstatus = "status";
4354  }
4355  if (isset($this->fields) && is_array($this->fields) && array_key_exists('status', $this->fields)) {
4356  $fieldstatus = 'status';
4357  }
4358 
4359  $sql = "UPDATE ".$this->db->prefix().$elementTable;
4360  $sql .= " SET ".$fieldstatus." = ".((int) $status);
4361  // If status = 1 = validated, update also fk_user_valid
4362  // TODO Replace the test on $elementTable by doing a test on existence of the field in $this->fields
4363  if ($status == 1 && in_array($elementTable, array('expensereport', 'inventory'))) {
4364  $sql .= ", fk_user_valid = ".((int) $user->id);
4365  }
4366  if ($status == 1 && in_array($elementTable, array('expensereport'))) {
4367  $sql .= ", date_valid = '".$this->db->idate(dol_now())."'";
4368  }
4369  if ($status == 1 && in_array($elementTable, array('inventory'))) {
4370  $sql .= ", date_validation = '".$this->db->idate(dol_now())."'";
4371  }
4372  $sql .= " WHERE rowid = ".((int) $elementId);
4373  $sql .= " AND ".$fieldstatus." <> ".((int) $status); // We avoid update if status already correct
4374 
4375  dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
4376  $resql = $this->db->query($sql);
4377  if ($resql) {
4378  $error = 0;
4379 
4380  $nb_rows_affected = $this->db->affected_rows($resql); // should be 1 or 0 if status was already correct
4381 
4382  if ($nb_rows_affected > 0) {
4383  if (empty($trigkey)) {
4384  // Try to guess trigkey (for backward compatibility, now we should have trigkey defined into the call of setStatus)
4385  if ($this->element == 'supplier_proposal' && $status == 2) {
4386  $trigkey = 'SUPPLIER_PROPOSAL_SIGN'; // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
4387  }
4388  if ($this->element == 'supplier_proposal' && $status == 3) {
4389  $trigkey = 'SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
4390  }
4391  if ($this->element == 'supplier_proposal' && $status == 4) {
4392  $trigkey = 'SUPPLIER_PROPOSAL_CLOSE'; // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
4393  }
4394  if ($this->element == 'fichinter' && $status == 3) {
4395  $trigkey = 'FICHINTER_CLASSIFY_DONE';
4396  }
4397  if ($this->element == 'fichinter' && $status == 2) {
4398  $trigkey = 'FICHINTER_CLASSIFY_BILLED';
4399  }
4400  if ($this->element == 'fichinter' && $status == 1) {
4401  $trigkey = 'FICHINTER_CLASSIFY_UNBILLED';
4402  }
4403  }
4404 
4405  if ($trigkey) {
4406  // Call trigger
4407  $result = $this->call_trigger($trigkey, $user);
4408  if ($result < 0) {
4409  $error++;
4410  }
4411  // End call triggers
4412  }
4413  } else {
4414  // The status was probably already good. We do nothing more, no triggers.
4415  }
4416 
4417  if (!$error) {
4418  $this->db->commit();
4419 
4420  if (empty($savElementId)) {
4421  // If the element we update is $this (so $elementId was provided as null)
4422  if ($fieldstatus == 'tosell') {
4423  $this->status = $status;
4424  } elseif ($fieldstatus == 'tobuy') {
4425  $this->status_buy = $status;
4426  } else {
4427  $this->statut = $status;
4428  $this->status = $status;
4429  }
4430  }
4431 
4432  return 1;
4433  } else {
4434  $this->db->rollback();
4435  dol_syslog(get_class($this)."::setStatut ".$this->error, LOG_ERR);
4436  return -1;
4437  }
4438  } else {
4439  $this->error = $this->db->lasterror();
4440  $this->db->rollback();
4441  return -1;
4442  }
4443  }
4444 
4445 
4453  public function getCanvas($id = 0, $ref = '')
4454  {
4455  global $conf;
4456 
4457  if (empty($id) && empty($ref)) {
4458  return 0;
4459  }
4460  if (!empty($conf->global->MAIN_DISABLE_CANVAS)) {
4461  return 0; // To increase speed. Not enabled by default.
4462  }
4463 
4464  // Clean parameters
4465  $ref = trim($ref);
4466 
4467  $sql = "SELECT rowid, canvas";
4468  $sql .= " FROM ".$this->db->prefix().$this->table_element;
4469  $sql .= " WHERE entity IN (".getEntity($this->element).")";
4470  if (!empty($id)) {
4471  $sql .= " AND rowid = ".((int) $id);
4472  }
4473  if (!empty($ref)) {
4474  $sql .= " AND ref = '".$this->db->escape($ref)."'";
4475  }
4476 
4477  $resql = $this->db->query($sql);
4478  if ($resql) {
4479  $obj = $this->db->fetch_object($resql);
4480  if ($obj) {
4481  $this->canvas = $obj->canvas;
4482  return 1;
4483  } else {
4484  return 0;
4485  }
4486  } else {
4487  dol_print_error($this->db);
4488  return -1;
4489  }
4490  }
4491 
4492 
4499  public function getSpecialCode($lineid)
4500  {
4501  $sql = "SELECT special_code FROM ".$this->db->prefix().$this->table_element_line;
4502  $sql .= " WHERE rowid = ".((int) $lineid);
4503  $resql = $this->db->query($sql);
4504  if ($resql) {
4505  $row = $this->db->fetch_row($resql);
4506  return $row[0];
4507  }
4508 
4509  return 0;
4510  }
4511 
4520  public function isObjectUsed($id = 0, $entity = 0)
4521  {
4522  global $langs;
4523 
4524  if (empty($id)) {
4525  $id = $this->id;
4526  }
4527 
4528  // Check parameters
4529  if (!isset($this->childtables) || !is_array($this->childtables) || count($this->childtables) == 0) {
4530  dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
4531  return -1;
4532  }
4533 
4534  $arraytoscan = $this->childtables; // array('tablename'=>array('fk_element'=>'parentfield'), ...) or array('tablename'=>array('parent'=>table_parent, 'parentkey'=>'nameoffieldforparentfkkey'), ...)
4535  // For backward compatibility, we check if array is old format array('tablename1', 'tablename2', ...)
4536  $tmparray = array_keys($this->childtables);
4537  if (is_numeric($tmparray[0])) {
4538  $arraytoscan = array_flip($this->childtables);
4539  }
4540 
4541  // Test if child exists
4542  $haschild = 0;
4543  foreach ($arraytoscan as $table => $element) {
4544  //print $id.'-'.$table.'-'.$elementname.'<br>';
4545  // Check if element can be deleted
4546  $sql = "SELECT COUNT(*) as nb";
4547  $sql.= " FROM ".$this->db->prefix().$table." as c";
4548  if (!empty($element['parent']) && !empty($element['parentkey'])) {
4549  $sql.= ", ".$this->db->prefix().$element['parent']." as p";
4550  }
4551  if (!empty($element['fk_element'])) {
4552  $sql.= " WHERE c.".$element['fk_element']." = ".((int) $id);
4553  } else {
4554  $sql.= " WHERE c.".$this->fk_element." = ".((int) $id);
4555  }
4556  if (!empty($element['parent']) && !empty($element['parentkey'])) {
4557  $sql.= " AND c.".$element['parentkey']." = p.rowid";
4558  }
4559  if (!empty($element['parent']) && !empty($element['parenttypefield']) && !empty($element['parenttypevalue'])) {
4560  $sql.= " AND c.".$element['parenttypefield']." = '".$this->db->escape($element['parenttypevalue'])."'";
4561  }
4562  if (!empty($entity)) {
4563  if (!empty($element['parent']) && !empty($element['parentkey'])) {
4564  $sql.= " AND p.entity = ".((int) $entity);
4565  } else {
4566  $sql.= " AND c.entity = ".((int) $entity);
4567  }
4568  }
4569 
4570  $resql = $this->db->query($sql);
4571  if ($resql) {
4572  $obj = $this->db->fetch_object($resql);
4573  if ($obj->nb > 0) {
4574  $langs->load("errors");
4575  //print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
4576  $haschild += $obj->nb;
4577  if (is_numeric($element)) { // very old usage array('table1', 'table2', ...)
4578  $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $table);
4579  } elseif (is_string($element)) { // old usage array('table1' => 'TranslateKey1', 'table2' => 'TranslateKey2', ...)
4580  $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element));
4581  } else { // new usage: $element['name']=Translation key
4582  $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element['name']));
4583  }
4584  break; // We found at least one, we stop here
4585  }
4586  } else {
4587  $this->errors[] = $this->db->lasterror();
4588  return -1;
4589  }
4590  }
4591  if ($haschild > 0) {
4592  $this->errors[] = "ErrorRecordHasChildren";
4593  return $haschild;
4594  } else {
4595  return 0;
4596  }
4597  }
4598 
4605  public function hasProductsOrServices($predefined = -1)
4606  {
4607  $nb = 0;
4608 
4609  foreach ($this->lines as $key => $val) {
4610  $qualified = 0;
4611  if ($predefined == -1) {
4612  $qualified = 1;
4613  }
4614  if ($predefined == 1 && $val->fk_product > 0) {
4615  $qualified = 1;
4616  }
4617  if ($predefined == 0 && $val->fk_product <= 0) {
4618  $qualified = 1;
4619  }
4620  if ($predefined == 2 && $val->fk_product > 0 && $val->product_type == 0) {
4621  $qualified = 1;
4622  }
4623  if ($predefined == 3 && $val->fk_product > 0 && $val->product_type == 1) {
4624  $qualified = 1;
4625  }
4626  if ($qualified) {
4627  $nb++;
4628  }
4629  }
4630  dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
4631  return $nb;
4632  }
4633 
4639  public function getTotalDiscount()
4640  {
4641  if (!empty($this->table_element_line) ) {
4642  $total_discount = 0.00;
4643 
4644  $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
4645  $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
4646  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
4647 
4648  dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
4649  $resql = $this->db->query($sql);
4650  if ($resql) {
4651  $num = $this->db->num_rows($resql);
4652  $i = 0;
4653  while ($i < $num) {
4654  $obj = $this->db->fetch_object($resql);
4655 
4656  $pu_ht = $obj->pu_ht;
4657  $qty = $obj->qty;
4658  $total_ht = $obj->total_ht;
4659 
4660  $total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
4661  $total_discount += $total_discount_line;
4662 
4663  $i++;
4664  }
4665  }
4666 
4667  //print $total_discount; exit;
4668  return price2num($total_discount);
4669  }
4670 
4671  return null;
4672  }
4673 
4674 
4681  public function getTotalWeightVolume()
4682  {
4683  $totalWeight = 0;
4684  $totalVolume = 0;
4685  // defined for shipment only
4686  $totalOrdered = '';
4687  // defined for shipment only
4688  $totalToShip = '';
4689 
4690  foreach ($this->lines as $line) {
4691  if (isset($line->qty_asked)) {
4692  if (empty($totalOrdered)) {
4693  $totalOrdered = 0; // Avoid warning because $totalOrdered is ''
4694  }
4695  $totalOrdered += $line->qty_asked; // defined for shipment only
4696  }
4697  if (isset($line->qty_shipped)) {
4698  if (empty($totalToShip)) {
4699  $totalToShip = 0; // Avoid warning because $totalToShip is ''
4700  }
4701  $totalToShip += $line->qty_shipped; // defined for shipment only
4702  } elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty)) {
4703  if (empty($totalToShip)) {
4704  $totalToShip = 0;
4705  }
4706  $totalToShip += $line->qty; // defined for reception only
4707  }
4708 
4709  // Define qty, weight, volume, weight_units, volume_units
4710  if ($this->element == 'shipping') {
4711  // for shipments
4712  $qty = $line->qty_shipped ? $line->qty_shipped : 0;
4713  } else {
4714  $qty = $line->qty ? $line->qty : 0;
4715  }
4716 
4717  $weight = !empty($line->weight) ? $line->weight : 0;
4718  ($weight == 0 && !empty($line->product->weight)) ? $weight = $line->product->weight : 0;
4719  $volume = !empty($line->volume) ? $line->volume : 0;
4720  ($volume == 0 && !empty($line->product->volume)) ? $volume = $line->product->volume : 0;
4721 
4722  $weight_units = !empty($line->weight_units) ? $line->weight_units : 0;
4723  ($weight_units == 0 && !empty($line->product->weight_units)) ? $weight_units = $line->product->weight_units : 0;
4724  $volume_units = !empty($line->volume_units) ? $line->volume_units : 0;
4725  ($volume_units == 0 && !empty($line->product->volume_units)) ? $volume_units = $line->product->volume_units : 0;
4726 
4727  $weightUnit = 0;
4728  $volumeUnit = 0;
4729  if (!empty($weight_units)) {
4730  $weightUnit = $weight_units;
4731  }
4732  if (!empty($volume_units)) {
4733  $volumeUnit = $volume_units;
4734  }
4735 
4736  if (empty($totalWeight)) {
4737  $totalWeight = 0; // Avoid warning because $totalWeight is ''
4738  }
4739  if (empty($totalVolume)) {
4740  $totalVolume = 0; // Avoid warning because $totalVolume is ''
4741  }
4742 
4743  //var_dump($line->volume_units);
4744  if ($weight_units < 50) { // < 50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
4745  $trueWeightUnit = pow(10, $weightUnit);
4746  $totalWeight += $weight * $qty * $trueWeightUnit;
4747  } else {
4748  if ($weight_units == 99) {
4749  // conversion 1 Pound = 0.45359237 KG
4750  $trueWeightUnit = 0.45359237;
4751  $totalWeight += $weight * $qty * $trueWeightUnit;
4752  } elseif ($weight_units == 98) {
4753  // conversion 1 Ounce = 0.0283495 KG
4754  $trueWeightUnit = 0.0283495;
4755  $totalWeight += $weight * $qty * $trueWeightUnit;
4756  } else {
4757  $totalWeight += $weight * $qty; // This may be wrong if we mix different units
4758  }
4759  }
4760  if ($volume_units < 50) { // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
4761  //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
4762  $trueVolumeUnit = pow(10, $volumeUnit);
4763  //print $line->volume;
4764  $totalVolume += $volume * $qty * $trueVolumeUnit;
4765  } else {
4766  $totalVolume += $volume * $qty; // This may be wrong if we mix different units
4767  }
4768  }
4769 
4770  return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
4771  }
4772 
4773 
4779  public function setExtraParameters()
4780  {
4781  $this->db->begin();
4782 
4783  $extraparams = (!empty($this->extraparams) ? json_encode($this->extraparams) : null);
4784 
4785  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
4786  $sql .= " SET extraparams = ".(!empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
4787  $sql .= " WHERE rowid = ".((int) $this->id);
4788 
4789  dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
4790  $resql = $this->db->query($sql);
4791  if (!$resql) {
4792  $this->error = $this->db->lasterror();
4793  $this->db->rollback();
4794  return -1;
4795  } else {
4796  $this->db->commit();
4797  return 1;
4798  }
4799  }
4800 
4801 
4802  // --------------------
4803  // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
4804  // --------------------
4805 
4806  /* This is to show add lines */
4807 
4817  public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl')
4818  {
4819  global $conf, $user, $langs, $object, $hookmanager, $extrafields;
4820  global $form;
4821 
4822  // Line extrafield
4823  if (!is_object($extrafields)) {
4824  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4825  $extrafields = new ExtraFields($this->db);
4826  }
4827  $extrafields->fetch_name_optionals_label($this->table_element_line);
4828 
4829  // Output template part (modules that overwrite templates must declare this into descriptor)
4830  // Use global variables + $dateSelector + $seller and $buyer
4831  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'.
4832  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4833  foreach ($dirtpls as $module => $reldir) {
4834  if (!empty($module)) {
4835  $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
4836  } else {
4837  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php';
4838  }
4839 
4840  if (empty($conf->file->strict_mode)) {
4841  $res = @include $tpl;
4842  } else {
4843  $res = include $tpl; // for debug
4844  }
4845  if ($res) {
4846  break;
4847  }
4848  }
4849  }
4850 
4851 
4852 
4853  /* This is to show array of line of details */
4854 
4855 
4870  public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl')
4871  {
4872  global $conf, $hookmanager, $langs, $user, $form, $extrafields, $object;
4873  // TODO We should not use global var for this
4874  global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
4875 
4876  // Define usemargins
4877  $usemargins = 0;
4878  if (isModEnabled('margin') && !empty($this->element) && in_array($this->element, array('facture', 'facturerec', 'propal', 'commande'))) {
4879  $usemargins = 1;
4880  }
4881 
4882  $num = count($this->lines);
4883 
4884  // Line extrafield
4885  if (!is_object($extrafields)) {
4886  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4887  $extrafields = new ExtraFields($this->db);
4888  }
4889  $extrafields->fetch_name_optionals_label($this->table_element_line);
4890 
4891  $parameters = array('num'=>$num, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$this->table_element_line);
4892  $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4893  if (empty($reshook)) {
4894  // Output template part (modules that overwrite templates must declare this into descriptor)
4895  // Use global variables + $dateSelector + $seller and $buyer
4896  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook.
4897  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4898  foreach ($dirtpls as $module => $reldir) {
4899  $res = 0;
4900  if (!empty($module)) {
4901  $tpl = dol_buildpath($reldir.'/objectline_title.tpl.php');
4902  } else {
4903  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php';
4904  }
4905  if (file_exists($tpl)) {
4906  if (empty($conf->file->strict_mode)) {
4907  $res = @include $tpl;
4908  } else {
4909  $res = include $tpl; // for debug
4910  }
4911  }
4912  if ($res) {
4913  break;
4914  }
4915  }
4916  }
4917 
4918  $i = 0;
4919 
4920  print "<!-- begin printObjectLines() --><tbody>\n";
4921  foreach ($this->lines as $line) {
4922  //Line extrafield
4923  $line->fetch_optionals();
4924 
4925  //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line)))
4926  if (is_object($hookmanager)) { // Old code is commented on preceding line.
4927  if (empty($line->fk_parent_line)) {
4928  $parameters = array('line'=>$line, 'num'=>$num, 'i'=>$i, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$line->table_element);
4929  $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4930  } else {
4931  $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);
4932  $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4933  }
4934  }
4935  if (empty($reshook)) {
4936  $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafields, $defaulttpldir);
4937  }
4938 
4939  $i++;
4940  }
4941  print "</tbody><!-- end printObjectLines() -->\n";
4942  }
4943 
4961  public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/core/tpl')
4962  {
4963  global $conf, $langs, $user, $object, $hookmanager;
4964  global $form;
4965  global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
4966 
4967  $object_rights = $this->getRights();
4968 
4969  $text = '';
4970  $description = '';
4971 
4972  // Line in view mode
4973  if ($action != 'editline' || $selected != $line->id) {
4974  // Product
4975  if (!empty($line->fk_product) && $line->fk_product > 0) {
4976  $product_static = new Product($this->db);
4977  $product_static->fetch($line->fk_product);
4978 
4979  $product_static->ref = $line->ref; //can change ref in hook
4980  $product_static->label = !empty($line->label) ? $line->label : ""; //can change label in hook
4981 
4982  $text = $product_static->getNomUrl(1);
4983 
4984  // Define output language and label
4985  if (getDolGlobalInt('MAIN_MULTILANGS')) {
4986  if (property_exists($this, 'socid') && !is_object($this->thirdparty)) {
4987  dol_print_error('', 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
4988  return;
4989  }
4990 
4991  $prod = new Product($this->db);
4992  $prod->fetch($line->fk_product);
4993 
4994  $outputlangs = $langs;
4995  $newlang = '';
4996  if (empty($newlang) && GETPOST('lang_id', 'aZ09')) {
4997  $newlang = GETPOST('lang_id', 'aZ09');
4998  }
4999  if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang) && is_object($this->thirdparty)) {
5000  $newlang = $this->thirdparty->default_lang; // To use language of customer
5001  }
5002  if (!empty($newlang)) {
5003  $outputlangs = new Translate("", $conf);
5004  $outputlangs->setDefaultLang($newlang);
5005  }
5006 
5007  $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
5008  } else {
5009  $label = $line->product_label;
5010  }
5011 
5012  $text .= ' - '.(!empty($line->label) ? $line->label : $label);
5013  $description .= (getDolGlobalInt('PRODUIT_DESC_IN_FORM_ACCORDING_TO_DEVICE') ? '' : (!empty($line->description) ? dol_htmlentitiesbr($line->description) : '')); // Description is what to show on popup. We shown nothing if already into desc.
5014  }
5015 
5016  $line->pu_ttc = price2num((!empty($line->subprice) ? $line->subprice : 0) * (1 + ((!empty($line->tva_tx) ? $line->tva_tx : 0) / 100)), 'MU');
5017 
5018  // Output template part (modules that overwrite templates must declare this into descriptor)
5019  // Use global variables + $dateSelector + $seller and $buyer
5020  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5021  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5022  foreach ($dirtpls as $module => $reldir) {
5023  $res = 0;
5024  if (!empty($module)) {
5025  $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
5026  } else {
5027  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php';
5028  }
5029  if (file_exists($tpl)) {
5030  if (empty($conf->file->strict_mode)) {
5031  $res = @include $tpl;
5032  } else {
5033  $res = include $tpl; // for debug
5034  }
5035  }
5036  if ($res) {
5037  break;
5038  }
5039  }
5040  }
5041 
5042  // Line in update mode
5043  if ($this->statut == 0 && $action == 'editline' && $selected == $line->id) {
5044  $label = (!empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
5045 
5046  $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU');
5047 
5048  // Output template part (modules that overwrite templates must declare this into descriptor)
5049  // Use global variables + $dateSelector + $seller and $buyer
5050  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5051  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5052  foreach ($dirtpls as $module => $reldir) {
5053  if (!empty($module)) {
5054  $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
5055  } else {
5056  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php';
5057  }
5058 
5059  if (empty($conf->file->strict_mode)) {
5060  $res = @include $tpl;
5061  } else {
5062  $res = include $tpl; // for debug
5063  }
5064  if ($res) {
5065  break;
5066  }
5067  }
5068  }
5069  }
5070 
5071 
5072  /* This is to show array of line of details of source object */
5073 
5074 
5085  public function printOriginLinesList($restrictlist = '', $selectedLines = array())
5086  {
5087  global $langs, $hookmanager, $conf, $form, $action;
5088 
5089  print '<tr class="liste_titre">';
5090  print '<td class="linecolref">'.$langs->trans('Ref').'</td>';
5091  print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
5092  print '<td class="linecolvat right">'.$langs->trans('VATRate').'</td>';
5093  print '<td class="linecoluht right">'.$langs->trans('PriceUHT').'</td>';
5094  if (isModEnabled("multicurrency")) {
5095  print '<td class="linecoluht_currency right">'.$langs->trans('PriceUHTCurrency').'</td>';
5096  }
5097  print '<td class="linecolqty right">'.$langs->trans('Qty').'</td>';
5098  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5099  print '<td class="linecoluseunit left">'.$langs->trans('Unit').'</td>';
5100  }
5101  print '<td class="linecoldiscount right">'.$langs->trans('ReductionShort').'</td>';
5102  print '<td class="linecolht right">'.$langs->trans('TotalHT').'</td>';
5103  print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
5104  print '</tr>';
5105  $i = 0;
5106 
5107  if (!empty($this->lines)) {
5108  foreach ($this->lines as $line) {
5109  $reshook = 0;
5110  //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line))) {
5111  if (is_object($hookmanager)) { // Old code is commented on preceding line.
5112  $parameters = array('line'=>$line, 'i'=>$i, 'restrictlist'=>$restrictlist, 'selectedLines'=> $selectedLines);
5113  if (!empty($line->fk_parent_line)) { $parameters['fk_parent_line'] = $line->fk_parent_line; }
5114  $reshook = $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5115  }
5116  if (empty($reshook)) {
5117  $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines);
5118  }
5119 
5120  $i++;
5121  }
5122  }
5123  }
5124 
5138  public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
5139  {
5140  global $langs, $conf;
5141 
5142  //var_dump($line);
5143  if (!empty($line->date_start)) {
5144  $date_start = $line->date_start;
5145  } else {
5146  $date_start = $line->date_debut_prevue;
5147  if ($line->date_debut_reel) {
5148  $date_start = $line->date_debut_reel;
5149  }
5150  }
5151  if (!empty($line->date_end)) {
5152  $date_end = $line->date_end;
5153  } else {
5154  $date_end = $line->date_fin_prevue;
5155  if ($line->date_fin_reel) {
5156  $date_end = $line->date_fin_reel;
5157  }
5158  }
5159 
5160  $this->tpl['id'] = $line->id;
5161 
5162  $this->tpl['label'] = '';
5163  if (!empty($line->fk_parent_line)) {
5164  $this->tpl['label'] .= img_picto('', 'rightarrow');
5165  }
5166 
5167  if (($line->info_bits & 2) == 2) { // TODO Not sure this is used for source object
5168  $discount = new DiscountAbsolute($this->db);
5169  $discount->fk_soc = $this->socid;
5170  $this->tpl['label'] .= $discount->getNomUrl(0, 'discount');
5171  } elseif (!empty($line->fk_product)) {
5172  $productstatic = new Product($this->db);
5173  $productstatic->id = $line->fk_product;
5174  $productstatic->ref = $line->ref;
5175  $productstatic->type = $line->fk_product_type;
5176  if (empty($productstatic->ref)) {
5177  $line->fetch_product();
5178  $productstatic = $line->product;
5179  }
5180 
5181  $this->tpl['label'] .= $productstatic->getNomUrl(1);
5182  $this->tpl['label'] .= ' - '.(!empty($line->label) ? $line->label : $line->product_label);
5183  // Dates
5184  if ($line->product_type == 1 && ($date_start || $date_end)) {
5185  $this->tpl['label'] .= get_date_range($date_start, $date_end);
5186  }
5187  } else {
5188  $this->tpl['label'] .= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
5189  if (!empty($line->desc)) {
5190  $this->tpl['label'] .= $line->desc;
5191  } else {
5192  $this->tpl['label'] .= ($line->label ? '&nbsp;'.$line->label : '');
5193  }
5194 
5195  // Dates
5196  if ($line->product_type == 1 && ($date_start || $date_end)) {
5197  $this->tpl['label'] .= get_date_range($date_start, $date_end);
5198  }
5199  }
5200 
5201  if (!empty($line->desc)) {
5202  if ($line->desc == '(CREDIT_NOTE)') { // TODO Not sure this is used for source object
5203  $discount = new DiscountAbsolute($this->db);
5204  $discount->fetch($line->fk_remise_except);
5205  $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
5206  } elseif ($line->desc == '(DEPOSIT)') { // TODO Not sure this is used for source object
5207  $discount = new DiscountAbsolute($this->db);
5208  $discount->fetch($line->fk_remise_except);
5209  $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
5210  } elseif ($line->desc == '(EXCESS RECEIVED)') {
5211  $discount = new DiscountAbsolute($this->db);
5212  $discount->fetch($line->fk_remise_except);
5213  $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
5214  } elseif ($line->desc == '(EXCESS PAID)') {
5215  $discount = new DiscountAbsolute($this->db);
5216  $discount->fetch($line->fk_remise_except);
5217  $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
5218  } else {
5219  $this->tpl['description'] = dol_trunc($line->desc, 60);
5220  }
5221  } else {
5222  $this->tpl['description'] = '&nbsp;';
5223  }
5224 
5225  // VAT Rate
5226  $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
5227  $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
5228  if (!empty($line->vat_src_code) && !preg_match('/\‍(/', $this->tpl['vat_rate'])) {
5229  $this->tpl['vat_rate'] .= ' ('.$line->vat_src_code.')';
5230  }
5231 
5232  $this->tpl['price'] = price($line->subprice);
5233  $this->tpl['total_ht'] = price($line->total_ht);
5234  $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
5235  $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
5236  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5237  $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
5238  }
5239  $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
5240 
5241  // Is the line strike or not
5242  $this->tpl['strike'] = 0;
5243  if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) {
5244  $this->tpl['strike'] = 1;
5245  }
5246 
5247  // Output template part (modules that overwrite templates must declare this into descriptor)
5248  // Use global variables + $dateSelector + $seller and $buyer
5249  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5250  foreach ($dirtpls as $module => $reldir) {
5251  if (!empty($module)) {
5252  $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
5253  } else {
5254  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php';
5255  }
5256 
5257  if (empty($conf->file->strict_mode)) {
5258  $res = @include $tpl;
5259  } else {
5260  $res = include $tpl; // for debug
5261  }
5262  if ($res) {
5263  break;
5264  }
5265  }
5266  }
5267 
5268 
5269  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5280  public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
5281  {
5282  // phpcs:enable
5283  $this->db->begin();
5284 
5285  $sql = "INSERT INTO ".$this->db->prefix()."element_resources (";
5286  $sql .= "resource_id";
5287  $sql .= ", resource_type";
5288  $sql .= ", element_id";
5289  $sql .= ", element_type";
5290  $sql .= ", busy";
5291  $sql .= ", mandatory";
5292  $sql .= ") VALUES (";
5293  $sql .= ((int) $resource_id);
5294  $sql .= ", '".$this->db->escape($resource_type)."'";
5295  $sql .= ", '".$this->db->escape($this->id)."'";
5296  $sql .= ", '".$this->db->escape($this->element)."'";
5297  $sql .= ", '".$this->db->escape($busy)."'";
5298  $sql .= ", '".$this->db->escape($mandatory)."'";
5299  $sql .= ")";
5300 
5301  dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
5302  if ($this->db->query($sql)) {
5303  $this->db->commit();
5304  return 1;
5305  } else {
5306  $this->error = $this->db->lasterror();
5307  $this->db->rollback();
5308  return 0;
5309  }
5310  }
5311 
5312  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5321  public function delete_resource($rowid, $element, $notrigger = 0)
5322  {
5323  // phpcs:enable
5324  global $user;
5325 
5326  $this->db->begin();
5327 
5328  $sql = "DELETE FROM ".$this->db->prefix()."element_resources";
5329  $sql .= " WHERE rowid = ".((int) $rowid);
5330 
5331  dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
5332 
5333  $resql = $this->db->query($sql);
5334  if (!$resql) {
5335  $this->error = $this->db->lasterror();
5336  $this->db->rollback();
5337  return -1;
5338  } else {
5339  if (!$notrigger) {
5340  $result = $this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
5341  if ($result < 0) {
5342  $this->db->rollback();
5343  return -1;
5344  }
5345  }
5346  $this->db->commit();
5347  return 1;
5348  }
5349  }
5350 
5351 
5357  public function __clone()
5358  {
5359  // Force a copy of this->lines, otherwise it will point to same object.
5360  if (isset($this->lines) && is_array($this->lines)) {
5361  $nboflines = count($this->lines);
5362  for ($i = 0; $i < $nboflines; $i++) {
5363  $this->lines[$i] = clone $this->lines[$i];
5364  }
5365  }
5366  }
5367 
5381  protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
5382  {
5383  global $conf, $langs, $user, $hookmanager, $action;
5384 
5385  $srctemplatepath = '';
5386 
5387  $parameters = array('modelspath'=>$modelspath, 'modele'=>$modele, 'outputlangs'=>$outputlangs, 'hidedetails'=>$hidedetails, 'hidedesc'=>$hidedesc, 'hideref'=>$hideref, 'moreparams'=>$moreparams);
5388  $reshook = $hookmanager->executeHooks('commonGenerateDocument', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5389 
5390  if (!empty($reshook)) {
5391  return $reshook;
5392  }
5393 
5394  dol_syslog("commonGenerateDocument modele=".$modele." outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
5395 
5396  if (empty($modele)) {
5397  $this->error = 'BadValueForParameterModele';
5398  return -1;
5399  }
5400 
5401  // Increase limit for PDF build
5402  $err = error_reporting();
5403  error_reporting(0);
5404  @set_time_limit(120);
5405  error_reporting($err);
5406 
5407  // If selected model is a filename template (then $modele="modelname" or "modelname:filename")
5408  $tmp = explode(':', $modele, 2);
5409  if (!empty($tmp[1])) {
5410  $modele = $tmp[0];
5411  $srctemplatepath = $tmp[1];
5412  }
5413 
5414  // Search template files
5415  $file = '';
5416  $classname = '';
5417  $filefound = '';
5418  $dirmodels = array('/');
5419  if (is_array($conf->modules_parts['models'])) {
5420  $dirmodels = array_merge($dirmodels, $conf->modules_parts['models']);
5421  }
5422  foreach ($dirmodels as $reldir) {
5423  foreach (array('doc', 'pdf') as $prefix) {
5424  if (in_array(get_class($this), array('Adherent'))) {
5425  // Member module use prefix_modele.class.php
5426  $file = $prefix."_".$modele.".class.php";
5427  } else {
5428  // Other module use prefix_modele.modules.php
5429  $file = $prefix."_".$modele.".modules.php";
5430  }
5431 
5432  // On verifie l'emplacement du modele
5433  $file = dol_buildpath($reldir.$modelspath.$file, 0);
5434  if (file_exists($file)) {
5435  $filefound = $file;
5436  $classname = $prefix.'_'.$modele;
5437  break;
5438  }
5439  }
5440  if ($filefound) {
5441  break;
5442  }
5443  }
5444 
5445  if (!$filefound) {
5446  $this->error = $langs->trans("Error").' Failed to load doc generator with modelpaths='.$modelspath.' - modele='.$modele;
5447  $this->errors[] = $this->error;
5448  dol_syslog($this->error, LOG_ERR);
5449  return -1;
5450  }
5451 
5452  // If generator was found
5453  global $db; // Required to solve a conception default making an include of code using $db instead of $this->db just after.
5454 
5455  require_once $file;
5456 
5457  $obj = new $classname($this->db);
5458 
5459  // If generator is ODT, we must have srctemplatepath defined, if not we set it.
5460  if ($obj->type == 'odt' && empty($srctemplatepath)) {
5461  $varfortemplatedir = $obj->scandir;
5462  if ($varfortemplatedir && !empty($conf->global->$varfortemplatedir)) {
5463  $dirtoscan = $conf->global->$varfortemplatedir;
5464 
5465  $listoffiles = array();
5466 
5467  // Now we add first model found in directories scanned
5468  $listofdir = explode(',', $dirtoscan);
5469  foreach ($listofdir as $key => $tmpdir) {
5470  $tmpdir = trim($tmpdir);
5471  $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
5472  if (!$tmpdir) {
5473  unset($listofdir[$key]);
5474  continue;
5475  }
5476  if (is_dir($tmpdir)) {
5477  $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
5478  if (count($tmpfiles)) {
5479  $listoffiles = array_merge($listoffiles, $tmpfiles);
5480  }
5481  }
5482  }
5483 
5484  if (count($listoffiles)) {
5485  foreach ($listoffiles as $record) {
5486  $srctemplatepath = $record['fullname'];
5487  break;
5488  }
5489  }
5490  }
5491 
5492  if (empty($srctemplatepath)) {
5493  $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
5494  return -1;
5495  }
5496  }
5497 
5498  if ($obj->type == 'odt' && !empty($srctemplatepath)) {
5499  if (!dol_is_file($srctemplatepath)) {
5500  dol_syslog("Failed to locate template file ".$srctemplatepath, LOG_WARNING);
5501  $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
5502  return -1;
5503  }
5504  }
5505 
5506  // We save charset_output to restore it because write_file can change it if needed for
5507  // output format that does not support UTF8.
5508  $sav_charset_output = empty($outputlangs->charset_output) ? '' : $outputlangs->charset_output;
5509 
5510  if (in_array(get_class($this), array('Adherent'))) {
5511  $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, 'tmp_cards', $moreparams);
5512  } else {
5513  $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
5514  }
5515  // After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
5516 
5517  if ($resultwritefile > 0) {
5518  $outputlangs->charset_output = $sav_charset_output;
5519 
5520  // We delete old preview
5521  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5522  dol_delete_preview($this);
5523 
5524  // Index file in database
5525  if (!empty($obj->result['fullpath'])) {
5526  $destfull = $obj->result['fullpath'];
5527 
5528  // Update the last_main_doc field into main object (if document generator has property ->update_main_doc_field set)
5529  $update_main_doc_field = 0;
5530  if (!empty($obj->update_main_doc_field)) {
5531  $update_main_doc_field = 1;
5532  }
5533 
5534  // Check that the file exists, before indexing it.
5535  // Hint: It does not exist, if we create a PDF and auto delete the ODT File
5536  if (dol_is_file($destfull)) {
5537  $this->indexFile($destfull, $update_main_doc_field);
5538  }
5539  } else {
5540  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);
5541  }
5542 
5543  // Success in building document. We build meta file.
5544  dol_meta_create($this);
5545 
5546  return 1;
5547  } else {
5548  $outputlangs->charset_output = $sav_charset_output;
5549  $this->error = $obj->error;
5550  $this->errors = $obj->errors;
5551  dol_syslog("Error generating document for ".__CLASS__.". Error: ".$obj->error, LOG_ERR);
5552  return -1;
5553  }
5554  }
5555 
5565  public function indexFile($destfull, $update_main_doc_field)
5566  {
5567  global $conf, $user;
5568 
5569  $upload_dir = dirname($destfull);
5570  $destfile = basename($destfull);
5571  $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
5572 
5573  if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a tmp dir
5574  $filename = basename($destfile);
5575  $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
5576  $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
5577 
5578  include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
5579  $ecmfile = new EcmFiles($this->db);
5580  $result = $ecmfile->fetch(0, '', ($rel_dir ? $rel_dir.'/' : '').$filename);
5581 
5582  // Set the public "share" key
5583  $setsharekey = false;
5584  if ($this->element == 'propal' || $this->element == 'proposal') {
5585  if (!isset($conf->global->PROPOSAL_ALLOW_ONLINESIGN) || !empty($conf->global->PROPOSAL_ALLOW_ONLINESIGN)) {
5586  $setsharekey = true; // feature to make online signature is not set or set to on (default)
5587  }
5588  if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) {
5589  $setsharekey = true;
5590  }
5591  }
5592  if ($this->element == 'commande' && !empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD)) {
5593  $setsharekey = true;
5594  }
5595  if ($this->element == 'facture' && !empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD)) {
5596  $setsharekey = true;
5597  }
5598  if ($this->element == 'bank_account' && !empty($conf->global->BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD)) {
5599  $setsharekey = true;
5600  }
5601  if ($this->element == 'product' && !empty($conf->global->PRODUCT_ALLOW_EXTERNAL_DOWNLOAD)) {
5602  $setsharekey = true;
5603  }
5604  if ($this->element == 'contrat' && !empty($conf->global->CONTRACT_ALLOW_EXTERNAL_DOWNLOAD)) {
5605  $setsharekey = true;
5606  }
5607  if ($this->element == 'fichinter' && !empty($conf->global->FICHINTER_ALLOW_EXTERNAL_DOWNLOAD)) {
5608  $setsharekey = true;
5609  }
5610  if ($this->element == 'supplier_proposal' && !empty($conf->global->SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) {
5611  $setsharekey = true;
5612  }
5613 
5614  if ($setsharekey) {
5615  if (empty($ecmfile->share)) { // Because object not found or share not set yet
5616  require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
5617  $ecmfile->share = getRandomPassword(true);
5618  }
5619  }
5620 
5621  if ($result > 0) {
5622  $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5623  $ecmfile->fullpath_orig = '';
5624  $ecmfile->gen_or_uploaded = 'generated';
5625  $ecmfile->description = ''; // indexed content
5626  $ecmfile->keywords = ''; // keyword content
5627  $result = $ecmfile->update($user);
5628  if ($result < 0) {
5629  setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5630  return -1;
5631  }
5632  } else {
5633  $ecmfile->entity = $conf->entity;
5634  $ecmfile->filepath = $rel_dir;
5635  $ecmfile->filename = $filename;
5636  $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5637  $ecmfile->fullpath_orig = '';
5638  $ecmfile->gen_or_uploaded = 'generated';
5639  $ecmfile->description = ''; // indexed content
5640  $ecmfile->keywords = ''; // keyword content
5641  $ecmfile->src_object_type = $this->table_element; // $this->table_name is 'myobject' or 'mymodule_myobject'.
5642  $ecmfile->src_object_id = $this->id;
5643 
5644  $result = $ecmfile->create($user);
5645  if ($result < 0) {
5646  setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5647  return -1;
5648  }
5649  }
5650 
5651  /*$this->result['fullname']=$destfull;
5652  $this->result['filepath']=$ecmfile->filepath;
5653  $this->result['filename']=$ecmfile->filename;*/
5654  //var_dump($obj->update_main_doc_field);exit;
5655 
5656  if ($update_main_doc_field && !empty($this->table_element)) {
5657  $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath."/".$ecmfile->filename)."'";
5658  $sql .= " WHERE rowid = ".((int) $this->id);
5659 
5660  $resql = $this->db->query($sql);
5661  if (!$resql) {
5662  dol_print_error($this->db);
5663  return -1;
5664  } else {
5665  $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
5666  }
5667  }
5668  }
5669 
5670  return 1;
5671  }
5672 
5680  public function addThumbs($file)
5681  {
5682  $file_osencoded = dol_osencode($file);
5683 
5684  if (file_exists($file_osencoded)) {
5685  require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
5686 
5687  $tmparraysize = getDefaultImageSizes();
5688  $maxwidthsmall = $tmparraysize['maxwidthsmall'];
5689  $maxheightsmall = $tmparraysize['maxheightsmall'];
5690  $maxwidthmini = $tmparraysize['maxwidthmini'];
5691  $maxheightmini = $tmparraysize['maxheightmini'];
5692  //$quality = $tmparraysize['quality'];
5693  $quality = 50; // For thumbs, we force quality to 50
5694 
5695  // Create small thumbs for company (Ratio is near 16/9)
5696  // Used on logon for example
5697  vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
5698 
5699  // Create mini thumbs for company (Ratio is near 16/9)
5700  // Used on menu or for setup page for example
5701  vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
5702  }
5703  }
5704 
5712  public function delThumbs($file)
5713  {
5714  $imgThumbName = getImageFileNameForSize($file, '_small'); // Full path of thumb file
5715  dol_delete_file($imgThumbName);
5716  $imgThumbName = getImageFileNameForSize($file, '_mini'); // Full path of thumb file
5717  dol_delete_file($imgThumbName);
5718  }
5719 
5720 
5721  /* Functions common to commonobject and commonobjectline */
5722 
5723  /* For default values */
5724 
5738  public function getDefaultCreateValueFor($fieldname, $alternatevalue = null, $type = 'alphanohtml')
5739  {
5740  global $conf, $_POST;
5741 
5742  // If param here has been posted, we use this value first.
5743  if (GETPOSTISSET($fieldname)) {
5744  return GETPOST($fieldname, $type, 3);
5745  }
5746 
5747  if (isset($alternatevalue)) {
5748  return $alternatevalue;
5749  }
5750 
5751  $newelement = $this->element;
5752  if ($newelement == 'facture') {
5753  $newelement = 'invoice';
5754  }
5755  if ($newelement == 'commande') {
5756  $newelement = 'order';
5757  }
5758  if (empty($newelement)) {
5759  dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
5760  return '';
5761  }
5762 
5763  $keyforfieldname = strtoupper($newelement.'_DEFAULT_'.$fieldname);
5764  //var_dump($keyforfieldname);
5765  if (isset($conf->global->$keyforfieldname)) {
5766  return $conf->global->$keyforfieldname;
5767  }
5768 
5769  // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
5770  // store content into $conf->cache['overwrite_default']
5771 
5772  return '';
5773  }
5774 
5775 
5776  /* For triggers */
5777 
5778 
5779  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5790  public function call_trigger($triggerName, $user)
5791  {
5792  // phpcs:enable
5793  global $langs, $conf;
5794  if (!empty(self::TRIGGER_PREFIX) && strpos($triggerName, self::TRIGGER_PREFIX . '_') !== 0) {
5795  dol_print_error('', 'The trigger "' . $triggerName . '" does not start with "' . self::TRIGGER_PREFIX . '_" as required.');
5796  exit;
5797  }
5798  if (!is_object($langs)) { // If lang was not defined, we set it. It is required by run_triggers.
5799  include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5800  $langs = new Translate('', $conf);
5801  }
5802 
5803  include_once DOL_DOCUMENT_ROOT.'/core/class/interfaces.class.php';
5804  $interface = new Interfaces($this->db);
5805  $result = $interface->run_triggers($triggerName, $this, $user, $langs, $conf);
5806 
5807  if ($result < 0) {
5808  if (!empty($this->errors)) {
5809  $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.
5810  } else {
5811  $this->errors = $interface->errors;
5812  }
5813  }
5814  return $result;
5815  }
5816 
5817 
5818  /* Functions for data in other language */
5819 
5820 
5829  {
5830  // To avoid SQL errors. Probably not the better solution though
5831  if (!$this->element) {
5832  return 0;
5833  }
5834  if (!($this->id > 0)) {
5835  return 0;
5836  }
5837  if (is_array($this->array_languages)) {
5838  return 1;
5839  }
5840 
5841  $this->array_languages = array();
5842 
5843  $element = $this->element;
5844  if ($element == 'categorie') {
5845  $element = 'categories'; // For compatibility
5846  }
5847 
5848  // Request to get translation values for object
5849  $sql = "SELECT rowid, property, lang , value";
5850  $sql .= " FROM ".$this->db->prefix()."object_lang";
5851  $sql .= " WHERE type_object = '".$this->db->escape($element)."'";
5852  $sql .= " AND fk_object = ".((int) $this->id);
5853 
5854  //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
5855  $resql = $this->db->query($sql);
5856  if ($resql) {
5857  $numrows = $this->db->num_rows($resql);
5858  if ($numrows) {
5859  $i = 0;
5860  while ($i < $numrows) {
5861  $obj = $this->db->fetch_object($resql);
5862  $key = $obj->property;
5863  $value = $obj->value;
5864  $codelang = $obj->lang;
5865  $type = $this->fields[$key]['type'];
5866 
5867  // we can add this attribute to object
5868  if (preg_match('/date/', $type)) {
5869  $this->array_languages[$key][$codelang] = $this->db->jdate($value);
5870  } else {
5871  $this->array_languages[$key][$codelang] = $value;
5872  }
5873 
5874  $i++;
5875  }
5876  }
5877 
5878  $this->db->free($resql);
5879 
5880  if ($numrows) {
5881  return $numrows;
5882  } else {
5883  return 0;
5884  }
5885  } else {
5886  dol_print_error($this->db);
5887  return -1;
5888  }
5889  }
5890 
5897  public function setValuesForExtraLanguages($onlykey = '')
5898  {
5899  global $_POST, $langs;
5900 
5901  // Get extra fields
5902  foreach ($_POST as $postfieldkey => $postfieldvalue) {
5903  $tmparray = explode('-', $postfieldkey);
5904  if ($tmparray[0] != 'field') {
5905  continue;
5906  }
5907 
5908  $element = $tmparray[1];
5909  $key = $tmparray[2];
5910  $codelang = $tmparray[3];
5911  //var_dump("postfieldkey=".$postfieldkey." element=".$element." key=".$key." codelang=".$codelang);
5912 
5913  if (!empty($onlykey) && $key != $onlykey) {
5914  continue;
5915  }
5916  if ($element != $this->element) {
5917  continue;
5918  }
5919 
5920  $key_type = $this->fields[$key]['type'];
5921 
5922  $enabled = 1;
5923  if (isset($this->fields[$key]['enabled'])) {
5924  $enabled = dol_eval($this->fields[$key]['enabled'], 1, 1, '1');
5925  }
5926  /*$perms = 1;
5927  if (isset($this->fields[$key]['perms']))
5928  {
5929  $perms = dol_eval($this->fields[$key]['perms'], 1, 1, '1');
5930  }*/
5931  if (empty($enabled)) {
5932  continue;
5933  }
5934  //if (empty($perms)) continue;
5935 
5936  if (in_array($key_type, array('date'))) {
5937  // Clean parameters
5938  // TODO GMT date in memory must be GMT so we should add gm=true in parameters
5939  $value_key = dol_mktime(0, 0, 0, GETPOST($postfieldkey."month", 'int'), GETPOST($postfieldkey."day", 'int'), GETPOST($postfieldkey."year", 'int'));
5940  } elseif (in_array($key_type, array('datetime'))) {
5941  // Clean parameters
5942  // TODO GMT date in memory must be GMT so we should add gm=true in parameters
5943  $value_key = dol_mktime(GETPOST($postfieldkey."hour", 'int'), GETPOST($postfieldkey."min", 'int'), 0, GETPOST($postfieldkey."month", 'int'), GETPOST($postfieldkey."day", 'int'), GETPOST($postfieldkey."year", 'int'));
5944  } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
5945  $value_arr = GETPOST($postfieldkey, 'array'); // check if an array
5946  if (!empty($value_arr)) {
5947  $value_key = implode(',', $value_arr);
5948  } else {
5949  $value_key = '';
5950  }
5951  } elseif (in_array($key_type, array('price', 'double'))) {
5952  $value_arr = GETPOST($postfieldkey, 'alpha');
5953  $value_key = price2num($value_arr);
5954  } else {
5955  $value_key = GETPOST($postfieldkey);
5956  if (in_array($key_type, array('link')) && $value_key == '-1') {
5957  $value_key = '';
5958  }
5959  }
5960 
5961  $this->array_languages[$key][$codelang] = $value_key;
5962 
5963  /*if ($nofillrequired) {
5964  $langs->load('errors');
5965  setEventMessages($langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required), null, 'errors');
5966  return -1;
5967  }*/
5968  }
5969 
5970  return 1;
5971  }
5972 
5973 
5974  /* Functions for extrafields */
5975 
5982  public function fetchNoCompute($id)
5983  {
5984  global $conf;
5985 
5986  $savDisableCompute = $conf->disable_compute;
5987  $conf->disable_compute = 1;
5988 
5989  $ret = $this->fetch($id);
5990 
5991  $conf->disable_compute = $savDisableCompute;
5992 
5993  return $ret;
5994  }
5995 
5996  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6006  public function fetch_optionals($rowid = null, $optionsArray = null)
6007  {
6008  // phpcs:enable
6009  global $conf, $extrafields;
6010 
6011  if (empty($rowid)) {
6012  $rowid = $this->id;
6013  }
6014  if (empty($rowid) && isset($this->rowid)) {
6015  $rowid = $this->rowid; // deprecated
6016  }
6017 
6018  // To avoid SQL errors. Probably not the better solution though
6019  if (!$this->table_element) {
6020  return 0;
6021  }
6022 
6023  $this->array_options = array();
6024 
6025  if (!is_array($optionsArray)) {
6026  // If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
6027  if (!isset($extrafields) || !is_object($extrafields)) {
6028  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6029  $extrafields = new ExtraFields($this->db);
6030  }
6031 
6032  // Load array of extrafields for elementype = $this->table_element
6033  if (empty($extrafields->attributes[$this->table_element]['loaded'])) {
6034  $extrafields->fetch_name_optionals_label($this->table_element);
6035  }
6036  $optionsArray = (!empty($extrafields->attributes[$this->table_element]['label']) ? $extrafields->attributes[$this->table_element]['label'] : null);
6037  } else {
6038  global $extrafields;
6039  dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
6040  }
6041 
6042  $table_element = $this->table_element;
6043  if ($table_element == 'categorie') {
6044  $table_element = 'categories'; // For compatibility
6045  }
6046 
6047  // Request to get complementary values
6048  if (is_array($optionsArray) && count($optionsArray) > 0) {
6049  $sql = "SELECT rowid";
6050  foreach ($optionsArray as $name => $label) {
6051  if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate') {
6052  $sql .= ", ".$name;
6053  }
6054  }
6055  $sql .= " FROM ".$this->db->prefix().$table_element."_extrafields";
6056  $sql .= " WHERE fk_object = ".((int) $rowid);
6057 
6058  //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6059  $resql = $this->db->query($sql);
6060  if ($resql) {
6061  $numrows = $this->db->num_rows($resql);
6062  if ($numrows) {
6063  $tab = $this->db->fetch_array($resql);
6064 
6065  foreach ($tab as $key => $value) {
6066  // 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)
6067  if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && !is_int($key)) {
6068  // we can add this attribute to object
6069  if (!empty($extrafields->attributes[$this->table_element]) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date', 'datetime'))) {
6070  //var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
6071  $this->array_options["options_".$key] = $this->db->jdate($value);
6072  } else {
6073  $this->array_options["options_".$key] = $value;
6074  }
6075 
6076  //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
6077  }
6078  }
6079  }
6080 
6081  // If field is a computed field, value must become result of compute (regardless of whether a row exists
6082  // in the element's extrafields table)
6083  if (is_array($extrafields->attributes[$this->table_element]['label'])) {
6084  foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
6085  if (!empty($extrafields->attributes[$this->table_element]) && !empty($extrafields->attributes[$this->table_element]['computed'][$key])) {
6086  //var_dump($conf->disable_compute);
6087  if (empty($conf->disable_compute)) {
6088  global $objectoffield; // We set a global variable to $objectoffield so
6089  $objectoffield = $this; // we can use it inside computed formula
6090  $this->array_options['options_' . $key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0, '');
6091  }
6092  }
6093  }
6094  }
6095 
6096  $this->db->free($resql);
6097 
6098  if ($numrows) {
6099  return $numrows;
6100  } else {
6101  return 0;
6102  }
6103  } else {
6104  $this->errors[]=$this->db->lasterror;
6105  return -1;
6106  }
6107  }
6108  return 0;
6109  }
6110 
6117  public function deleteExtraFields()
6118  {
6119  global $conf;
6120 
6121  if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) {
6122  return 0;
6123  }
6124 
6125  $this->db->begin();
6126 
6127  $table_element = $this->table_element;
6128  if ($table_element == 'categorie') {
6129  $table_element = 'categories'; // For compatibility
6130  }
6131 
6132  dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
6133 
6134  $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6135 
6136  $resql = $this->db->query($sql_del);
6137  if (!$resql) {
6138  $this->error = $this->db->lasterror();
6139  $this->db->rollback();
6140  return -1;
6141  } else {
6142  $this->db->commit();
6143  return 1;
6144  }
6145  }
6146 
6157  public function insertExtraFields($trigger = '', $userused = null)
6158  {
6159  global $conf, $langs, $user;
6160 
6161  if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) {
6162  return 0;
6163  }
6164 
6165  if (empty($userused)) {
6166  $userused = $user;
6167  }
6168 
6169  $error = 0;
6170 
6171  if (!empty($this->array_options)) {
6172  // Check parameters
6173  $langs->load('admin');
6174  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6175  $extrafields = new ExtraFields($this->db);
6176  $target_extrafields = $extrafields->fetch_name_optionals_label($this->table_element);
6177 
6178  // Eliminate copied source object extra fields that do not exist in target object
6179  $new_array_options = array();
6180  foreach ($this->array_options as $key => $value) {
6181  if (in_array(substr($key, 8), array_keys($target_extrafields))) { // We remove the 'options_' from $key for test
6182  $new_array_options[$key] = $value;
6183  } elseif (in_array($key, array_keys($target_extrafields))) { // We test on $key that does not contain the 'options_' prefix
6184  $new_array_options['options_'.$key] = $value;
6185  }
6186  }
6187 
6188  foreach ($new_array_options as $key => $value) {
6189  $attributeKey = substr($key, 8); // Remove 'options_' prefix
6190  $attributeType = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
6191  $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$attributeKey];
6192  $attributeParam = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
6193  $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
6194  $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
6195  $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$attributeKey];
6196 
6197  // If we clone, we have to clean unique extrafields to prevent duplicates.
6198  // This behaviour can be prevented by external code by changing $this->context['createfromclone'] value in createFrom hook
6199  if (!empty($this->context['createfromclone']) && $this->context['createfromclone'] == 'createfromclone' && !empty($attributeUnique)) {
6200  $new_array_options[$key] = null;
6201  }
6202 
6203  // Similar code than into insertExtraFields
6204  if ($attributeRequired) {
6205  $mandatorypb = false;
6206  if ($attributeType == 'link' && $this->array_options[$key] == '-1') {
6207  $mandatorypb = true;
6208  }
6209  if ($this->array_options[$key] === '') {
6210  $mandatorypb = true;
6211  }
6212  if ($attributeType == 'sellist' && $this->array_options[$key] == '0') {
6213  $mandatorypb = true;
6214  }
6215  if ($mandatorypb) {
6216  $langs->load("errors");
6217  dol_syslog("Mandatory field '".$key."' is empty during create and set to required into definition of extrafields");
6218  $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6219  return -1;
6220  }
6221  }
6222 
6223  //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6224  //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6225 
6226  if (!empty($attrfieldcomputed)) {
6227  if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS)) {
6228  $value = dol_eval($attrfieldcomputed, 1, 0, '');
6229  dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
6230  $new_array_options[$key] = $value;
6231  } else {
6232  $new_array_options[$key] = null;
6233  }
6234  }
6235 
6236  switch ($attributeType) {
6237  case 'int':
6238  if (!is_numeric($value) && $value != '') {
6239  $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6240  return -1;
6241  } elseif ($value == '') {
6242  $new_array_options[$key] = null;
6243  }
6244  break;
6245  case 'price':
6246  case 'double':
6247  $value = price2num($value);
6248  if (!is_numeric($value) && $value != '') {
6249  dol_syslog($langs->trans("ExtraFieldHasWrongValue")." for ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6250  $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6251  return -1;
6252  } elseif ($value == '') {
6253  $value = null;
6254  }
6255  //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6256  $new_array_options[$key] = $value;
6257  break;
6258  /*case 'select': // Not required, we chosed value='0' for undefined values
6259  if ($value=='-1')
6260  {
6261  $this->array_options[$key] = null;
6262  }
6263  break;*/
6264  case 'password':
6265  $algo = '';
6266  if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
6267  // If there is an encryption choice, we use it to crypt data before insert
6268  $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
6269  $algo = reset($tmparrays);
6270  if ($algo != '') {
6271  //global $action; // $action may be 'create', 'update', 'update_extras'...
6272  //var_dump($action);
6273  //var_dump($this->oldcopy);exit;
6274  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
6275  //var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]);
6276  if (isset($this->oldcopy->array_options[$key]) && $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.
6277  $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6278  } else {
6279  // var_dump($algo);
6280  $newvalue = dol_hash($this->array_options[$key], $algo);
6281  $new_array_options[$key] = $newvalue;
6282  }
6283  } else {
6284  $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6285  }
6286  }
6287  } else // Common usage
6288  {
6289  $new_array_options[$key] = $this->array_options[$key];
6290  }
6291  break;
6292  case 'date':
6293  case 'datetime':
6294  // If data is a string instead of a timestamp, we convert it
6295  if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6296  $this->array_options[$key] = strtotime($this->array_options[$key]);
6297  }
6298  $new_array_options[$key] = $this->db->idate($this->array_options[$key]);
6299  break;
6300  case 'datetimegmt':
6301  // If data is a string instead of a timestamp, we convert it
6302  if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6303  $this->array_options[$key] = strtotime($this->array_options[$key]);
6304  }
6305  $new_array_options[$key] = $this->db->idate($this->array_options[$key], 'gmt');
6306  break;
6307  case 'link':
6308  $param_list = array_keys($attributeParam['options']);
6309  // 0 : ObjectName
6310  // 1 : classPath
6311  $InfoFieldList = explode(":", $param_list[0]);
6312  dol_include_once($InfoFieldList[1]);
6313  if ($InfoFieldList[0] && class_exists($InfoFieldList[0])) {
6314  if ($value == '-1') { // -1 is key for no defined in combo list of objects
6315  $new_array_options[$key] = '';
6316  } elseif ($value) {
6317  $object = new $InfoFieldList[0]($this->db);
6318  if (is_numeric($value)) {
6319  $res = $object->fetch($value); // Common case
6320  } else {
6321  $res = $object->fetch('', $value); // For compatibility
6322  }
6323 
6324  if ($res > 0) {
6325  $new_array_options[$key] = $object->id;
6326  } else {
6327  $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
6328  return -1;
6329  }
6330  }
6331  } else {
6332  dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6333  }
6334  break;
6335  case 'checkbox':
6336  case 'chkbxlst':
6337  if (is_array($this->array_options[$key])) {
6338  $new_array_options[$key] = implode(',', $this->array_options[$key]);
6339  } else {
6340  $new_array_options[$key] = $this->array_options[$key];
6341  }
6342  break;
6343  }
6344  }
6345 
6346  $this->db->begin();
6347 
6348  $table_element = $this->table_element;
6349  if ($table_element == 'categorie') {
6350  $table_element = 'categories'; // For compatibility
6351  }
6352 
6353  dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG);
6354 
6355  $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6356  $this->db->query($sql_del);
6357 
6358  $sql = "INSERT INTO ".$this->db->prefix().$table_element."_extrafields (fk_object";
6359  foreach ($new_array_options as $key => $value) {
6360  $attributeKey = substr($key, 8); // Remove 'options_' prefix
6361  // Add field of attribut
6362  if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator
6363  $sql .= ",".$attributeKey;
6364  }
6365  }
6366  // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6367  if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6368  foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6369  if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6370  $sql .= ",".$tmpkey;
6371  }
6372  }
6373  }
6374  $sql .= ") VALUES (".$this->id;
6375 
6376  foreach ($new_array_options as $key => $value) {
6377  $attributeKey = substr($key, 8); // Remove 'options_' prefix
6378  // Add field of attribute
6379  if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator)
6380  if ($new_array_options[$key] != '' || $new_array_options[$key] == '0') {
6381  $sql .= ",'".$this->db->escape($new_array_options[$key])."'";
6382  } else {
6383  $sql .= ",null";
6384  }
6385  }
6386  }
6387  // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6388  if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6389  foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6390  if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6391  if (in_array($tmpval, array('int', 'double', 'price'))) {
6392  $sql .= ", 0";
6393  } else {
6394  $sql .= ", ''";
6395  }
6396  }
6397  }
6398  }
6399 
6400  $sql .= ")";
6401 
6402  $resql = $this->db->query($sql);
6403  if (!$resql) {
6404  $this->error = $this->db->lasterror();
6405  $error++;
6406  }
6407 
6408  if (!$error && $trigger) {
6409  // Call trigger
6410  $this->context = array('extrafieldaddupdate'=>1);
6411  $result = $this->call_trigger($trigger, $userused);
6412  if ($result < 0) {
6413  $error++;
6414  }
6415  // End call trigger
6416  }
6417 
6418  if ($error) {
6419  $this->db->rollback();
6420  return -1;
6421  } else {
6422  $this->db->commit();
6423  return 1;
6424  }
6425  } else {
6426  return 0;
6427  }
6428  }
6429 
6440  public function insertExtraLanguages($trigger = '', $userused = null)
6441  {
6442  global $conf, $langs, $user;
6443 
6444  if (empty($userused)) {
6445  $userused = $user;
6446  }
6447 
6448  $error = 0;
6449 
6450  if (!empty($conf->global->MAIN_EXTRALANGUAGES_DISABLED)) {
6451  return 0; // For avoid conflicts if trigger used
6452  }
6453 
6454  if (is_array($this->array_languages)) {
6455  $new_array_languages = $this->array_languages;
6456 
6457  foreach ($new_array_languages as $key => $value) {
6458  $attributeKey = $key;
6459  $attributeType = $this->fields[$attributeKey]['type'];
6460  $attributeLabel = $this->fields[$attributeKey]['label'];
6461 
6462  //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6463  //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6464 
6465  switch ($attributeType) {
6466  case 'int':
6467  if (!is_numeric($value) && $value != '') {
6468  $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6469  return -1;
6470  } elseif ($value == '') {
6471  $new_array_languages[$key] = null;
6472  }
6473  break;
6474  case 'double':
6475  $value = price2num($value);
6476  if (!is_numeric($value) && $value != '') {
6477  dol_syslog($langs->trans("ExtraLanguageHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6478  $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6479  return -1;
6480  } elseif ($value == '') {
6481  $new_array_languages[$key] = null;
6482  } else {
6483  $new_array_languages[$key] = $value;
6484  }
6485  break;
6486  /*case 'select': // Not required, we chosed value='0' for undefined values
6487  if ($value=='-1')
6488  {
6489  $this->array_options[$key] = null;
6490  }
6491  break;*/
6492  }
6493  }
6494 
6495  $this->db->begin();
6496 
6497  $table_element = $this->table_element;
6498  if ($table_element == 'categorie') {
6499  $table_element = 'categories'; // For compatibility
6500  }
6501 
6502  dol_syslog(get_class($this)."::insertExtraLanguages delete then insert", LOG_DEBUG);
6503 
6504  foreach ($new_array_languages as $key => $langcodearray) { // $key = 'name', 'town', ...
6505  foreach ($langcodearray as $langcode => $value) {
6506  $sql_del = "DELETE FROM ".$this->db->prefix()."object_lang";
6507  $sql_del .= " WHERE fk_object = ".((int) $this->id)." AND property = '".$this->db->escape($key)."' AND type_object = '".$this->db->escape($table_element)."'";
6508  $sql_del .= " AND lang = '".$this->db->escape($langcode)."'";
6509  $this->db->query($sql_del);
6510 
6511  if ($value !== '') {
6512  $sql = "INSERT INTO ".$this->db->prefix()."object_lang (fk_object, property, type_object, lang, value";
6513  $sql .= ") VALUES (".$this->id.", '".$this->db->escape($key)."', '".$this->db->escape($table_element)."', '".$this->db->escape($langcode)."', '".$this->db->escape($value)."'";
6514  $sql .= ")";
6515 
6516  $resql = $this->db->query($sql);
6517  if (!$resql) {
6518  $this->error = $this->db->lasterror();
6519  $error++;
6520  break;
6521  }
6522  }
6523  }
6524  }
6525 
6526  if (!$error && $trigger) {
6527  // Call trigger
6528  $this->context = array('extralanguagesaddupdate'=>1);
6529  $result = $this->call_trigger($trigger, $userused);
6530  if ($result < 0) {
6531  $error++;
6532  }
6533  // End call trigger
6534  }
6535 
6536  if ($error) {
6537  $this->db->rollback();
6538  return -1;
6539  } else {
6540  $this->db->commit();
6541  return 1;
6542  }
6543  } else {
6544  return 0;
6545  }
6546  }
6547 
6558  public function updateExtraField($key, $trigger = null, $userused = null)
6559  {
6560  global $conf, $langs, $user;
6561 
6562  if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) {
6563  return 0;
6564  }
6565 
6566  if (empty($userused)) {
6567  $userused = $user;
6568  }
6569 
6570  $error = 0;
6571 
6572  if (!empty($this->array_options) && isset($this->array_options["options_".$key])) {
6573  // Check parameters
6574  $langs->load('admin');
6575  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6576  $extrafields = new ExtraFields($this->db);
6577  $extrafields->fetch_name_optionals_label($this->table_element);
6578 
6579  $value = $this->array_options["options_".$key];
6580 
6581  $attributeType = $extrafields->attributes[$this->table_element]['type'][$key];
6582  $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key];
6583  $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key];
6584  $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
6585  $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$key];
6586 
6587  // Similar code than into insertExtraFields
6588  if ($attributeRequired) {
6589  $mandatorypb = false;
6590  if ($attributeType == 'link' && $this->array_options["options_".$key] == '-1') {
6591  $mandatorypb = true;
6592  }
6593  if ($this->array_options["options_".$key] === '') {
6594  $mandatorypb = true;
6595  }
6596  if ($mandatorypb) {
6597  $langs->load("errors");
6598  dol_syslog("Mandatory field 'options_".$key."' is empty during update and set to required into definition of extrafields");
6599  $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6600  return -1;
6601  }
6602  }
6603 
6604  //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6605  //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6606 
6607  if (!empty($attrfieldcomputed)) {
6608  if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS)) {
6609  $value = dol_eval($attrfieldcomputed, 1, 0, '');
6610  dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
6611  $this->array_options["options_".$key] = $value;
6612  } else {
6613  $this->array_options["options_".$key] = null;
6614  }
6615  }
6616 
6617  switch ($attributeType) {
6618  case 'int':
6619  if (!is_numeric($value) && $value != '') {
6620  $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6621  return -1;
6622  } elseif ($value === '') {
6623  $this->array_options["options_".$key] = null;
6624  }
6625  break;
6626  case 'double':
6627  $value = price2num($value);
6628  if (!is_numeric($value) && $value != '') {
6629  dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6630  $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6631  return -1;
6632  } elseif ($value === '') {
6633  $value = null;
6634  }
6635  //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6636  $this->array_options["options_".$key] = $value;
6637  break;
6638  /*case 'select': // Not required, we chosed value='0' for undefined values
6639  if ($value=='-1')
6640  {
6641  $this->array_options[$key] = null;
6642  }
6643  break;*/
6644  case 'price':
6645  $this->array_options["options_".$key] = price2num($this->array_options["options_".$key]);
6646  break;
6647  case 'date':
6648  case 'datetime':
6649  if (empty($this->array_options["options_".$key])) {
6650  $this->array_options["options_".$key] = null;
6651  } else {
6652  $this->array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key]);
6653  }
6654  break;
6655  case 'datetimegmt':
6656  if (empty($this->array_options["options_".$key])) {
6657  $this->array_options["options_".$key] = null;
6658  } else {
6659  $this->array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key], 'gmt');
6660  }
6661  break;
6662  case 'boolean':
6663  if (empty($this->array_options["options_".$key])) {
6664  $this->array_options["options_".$key] = null;
6665  }
6666  break;
6667  case 'link':
6668  if ($this->array_options["options_".$key] === '') {
6669  $this->array_options["options_".$key] = null;
6670  }
6671  break;
6672  /*
6673  case 'link':
6674  $param_list = array_keys($attributeParam['options']);
6675  // 0 : ObjectName
6676  // 1 : classPath
6677  $InfoFieldList = explode(":", $param_list[0]);
6678  dol_include_once($InfoFieldList[1]);
6679  if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
6680  {
6681  if ($value == '-1') // -1 is key for no defined in combo list of objects
6682  {
6683  $new_array_options[$key] = '';
6684  } elseif ($value) {
6685  $object = new $InfoFieldList[0]($this->db);
6686  if (is_numeric($value)) $res = $object->fetch($value); // Common case
6687  else $res = $object->fetch('', $value); // For compatibility
6688 
6689  if ($res > 0) $new_array_options[$key] = $object->id;
6690  else {
6691  $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
6692  $this->db->rollback();
6693  return -1;
6694  }
6695  }
6696  } else {
6697  dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6698  }
6699  break;
6700  */
6701  case 'checkbox':
6702  case 'chkbxlst':
6703  if (is_array($this->array_options[$key])) {
6704  $new_array_options[$key] = implode(',', $this->array_options[$key]);
6705  } else {
6706  $new_array_options[$key] = $this->array_options[$key];
6707  }
6708  break;
6709  }
6710 
6711  $this->db->begin();
6712 
6713  $linealreadyfound = 0;
6714 
6715  // 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)
6716  $sql = "SELECT COUNT(rowid) as nb FROM ".$this->db->prefix().$this->table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6717  $resql = $this->db->query($sql);
6718  if ($resql) {
6719  $tmpobj = $this->db->fetch_object($resql);
6720  if ($tmpobj) {
6721  $linealreadyfound = $tmpobj->nb;
6722  }
6723  }
6724 
6725  if ($linealreadyfound) {
6726  if ($this->array_options["options_".$key] === null) {
6727  $sql = "UPDATE ".$this->db->prefix().$this->table_element."_extrafields SET ".$key." = null";
6728  } else {
6729  $sql = "UPDATE ".$this->db->prefix().$this->table_element."_extrafields SET ".$key." = '".$this->db->escape($this->array_options["options_".$key])."'";
6730  }
6731  $sql .= " WHERE fk_object = ".((int) $this->id);
6732  } else {
6733  $result = $this->insertExtraFields('', $user);
6734  if ($result < 0) {
6735  $error++;
6736  }
6737  }
6738 
6739  $resql = $this->db->query($sql);
6740  if (!$resql) {
6741  $error++;
6742  $this->error = $this->db->lasterror();
6743  }
6744  if (!$error && $trigger) {
6745  // Call trigger
6746  $this->context = array('extrafieldupdate'=>1);
6747  $result = $this->call_trigger($trigger, $userused);
6748  if ($result < 0) {
6749  $error++;
6750  }
6751  // End call trigger
6752  }
6753 
6754  if ($error) {
6755  dol_syslog(__METHOD__.$this->error, LOG_ERR);
6756  $this->db->rollback();
6757  return -1;
6758  } else {
6759  $this->db->commit();
6760  return 1;
6761  }
6762  } else {
6763  return 0;
6764  }
6765  }
6766 
6777  public function updateExtraLanguages($key, $trigger = null, $userused = null)
6778  {
6779  global $conf, $langs, $user;
6780 
6781  if (empty($userused)) {
6782  $userused = $user;
6783  }
6784 
6785  $error = 0;
6786 
6787  if (!empty($conf->global->MAIN_EXTRALANGUAGES_DISABLED)) {
6788  return 0; // For avoid conflicts if trigger used
6789  }
6790 
6791  return 0;
6792  }
6793 
6794 
6809  public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0, $nonewbutton = 0)
6810  {
6811  global $conf, $langs, $form;
6812 
6813  if (!is_object($form)) {
6814  require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
6815  $form = new Form($this->db);
6816  }
6817 
6818  if (!empty($this->fields)) {
6819  $val = $this->fields[$key];
6820  }
6821 
6822  // Validation tests and output
6823  $fieldValidationErrorMsg = '';
6824  $validationClass = '';
6825  $fieldValidationErrorMsg = $this->getFieldError($key);
6826  if (!empty($fieldValidationErrorMsg)) {
6827  $validationClass = ' --error'; // the -- is use as class state in css : .--error can't be be defined alone it must be define with another class like .my-class.--error or input.--error
6828  } else {
6829  $validationClass = ' --success'; // the -- is use as class state in css : .--success can't be be defined alone it must be define with another class like .my-class.--success or input.--success
6830  }
6831 
6832  $out = '';
6833  $type = '';
6834  $isDependList = 0;
6835  $param = array();
6836  $param['options'] = array();
6837  $reg = array();
6838  $size = !empty($this->fields[$key]['size']) ? $this->fields[$key]['size'] : 0;
6839  // Because we work on extrafields
6840  if (preg_match('/^(integer|link):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6841  $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
6842  $type = 'link';
6843  } elseif (preg_match('/^(integer|link):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6844  $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
6845  $type = 'link';
6846  } elseif (preg_match('/^(integer|link):(.*):(.*)/i', $val['type'], $reg)) {
6847  $param['options'] = array($reg[2].':'.$reg[3] => 'N');
6848  $type = 'link';
6849  } elseif (preg_match('/^(sellist):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6850  $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
6851  $type = 'sellist';
6852  } elseif (preg_match('/^(sellist):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6853  $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
6854  $type = 'sellist';
6855  } elseif (preg_match('/^(sellist):(.*):(.*)/i', $val['type'], $reg)) {
6856  $param['options'] = array($reg[2].':'.$reg[3] => 'N');
6857  $type = 'sellist';
6858  } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
6859  $param['options'] = array($reg[1] => 'N');
6860  $type = 'chkbxlst';
6861  } elseif (preg_match('/varchar\‍((\d+)\‍)/', $val['type'], $reg)) {
6862  $param['options'] = array();
6863  $type = 'varchar';
6864  $size = $reg[1];
6865  } elseif (preg_match('/varchar/', $val['type'])) {
6866  $param['options'] = array();
6867  $type = 'varchar';
6868  } else {
6869  $param['options'] = array();
6870  $type = $this->fields[$key]['type'];
6871  }
6872 
6873  // Special case that force options and type ($type can be integer, varchar, ...)
6874  if (!empty($this->fields[$key]['arrayofkeyval']) && is_array($this->fields[$key]['arrayofkeyval'])) {
6875  $param['options'] = $this->fields[$key]['arrayofkeyval'];
6876  $type = 'select';
6877  }
6878 
6879  $label = $this->fields[$key]['label'];
6880  //$elementtype=$this->fields[$key]['elementtype']; // Seems not used
6881  $default = (!empty($this->fields[$key]['default']) ? $this->fields[$key]['default'] : '');
6882  $computed = (!empty($this->fields[$key]['computed']) ? $this->fields[$key]['computed'] : '');
6883  $unique = (!empty($this->fields[$key]['unique']) ? $this->fields[$key]['unique'] : 0);
6884  $required = (!empty($this->fields[$key]['required']) ? $this->fields[$key]['required'] : 0);
6885  $autofocusoncreate = (!empty($this->fields[$key]['autofocusoncreate']) ? $this->fields[$key]['autofocusoncreate'] : 0);
6886 
6887  $langfile = (!empty($this->fields[$key]['langfile']) ? $this->fields[$key]['langfile'] : '');
6888  $list = (!empty($this->fields[$key]['list']) ? $this->fields[$key]['list'] : 0);
6889  $hidden = (in_array(abs($this->fields[$key]['visible']), array(0, 2)) ? 1 : 0);
6890 
6891  $objectid = $this->id;
6892 
6893  if ($computed) {
6894  if (!preg_match('/^search_/', $keyprefix)) {
6895  return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
6896  } else {
6897  return '';
6898  }
6899  }
6900 
6901  // Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine
6902  if (empty($morecss) && !empty($val['css'])) {
6903  $morecss = $val['css'];
6904  } elseif (empty($morecss)) {
6905  if ($type == 'date') {
6906  $morecss = 'minwidth100imp';
6907  } elseif ($type == 'datetime' || $type == 'link') { // link means an foreign key to another primary id
6908  $morecss = 'minwidth200imp';
6909  } elseif (in_array($type, array('int', 'integer', 'price')) || preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', $type)) {
6910  $morecss = 'maxwidth75';
6911  } elseif ($type == 'url') {
6912  $morecss = 'minwidth400';
6913  } elseif ($type == 'boolean') {
6914  $morecss = '';
6915  } else {
6916  if (round($size) < 12) {
6917  $morecss = 'minwidth100';
6918  } elseif (round($size) <= 48) {
6919  $morecss = 'minwidth200';
6920  } else {
6921  $morecss = 'minwidth400';
6922  }
6923  }
6924  }
6925 
6926  // Add validation state class
6927  if (!empty($validationClass)) {
6928  $morecss.= $validationClass;
6929  }
6930 
6931  if (in_array($type, array('date'))) {
6932  $tmp = explode(',', $size);
6933  $newsize = $tmp[0];
6934  $showtime = 0;
6935 
6936  // Do not show current date when field not required (see selectDate() method)
6937  if (!$required && $value == '') {
6938  $value = '-1';
6939  }
6940 
6941  // TODO Must also support $moreparam
6942  $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
6943  } elseif (in_array($type, array('datetime'))) {
6944  $tmp = explode(',', $size);
6945  $newsize = $tmp[0];
6946  $showtime = 1;
6947 
6948  // Do not show current date when field not required (see selectDate() method)
6949  if (!$required && $value == '') $value = '-1';
6950 
6951  // TODO Must also support $moreparam
6952  $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
6953  } elseif (in_array($type, array('duration'))) {
6954  $out = $form->select_duration($keyprefix.$key.$keysuffix, $value, 0, 'text', 0, 1);
6955  } elseif (in_array($type, array('int', 'integer'))) {
6956  $tmp = explode(',', $size);
6957  $newsize = $tmp[0];
6958  $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"'.($newsize > 0 ? ' maxlength="'.$newsize.'"' : '').' value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
6959  } elseif (in_array($type, array('real'))) {
6960  $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
6961  } elseif (preg_match('/varchar/', $type)) {
6962  $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"'.($size > 0 ? ' maxlength="'.$size.'"' : '').' value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
6963  } elseif (in_array($type, array('email', 'mail', 'phone', 'url', 'ip'))) {
6964  $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
6965  } elseif (preg_match('/^text/', $type)) {
6966  if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
6967  require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
6968  $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
6969  $out = $doleditor->Create(1);
6970  } else {
6971  $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
6972  }
6973  } elseif (preg_match('/^html/', $type)) {
6974  if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
6975  require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
6976  $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_5, '90%');
6977  $out = $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
6978  } else {
6979  $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
6980  }
6981  } elseif ($type == 'boolean') {
6982  $checked = '';
6983  if (!empty($value)) {
6984  $checked = ' checked value="1" ';
6985  } else {
6986  $checked = ' value="1" ';
6987  }
6988  $out = '<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
6989  } elseif ($type == 'price') {
6990  if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
6991  $value = price($value);
6992  }
6993  $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '.$langs->getCurrencySymbol($conf->currency);
6994  } elseif (preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', $type)) {
6995  if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
6996  $value = price($value);
6997  }
6998  $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
6999  } elseif ($type == 'select') {
7000  $out = '';
7001  if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_EXTRAFIELDS_DISABLE_SELECT2)) {
7002  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7003  $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7004  }
7005 
7006  $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7007  if ((!isset($this->fields[$key]['default'])) || ($this->fields[$key]['notnull'] != 1)) {
7008  $out .= '<option value="0">&nbsp;</option>';
7009  }
7010  foreach ($param['options'] as $keyb => $valb) {
7011  if ((string) $keyb == '') {
7012  continue;
7013  }
7014  if (strpos($valb, "|") !== false) {
7015  list($valb, $parent) = explode('|', $valb);
7016  }
7017  $out .= '<option value="'.$keyb.'"';
7018  $out .= (((string) $value == (string) $keyb) ? ' selected' : '');
7019  $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
7020  $out .= '>'.$valb.'</option>';
7021  }
7022  $out .= '</select>';
7023  } elseif ($type == 'sellist') {
7024  $out = '';
7025  if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_EXTRAFIELDS_DISABLE_SELECT2)) {
7026  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7027  $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7028  }
7029 
7030  $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7031  if (is_array($param['options'])) {
7032  $param_list = array_keys($param['options']);
7033  $InfoFieldList = explode(":", $param_list[0]);
7034  $parentName = '';
7035  $parentField = '';
7036  // 0 : tableName
7037  // 1 : label field name
7038  // 2 : key fields name (if differ of rowid)
7039  // 3 : key field parent (for dependent lists)
7040  // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
7041  // 5 : id category type
7042  // 6 : ids categories list separated by comma for category root
7043  $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7044 
7045  if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7046  if (strpos($InfoFieldList[4], 'extra.') !== false) {
7047  $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7048  } else {
7049  $keyList = $InfoFieldList[2].' as rowid';
7050  }
7051  }
7052  if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7053  list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7054  $keyList .= ', '.$parentField;
7055  }
7056 
7057  $filter_categorie = false;
7058  if (count($InfoFieldList) > 5) {
7059  if ($InfoFieldList[0] == 'categorie') {
7060  $filter_categorie = true;
7061  }
7062  }
7063 
7064  if ($filter_categorie === false) {
7065  $fields_label = explode('|', $InfoFieldList[1]);
7066  if (is_array($fields_label)) {
7067  $keyList .= ', ';
7068  $keyList .= implode(', ', $fields_label);
7069  }
7070 
7071  $sqlwhere = '';
7072  $sql = "SELECT " . $keyList;
7073  $sql .= " FROM " . $this->db->prefix() . $InfoFieldList[0];
7074  if (!empty($InfoFieldList[4])) {
7075  // can use SELECT request
7076  if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7077  $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7078  }
7079 
7080  // current object id can be use into filter
7081  if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7082  $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
7083  } else {
7084  $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7085  }
7086 
7087  //We have to join on extrafield table
7088  if (strpos($InfoFieldList[4], 'extra') !== false) {
7089  $sql .= " as main, " . $this->db->prefix() . $InfoFieldList[0] . "_extrafields as extra";
7090  $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2] . " AND " . $InfoFieldList[4];
7091  } else {
7092  $sqlwhere .= " WHERE " . $InfoFieldList[4];
7093  }
7094  } else {
7095  $sqlwhere .= ' WHERE 1=1';
7096  }
7097  // Some tables may have field, some other not. For the moment we disable it.
7098  if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7099  $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7100  }
7101  $sql .= $sqlwhere;
7102  //print $sql;
7103 
7104  $sql .= ' ORDER BY ' . implode(', ', $fields_label);
7105 
7106  dol_syslog(get_class($this) . '::showInputField type=sellist', LOG_DEBUG);
7107  $resql = $this->db->query($sql);
7108  if ($resql) {
7109  $out .= '<option value="0">&nbsp;</option>';
7110  $num = $this->db->num_rows($resql);
7111  $i = 0;
7112  while ($i < $num) {
7113  $labeltoshow = '';
7114  $obj = $this->db->fetch_object($resql);
7115 
7116  // Several field into label (eq table:code|libelle:rowid)
7117  $notrans = false;
7118  $fields_label = explode('|', $InfoFieldList[1]);
7119  if (count($fields_label) > 1) {
7120  $notrans = true;
7121  foreach ($fields_label as $field_toshow) {
7122  $labeltoshow .= $obj->$field_toshow . ' ';
7123  }
7124  } else {
7125  $labeltoshow = $obj->{$InfoFieldList[1]};
7126  }
7127  $labeltoshow = dol_trunc($labeltoshow, 45);
7128 
7129  if ($value == $obj->rowid) {
7130  foreach ($fields_label as $field_toshow) {
7131  $translabel = $langs->trans($obj->$field_toshow);
7132  if ($translabel != $obj->$field_toshow) {
7133  $labeltoshow = dol_trunc($translabel) . ' ';
7134  } else {
7135  $labeltoshow = dol_trunc($obj->$field_toshow) . ' ';
7136  }
7137  }
7138  $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7139  } else {
7140  if (!$notrans) {
7141  $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7142  if ($translabel != $obj->{$InfoFieldList[1]}) {
7143  $labeltoshow = dol_trunc($translabel, 18);
7144  } else {
7145  $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]});
7146  }
7147  }
7148  if (empty($labeltoshow)) {
7149  $labeltoshow = '(not defined)';
7150  }
7151  if ($value == $obj->rowid) {
7152  $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7153  }
7154 
7155  if (!empty($InfoFieldList[3]) && $parentField) {
7156  $parent = $parentName . ':' . $obj->{$parentField};
7157  $isDependList = 1;
7158  }
7159 
7160  $out .= '<option value="' . $obj->rowid . '"';
7161  $out .= ($value == $obj->rowid ? ' selected' : '');
7162  $out .= (!empty($parent) ? ' parent="' . $parent . '"' : '');
7163  $out .= '>' . $labeltoshow . '</option>';
7164  }
7165 
7166  $i++;
7167  }
7168  $this->db->free($resql);
7169  } else {
7170  print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
7171  }
7172  } else {
7173  require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7174  $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
7175  $out .= '<option value="0">&nbsp;</option>';
7176  foreach ($data as $data_key => $data_value) {
7177  $out .= '<option value="' . $data_key . '"';
7178  $out .= ($value == $data_key ? ' selected' : '');
7179  $out .= '>' . $data_value . '</option>';
7180  }
7181  }
7182  }
7183  $out .= '</select>';
7184  } elseif ($type == 'checkbox') {
7185  $value_arr = explode(',', $value);
7186  $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ?null:$param['options']), $value_arr, '', 0, $morecss, 0, '100%');
7187  } elseif ($type == 'radio') {
7188  $out = '';
7189  foreach ($param['options'] as $keyopt => $valopt) {
7190  $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
7191  $out .= ' value="'.$keyopt.'"';
7192  $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
7193  $out .= ($value == $keyopt ? 'checked' : '');
7194  $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$valopt.'</label><br>';
7195  }
7196  } elseif ($type == 'chkbxlst') {
7197  if (is_array($value)) {
7198  $value_arr = $value;
7199  } else {
7200  $value_arr = explode(',', $value);
7201  }
7202 
7203  if (is_array($param['options'])) {
7204  $param_list = array_keys($param['options']);
7205  $InfoFieldList = explode(":", $param_list[0]);
7206  $parentName = '';
7207  $parentField = '';
7208  // 0 : tableName
7209  // 1 : label field name
7210  // 2 : key fields name (if differ of rowid)
7211  // 3 : key field parent (for dependent lists)
7212  // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
7213  // 5 : id category type
7214  // 6 : ids categories list separated by comma for category root
7215  $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7216 
7217  if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7218  list ($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7219  $keyList .= ', '.$parentField;
7220  }
7221  if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7222  if (strpos($InfoFieldList[4], 'extra.') !== false) {
7223  $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7224  } else {
7225  $keyList = $InfoFieldList[2].' as rowid';
7226  }
7227  }
7228 
7229  $filter_categorie = false;
7230  if (count($InfoFieldList) > 5) {
7231  if ($InfoFieldList[0] == 'categorie') {
7232  $filter_categorie = true;
7233  }
7234  }
7235 
7236  if ($filter_categorie === false) {
7237  $fields_label = explode('|', $InfoFieldList[1]);
7238  if (is_array($fields_label)) {
7239  $keyList .= ', ';
7240  $keyList .= implode(', ', $fields_label);
7241  }
7242 
7243  $sqlwhere = '';
7244  $sql = "SELECT " . $keyList;
7245  $sql .= ' FROM ' . $this->db->prefix() . $InfoFieldList[0];
7246  if (!empty($InfoFieldList[4])) {
7247  // can use SELECT request
7248  if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7249  $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7250  }
7251 
7252  // current object id can be use into filter
7253  if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7254  $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
7255  } else {
7256  $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7257  }
7258 
7259  // We have to join on extrafield table
7260  if (strpos($InfoFieldList[4], 'extra') !== false) {
7261  $sql .= ' as main, ' . $this->db->prefix() . $InfoFieldList[0] . '_extrafields as extra';
7262  $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2] . " AND " . $InfoFieldList[4];
7263  } else {
7264  $sqlwhere .= " WHERE " . $InfoFieldList[4];
7265  }
7266  } else {
7267  $sqlwhere .= ' WHERE 1=1';
7268  }
7269  // Some tables may have field, some other not. For the moment we disable it.
7270  if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7271  $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7272  }
7273  // $sql.=preg_replace('/^ AND /','',$sqlwhere);
7274  // print $sql;
7275 
7276  $sql .= $sqlwhere;
7277  dol_syslog(get_class($this) . '::showInputField type=chkbxlst', LOG_DEBUG);
7278  $resql = $this->db->query($sql);
7279  if ($resql) {
7280  $num = $this->db->num_rows($resql);
7281  $i = 0;
7282 
7283  $data = array();
7284 
7285  while ($i < $num) {
7286  $labeltoshow = '';
7287  $obj = $this->db->fetch_object($resql);
7288 
7289  $notrans = false;
7290  // Several field into label (eq table:code|libelle:rowid)
7291  $fields_label = explode('|', $InfoFieldList[1]);
7292  if (count($fields_label) > 1) {
7293  $notrans = true;
7294  foreach ($fields_label as $field_toshow) {
7295  $labeltoshow .= $obj->$field_toshow . ' ';
7296  }
7297  } else {
7298  $labeltoshow = $obj->{$InfoFieldList[1]};
7299  }
7300  $labeltoshow = dol_trunc($labeltoshow, 45);
7301 
7302  if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7303  foreach ($fields_label as $field_toshow) {
7304  $translabel = $langs->trans($obj->$field_toshow);
7305  if ($translabel != $obj->$field_toshow) {
7306  $labeltoshow = dol_trunc($translabel, 18) . ' ';
7307  } else {
7308  $labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
7309  }
7310  }
7311 
7312  $data[$obj->rowid] = $labeltoshow;
7313  } else {
7314  if (!$notrans) {
7315  $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7316  if ($translabel != $obj->{$InfoFieldList[1]}) {
7317  $labeltoshow = dol_trunc($translabel, 18);
7318  } else {
7319  $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
7320  }
7321  }
7322  if (empty($labeltoshow)) {
7323  $labeltoshow = '(not defined)';
7324  }
7325 
7326  if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7327  $data[$obj->rowid] = $labeltoshow;
7328  }
7329 
7330  if (!empty($InfoFieldList[3]) && $parentField) {
7331  $parent = $parentName . ':' . $obj->{$parentField};
7332  $isDependList = 1;
7333  }
7334 
7335  $data[$obj->rowid] = $labeltoshow;
7336  }
7337 
7338  $i++;
7339  }
7340  $this->db->free($resql);
7341 
7342  $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, '', 0, $morecss, 0, '100%');