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%');
7343  } else {
7344  print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
7345  }
7346  } else {
7347  require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7348  $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
7349  $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, '', 0, $morecss, 0, '100%');
7350  }
7351  }
7352  } elseif ($type == 'link') {
7353  $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
7354  $param_list_array = explode(':', $param_list[0]);
7355  $showempty = (($required && $default != '') ? 0 : 1);
7356 
7357  if (!preg_match('/search_/', $keyprefix)) {
7358  if (!empty($param_list_array[2])) { // If the entry into $fields is set to add a create button
7359  if (!empty($this->fields[$key]['picto'])) {
7360  $morecss .= ' widthcentpercentminusxx';
7361  } else {
7362  $morecss .= ' widthcentpercentminusx';
7363  }
7364  } else {
7365  if (!empty($this->fields[$key]['picto'])) {
7366  $morecss .= ' widthcentpercentminusx';
7367  }
7368  }
7369  }
7370 
7371  $out = $form->selectForForms($param_list[0], $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, $moreparam, 0, empty($val['disabled']) ? 0 : 1);
7372 
7373  if (!empty($param_list_array[2])) { // If the entry into $fields is set to add a create button
7374  if (!GETPOSTISSET('backtopage') && empty($val['disabled']) && empty($nonewbutton)) { // To avoid to open several times the 'Create Object' button and to avoid to have button if field is protected by a "disabled".
7375  list($class, $classfile) = explode(':', $param_list[0]);
7376  if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) {
7377  $url_path = dol_buildpath(dirname(dirname($classfile)).'/card.php', 1);
7378  } else {
7379  $url_path = dol_buildpath(dirname(dirname($classfile)).'/'.strtolower($class).'_card.php', 1);
7380  }
7381  $paramforthenewlink = '';
7382  $paramforthenewlink .= (GETPOSTISSET('action') ? '&action='.GETPOST('action', 'aZ09') : '');
7383  $paramforthenewlink .= (GETPOSTISSET('id') ? '&id='.GETPOST('id', 'int') : '');
7384  $paramforthenewlink .= (GETPOSTISSET('origin') ? '&origin='.GETPOST('origin', 'aZ09') : '');
7385  $paramforthenewlink .= (GETPOSTISSET('originid') ? '&originid='.GETPOST('originid', 'int') : '');
7386  $paramforthenewlink .= '&fk_'.strtolower($class).'=--IDFORBACKTOPAGE--';
7387  // TODO Add Javascript code to add input fields already filled into $paramforthenewlink so we won't loose them when going back to main page
7388  $out .= '<a class="butActionNew" title="'.$langs->trans("New").'" href="'.$url_path.'?action=create&backtopage='.urlencode($_SERVER['PHP_SELF'].($paramforthenewlink ? '?'.$paramforthenewlink : '')).'"><span class="fa fa-plus-circle valignmiddle"></span></a>';
7389  }
7390  }
7391  } elseif ($type == 'password') {
7392  // If prefix is 'search_', field is used as a filter, we use a common text field.
7393  $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
7394  } elseif ($type == 'array') {
7395  $newval = $val;
7396  $newval['type'] = 'varchar(256)';
7397 
7398  $out = '';
7399  if (!empty($value)) {
7400  foreach ($value as $option) {
7401  $out .= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
7402  $out .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $morecss).'<br></span>';
7403  }
7404  }
7405  $out .= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
7406 
7407  $newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
7408  $newInput .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $morecss).'<br></span>';
7409 
7410  if (!empty($conf->use_javascript_ajax)) {
7411  $out .= '
7412  <script nonce="'.getNonce().'">
7413  $(document).ready(function() {
7414  $("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
7415  $("'.dol_escape_js($newInput).'").insertBefore(this);
7416  });
7417 
7418  $(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
7419  $(this).parent().remove();
7420  });
7421  });
7422  </script>';
7423  }
7424  }
7425  if (!empty($hidden)) {
7426  $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
7427  }
7428 
7429  if ($isDependList==1) {
7430  $out .= $this->getJSListDependancies('_common');
7431  }
7432  /* Add comments
7433  if ($type == 'date') $out.=' (YYYY-MM-DD)';
7434  elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
7435  */
7436 
7437  // Display error message for field
7438  if (!empty($fieldValidationErrorMsg) && function_exists('getFieldErrorIcon')) {
7439  $out .= ' '.getFieldErrorIcon($fieldValidationErrorMsg);
7440  }
7441 
7442  return $out;
7443  }
7444 
7458  public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '')
7459  {
7460  global $conf, $langs, $form;
7461 
7462  if (!is_object($form)) {
7463  require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
7464  $form = new Form($this->db);
7465  }
7466 
7467  $label = empty($val['label']) ? '' : $val['label'];
7468  $type = empty($val['type']) ? '' : $val['type'];
7469  $size = empty($val['css']) ? '' : $val['css'];
7470  $reg = array();
7471 
7472  // Convert var to be able to share same code than showOutputField of extrafields
7473  if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
7474  $type = 'varchar'; // convert varchar(xx) int varchar
7475  $size = $reg[1];
7476  } elseif (preg_match('/varchar/', $type)) {
7477  $type = 'varchar'; // convert varchar(xx) int varchar
7478  }
7479  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
7480  $type = 'select';
7481  }
7482  if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
7483  $type = 'link';
7484  }
7485 
7486  $default = empty($val['default']) ? '' : $val['default'];
7487  $computed = empty($val['computed']) ? '' : $val['computed'];
7488  $unique = empty($val['unique']) ? '' : $val['unique'];
7489  $required = empty($val['required']) ? '' : $val['required'];
7490  $param = array();
7491  $param['options'] = array();
7492 
7493  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
7494  $param['options'] = $val['arrayofkeyval'];
7495  }
7496  if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
7497  $type = 'link';
7498  $stringforoptions = $reg[1].':'.$reg[2];
7499  if ($reg[1] == 'User') {
7500  $stringforoptions .= ':-1';
7501  }
7502  $param['options'] = array($stringforoptions => $stringforoptions);
7503  } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7504  $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7505  $type = 'sellist';
7506  } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
7507  $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
7508  $type = 'sellist';
7509  } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
7510  $param['options'] = array($reg[1].':'.$reg[2] => 'N');
7511  $type = 'sellist';
7512  } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
7513  $param['options'] = array($reg[1] => 'N');
7514  $type = 'chkbxlst';
7515  }
7516 
7517  $langfile = empty($val['langfile']) ? '' : $val['langfile'];
7518  $list = (empty($val['list']) ? '' : $val['list']);
7519  $help = (empty($val['help']) ? '' : $val['help']);
7520  $hidden = (($val['visible'] == 0) ? 1 : 0); // If zero, we are sure it is hidden, otherwise we show. If it depends on mode (view/create/edit form or list, this must be filtered by caller)
7521 
7522  if ($hidden) {
7523  return '';
7524  }
7525 
7526  // If field is a computed field, value must become result of compute
7527  if ($computed) {
7528  // Make the eval of compute string
7529  //var_dump($computed);
7530  $value = dol_eval($computed, 1, 0, '');
7531  }
7532 
7533  if (empty($morecss)) {
7534  if ($type == 'date') {
7535  $morecss = 'minwidth100imp';
7536  } elseif ($type == 'datetime' || $type == 'timestamp') {
7537  $morecss = 'minwidth200imp';
7538  } elseif (in_array($type, array('int', 'double', 'price'))) {
7539  $morecss = 'maxwidth75';
7540  } elseif ($type == 'url') {
7541  $morecss = 'minwidth400';
7542  } elseif ($type == 'boolean') {
7543  $morecss = '';
7544  } else {
7545  if (is_numeric($size) && round($size) < 12) {
7546  $morecss = 'minwidth100';
7547  } elseif (is_numeric($size) && round($size) <= 48) {
7548  $morecss = 'minwidth200';
7549  } else {
7550  $morecss = 'minwidth400';
7551  }
7552  }
7553  }
7554 
7555  // Format output value differently according to properties of field
7556  if (in_array($key, array('rowid', 'ref')) && method_exists($this, 'getNomUrl')) {
7557  if ($key != 'rowid' || empty($this->fields['ref'])) { // If we want ref field or if we want ID and there is no ref field, we show the link.
7558  $value = $this->getNomUrl(1, '', 0, '', 1);
7559  }
7560  } elseif ($key == 'status' && method_exists($this, 'getLibStatut')) {
7561  $value = $this->getLibStatut(3);
7562  } elseif ($type == 'date') {
7563  if (!empty($value)) {
7564  $value = dol_print_date($value, 'day'); // We suppose dates without time are always gmt (storage of course + output)
7565  } else {
7566  $value = '';
7567  }
7568  } elseif ($type == 'datetime' || $type == 'timestamp') {
7569  if (!empty($value)) {
7570  $value = dol_print_date($value, 'dayhour', 'tzuserrel');
7571  } else {
7572  $value = '';
7573  }
7574  } elseif ($type == 'duration') {
7575  include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
7576  if (!is_null($value) && $value !== '') {
7577  $value = convertSecondToTime($value, 'allhourmin');
7578  }
7579  } elseif ($type == 'double' || $type == 'real') {
7580  if (!is_null($value) && $value !== '') {
7581  $value = price($value);
7582  }
7583  } elseif ($type == 'boolean') {
7584  $checked = '';
7585  if (!empty($value)) {
7586  $checked = ' checked ';
7587  }
7588  $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
7589  } elseif ($type == 'mail' || $type == 'email') {
7590  $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
7591  } elseif ($type == 'url') {
7592  $value = dol_print_url($value, '_blank', 32, 1);
7593  } elseif ($type == 'phone') {
7594  $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
7595  } elseif ($type == 'ip') {
7596  $value = dol_print_ip($value, 0);
7597  } elseif ($type == 'price') {
7598  if (!is_null($value) && $value !== '') {
7599  $value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
7600  }
7601  } elseif ($type == 'select') {
7602  $value = isset($param['options'][$value])?$param['options'][$value]:'';
7603  } elseif ($type == 'sellist') {
7604  $param_list = array_keys($param['options']);
7605  $InfoFieldList = explode(":", $param_list[0]);
7606 
7607  $selectkey = "rowid";
7608  $keyList = 'rowid';
7609 
7610  if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7611  $selectkey = $InfoFieldList[2];
7612  $keyList = $InfoFieldList[2].' as rowid';
7613  }
7614 
7615  $fields_label = explode('|', $InfoFieldList[1]);
7616  if (is_array($fields_label)) {
7617  $keyList .= ', ';
7618  $keyList .= implode(', ', $fields_label);
7619  }
7620 
7621  $filter_categorie = false;
7622  if (count($InfoFieldList) > 5) {
7623  if ($InfoFieldList[0] == 'categorie') {
7624  $filter_categorie = true;
7625  }
7626  }
7627 
7628  $sql = "SELECT ".$keyList;
7629  $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
7630  if (strpos($InfoFieldList[4], 'extra') !== false) {
7631  $sql .= ' as main';
7632  }
7633  if ($selectkey == 'rowid' && empty($value)) {
7634  $sql .= " WHERE ".$selectkey." = 0";
7635  } elseif ($selectkey == 'rowid') {
7636  $sql .= " WHERE ".$selectkey." = ".((int) $value);
7637  } else {
7638  $sql .= " WHERE ".$selectkey." = '".$this->db->escape($value)."'";
7639  }
7640 
7641  //$sql.= ' AND entity = '.$conf->entity;
7642 
7643  dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
7644  $resql = $this->db->query($sql);
7645  if ($resql) {
7646  if ($filter_categorie === false) {
7647  $value = ''; // value was used, so now we reste it to use it to build final output
7648  $numrows = $this->db->num_rows($resql);
7649  if ($numrows) {
7650  $obj = $this->db->fetch_object($resql);
7651 
7652  // Several field into label (eq table:code|libelle:rowid)
7653  $fields_label = explode('|', $InfoFieldList[1]);
7654 
7655  if (is_array($fields_label) && count($fields_label) > 1) {
7656  foreach ($fields_label as $field_toshow) {
7657  $translabel = '';
7658  if (!empty($obj->$field_toshow)) {
7659  $translabel = $langs->trans($obj->$field_toshow);
7660  }
7661  if ($translabel != $field_toshow) {
7662  $value .= dol_trunc($translabel, 18) . ' ';
7663  } else {
7664  $value .= $obj->$field_toshow . ' ';
7665  }
7666  }
7667  } else {
7668  $translabel = '';
7669  if (!empty($obj->{$InfoFieldList[1]})) {
7670  $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7671  }
7672  if ($translabel != $obj->{$InfoFieldList[1]}) {
7673  $value = dol_trunc($translabel, 18);
7674  } else {
7675  $value = $obj->{$InfoFieldList[1]};
7676  }
7677  }
7678  }
7679  } else {
7680  require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
7681 
7682  $toprint = array();
7683  $obj = $this->db->fetch_object($resql);
7684  $c = new Categorie($this->db);
7685  $c->fetch($obj->rowid);
7686  $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
7687  foreach ($ways as $way) {
7688  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
7689  }
7690  $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
7691  }
7692  } else {
7693  dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
7694  }
7695  } elseif ($type == 'radio') {
7696  $value = $param['options'][$value];
7697  } elseif ($type == 'checkbox') {
7698  $value_arr = explode(',', $value);
7699  $value = '';
7700  if (is_array($value_arr) && count($value_arr) > 0) {
7701  $toprint = array();
7702  foreach ($value_arr as $keyval => $valueval) {
7703  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$param['options'][$valueval].'</li>';
7704  }
7705  $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
7706  }
7707  } elseif ($type == 'chkbxlst') {
7708  $value_arr = explode(',', $value);
7709 
7710  $param_list = array_keys($param['options']);
7711  $InfoFieldList = explode(":", $param_list[0]);
7712 
7713  $selectkey = "rowid";
7714  $keyList = 'rowid';
7715 
7716  if (count($InfoFieldList) >= 3) {
7717  $selectkey = $InfoFieldList[2];
7718  $keyList = $InfoFieldList[2].' as rowid';
7719  }
7720 
7721  $fields_label = explode('|', $InfoFieldList[1]);
7722  if (is_array($fields_label)) {
7723  $keyList .= ', ';
7724  $keyList .= implode(', ', $fields_label);
7725  }
7726 
7727  $filter_categorie = false;
7728  if (count($InfoFieldList) > 5) {
7729  if ($InfoFieldList[0] == 'categorie') {
7730  $filter_categorie = true;
7731  }
7732  }
7733 
7734  $sql = "SELECT ".$keyList;
7735  $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
7736  if (strpos($InfoFieldList[4], 'extra') !== false) {
7737  $sql .= ' as main';
7738  }
7739  // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
7740  // $sql.= ' AND entity = '.$conf->entity;
7741 
7742  dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG);
7743  $resql = $this->db->query($sql);
7744  if ($resql) {
7745  if ($filter_categorie === false) {
7746  $value = ''; // value was used, so now we reste it to use it to build final output
7747  $toprint = array();
7748  while ($obj = $this->db->fetch_object($resql)) {
7749  // Several field into label (eq table:code|libelle:rowid)
7750  $fields_label = explode('|', $InfoFieldList[1]);
7751  if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7752  if (is_array($fields_label) && count($fields_label) > 1) {
7753  foreach ($fields_label as $field_toshow) {
7754  $translabel = '';
7755  if (!empty($obj->$field_toshow)) {
7756  $translabel = $langs->trans($obj->$field_toshow);
7757  }
7758  if ($translabel != $field_toshow) {
7759  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
7760  } else {
7761  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->$field_toshow . '</li>';
7762  }
7763  }
7764  } else {
7765  $translabel = '';
7766  if (!empty($obj->{$InfoFieldList[1]})) {
7767  $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7768  }
7769  if ($translabel != $obj->{$InfoFieldList[1]}) {
7770  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
7771  } else {
7772  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->{$InfoFieldList[1]} . '</li>';
7773  }
7774  }
7775  }
7776  }
7777  } else {
7778  require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
7779 
7780  $toprint = array();
7781  while ($obj = $this->db->fetch_object($resql)) {
7782  if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7783  $c = new Categorie($this->db);
7784  $c->fetch($obj->rowid);
7785  $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
7786  foreach ($ways as $way) {
7787  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
7788  }
7789  }
7790  }
7791  }
7792  $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
7793  } else {
7794  dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
7795  }
7796  } elseif ($type == 'link') {
7797  $out = '';
7798 
7799  // only if something to display (perf)
7800  if ($value) {
7801  $param_list = array_keys($param['options']); // Example: $param_list='ObjectName:classPath:-1::customer'
7802 
7803  $InfoFieldList = explode(":", $param_list[0]);
7804  $classname = $InfoFieldList[0];
7805  $classpath = $InfoFieldList[1];
7806  $getnomurlparam = (empty($InfoFieldList[2]) ? 3 : $InfoFieldList[2]);
7807  $getnomurlparam2 = (empty($InfoFieldList[4]) ? '' : $InfoFieldList[4]);
7808  if (!empty($classpath)) {
7809  dol_include_once($InfoFieldList[1]);
7810  if ($classname && class_exists($classname)) {
7811  $object = new $classname($this->db);
7812  if ($object->element === 'product') { // Special cas for product because default valut of fetch are wrong
7813  $result = $object->fetch($value, '', '', '', 0, 1, 1);
7814  } else {
7815  $result = $object->fetch($value);
7816  }
7817  if ($result > 0) {
7818  if ($object->element === 'product') {
7819  $get_name_url_param_arr = array($getnomurlparam, $getnomurlparam2, 0, -1, 0, '', 0);
7820  if (isset($val['get_name_url_params'])) {
7821  $get_name_url_params = explode(':', $val['get_name_url_params']);
7822  if (!empty($get_name_url_params)) {
7823  $param_num_max = count($get_name_url_param_arr) - 1;
7824  foreach ($get_name_url_params as $param_num => $param_value) {
7825  if ($param_num > $param_num_max) {
7826  break;
7827  }
7828  $get_name_url_param_arr[$param_num] = $param_value;
7829  }
7830  }
7831  }
7832 
7836  $value = $object->getNomUrl($get_name_url_param_arr[0], $get_name_url_param_arr[1], $get_name_url_param_arr[2], $get_name_url_param_arr[3], $get_name_url_param_arr[4], $get_name_url_param_arr[5], $get_name_url_param_arr[6]);
7837  } else {
7838  $value = $object->getNomUrl($getnomurlparam, $getnomurlparam2);
7839  }
7840  } else {
7841  $value = '';
7842  }
7843  }
7844  } else {
7845  dol_syslog('Error bad setup of extrafield', LOG_WARNING);
7846  return 'Error bad setup of extrafield';
7847  }
7848  } else {
7849  $value = '';
7850  }
7851  } elseif ($type == 'password') {
7852  $value = preg_replace('/./i', '*', $value);
7853  } elseif ($type == 'array') {
7854  $value = implode('<br>', $value);
7855  } else { // text|html|varchar
7856  $value = dol_htmlentitiesbr($value);
7857  }
7858 
7859  //print $type.'-'.$size.'-'.$value;
7860  $out = $value;
7861 
7862  return $out;
7863  }
7864 
7871  public function clearFieldError($fieldKey)
7872  {
7873  $this->error = '';
7874  unset($this->validateFieldsErrors[$fieldKey]);
7875  }
7876 
7884  public function setFieldError($fieldKey, $msg = '')
7885  {
7886  global $langs;
7887  if (empty($msg)) {
7888  $msg = $langs->trans("UnknowError");
7889  }
7890 
7891  $this->error = $this->validateFieldsErrors[$fieldKey] = $msg;
7892  }
7893 
7900  public function getFieldError($fieldKey)
7901  {
7902  if (!empty($this->validateFieldsErrors[$fieldKey])) {
7903  return $this->validateFieldsErrors[$fieldKey];
7904  }
7905  return '';
7906  }
7907 
7916  public function validateField($fields, $fieldKey, $fieldValue)
7917  {
7918  global $langs;
7919 
7920  if (!class_exists('Validate')) {
7921  require_once DOL_DOCUMENT_ROOT . '/core/class/validate.class.php';
7922  }
7923 
7924  $this->clearFieldError($fieldKey);
7925 
7926  if (!isset($fields[$fieldKey])) {
7927  $this->setFieldError($fieldKey, $langs->trans('FieldNotFoundInObject'));
7928  return false;
7929  }
7930 
7931  $val = $fields[$fieldKey];
7932 
7933  $param = array();
7934  $param['options'] = array();
7935  $type = $val['type'];
7936 
7937  $required = false;
7938  if (isset($val['notnull']) && $val['notnull'] === 1) {
7939  // 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
7940  $required = true;
7941  }
7942 
7943  $maxSize = 0;
7944  $minSize = 0;
7945 
7946  //
7947  // PREPARE Elements
7948  //
7949  $reg = array();
7950 
7951  // Convert var to be able to share same code than showOutputField of extrafields
7952  if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
7953  $type = 'varchar'; // convert varchar(xx) int varchar
7954  $maxSize = $reg[1];
7955  } elseif (preg_match('/varchar/', $type)) {
7956  $type = 'varchar'; // convert varchar(xx) int varchar
7957  }
7958 
7959  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
7960  $type = 'select';
7961  }
7962 
7963  if (!empty($val['type']) && preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
7964  $type = 'link';
7965  }
7966 
7967  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
7968  $param['options'] = $val['arrayofkeyval'];
7969  }
7970 
7971  if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
7972  $type = 'link';
7973  $param['options'] = array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
7974  } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7975  $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7976  $type = 'sellist';
7977  } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
7978  $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
7979  $type = 'sellist';
7980  } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
7981  $param['options'] = array($reg[1].':'.$reg[2] => 'N');
7982  $type = 'sellist';
7983  }
7984 
7985  //
7986  // TEST Value
7987  //
7988 
7989  // Use Validate class to allow external Modules to use data validation part instead of concentrate all test here (factoring) or just for reuse
7990  $validate = new Validate($this->db, $langs);
7991 
7992 
7993  // little trick : to perform tests with good performances sort tests by quick to low
7994 
7995  //
7996  // COMMON TESTS
7997  //
7998 
7999  // Required test and empty value
8000  if ($required && !$validate->isNotEmptyString($fieldValue)) {
8001  $this->setFieldError($fieldKey, $validate->error);
8002  return false;
8003  } elseif (!$required && !$validate->isNotEmptyString($fieldValue)) {
8004  // if no value sent and the field is not mandatory, no need to perform tests
8005  return true;
8006  }
8007 
8008  // MAX Size test
8009  if (!empty($maxSize) && !$validate->isMaxLength($fieldValue, $maxSize)) {
8010  $this->setFieldError($fieldKey, $validate->error);
8011  return false;
8012  }
8013 
8014  // MIN Size test
8015  if (!empty($minSize) && !$validate->isMinLength($fieldValue, $minSize)) {
8016  $this->setFieldError($fieldKey, $validate->error);
8017  return false;
8018  }
8019 
8020  //
8021  // TESTS for TYPE
8022  //
8023 
8024  if (in_array($type, array('date', 'datetime', 'timestamp'))) {
8025  if (!$validate->isTimestamp($fieldValue)) {
8026  $this->setFieldError($fieldKey, $validate->error);
8027  return false;
8028  } else { return true; }
8029  } elseif ($type == 'duration') {
8030  if (!$validate->isDuration($fieldValue)) {
8031  $this->setFieldError($fieldKey, $validate->error);
8032  return false;
8033  } else { return true; }
8034  } elseif (in_array($type, array('double', 'real', 'price'))) {
8035  // is numeric
8036  if (!$validate->isNumeric($fieldValue)) {
8037  $this->setFieldError($fieldKey, $validate->error);
8038  return false;
8039  } else { return true; }
8040  } elseif ($type == 'boolean') {
8041  if (!$validate->isBool($fieldValue)) {
8042  $this->setFieldError($fieldKey, $validate->error);
8043  return false;
8044  } else { return true; }
8045  } elseif ($type == 'mail') {
8046  if (!$validate->isEmail($fieldValue)) {
8047  $this->setFieldError($fieldKey, $validate->error);
8048  return false;
8049  }
8050  } elseif ($type == 'url') {
8051  if (!$validate->isUrl($fieldValue)) {
8052  $this->setFieldError($fieldKey, $validate->error);
8053  return false;
8054  } else { return true; }
8055  } elseif ($type == 'phone') {
8056  if (!$validate->isPhone($fieldValue)) {
8057  $this->setFieldError($fieldKey, $validate->error);
8058  return false;
8059  } else { return true; }
8060  } elseif ($type == 'select' || $type == 'radio') {
8061  if (!isset($param['options'][$fieldValue])) {
8062  $this->error = $langs->trans('RequireValidValue');
8063  return false;
8064  } else { return true; }
8065  } elseif ($type == 'sellist' || $type == 'chkbxlst') {
8066  $param_list = array_keys($param['options']);
8067  $InfoFieldList = explode(":", $param_list[0]);
8068  $value_arr = explode(',', $fieldValue);
8069  $value_arr = array_map(array($this->db, 'escape'), $value_arr);
8070 
8071  $selectkey = "rowid";
8072  if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
8073  $selectkey = $InfoFieldList[2];
8074  }
8075 
8076  if (!$validate->isInDb($value_arr, $InfoFieldList[0], $selectkey)) {
8077  $this->setFieldError($fieldKey, $validate->error);
8078  return false;
8079  } else { return true; }
8080  } elseif ($type == 'link') {
8081  $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
8082  $InfoFieldList = explode(":", $param_list[0]);
8083  $classname = $InfoFieldList[0];
8084  $classpath = $InfoFieldList[1];
8085  if (!$validate->isFetchable($fieldValue, $classname, $classpath)) {
8086  $this->setFieldError($fieldKey, $validate->error);
8087  return false;
8088  } else { return true; }
8089  }
8090 
8091  // if no test failled all is ok
8092  return true;
8093  }
8094 
8108  public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = 0, $display_type = 'card')
8109  {
8110  global $db, $conf, $langs, $action, $form, $hookmanager;
8111 
8112  if (!is_object($form)) {
8113  $form = new Form($db);
8114  }
8115  if (!is_object($extrafields)) {
8116  dol_syslog('Bad parameter extrafields for showOptionals', LOG_ERR);
8117  return 'Bad parameter extrafields for showOptionals';
8118  }
8119  if (!is_array($extrafields->attributes[$this->table_element])) {
8120  dol_syslog("extrafields->attributes was not loaded with extrafields->fetch_name_optionals_label(table_element);", LOG_WARNING);
8121  }
8122 
8123  $out = '';
8124 
8125  $parameters = array('mode'=>$mode, 'params'=>$params, 'keysuffix'=>$keysuffix, 'keyprefix'=>$keyprefix, 'display_type'=>$display_type);
8126  $reshook = $hookmanager->executeHooks('showOptionals', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
8127 
8128  if (empty($reshook)) {
8129  if (is_array($extrafields->attributes[$this->table_element]) && key_exists('label', $extrafields->attributes[$this->table_element]) && is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label']) > 0) {
8130  $out .= "\n";
8131  $out .= '<!-- commonobject:showOptionals --> ';
8132  $out .= "\n";
8133 
8134  $nbofextrafieldsshown = 0;
8135  $e = 0; // var to manage the modulo (odd/even)
8136 
8137  $lastseparatorkeyfound = '';
8138  $extrafields_collapse_num = '';
8139  $extrafields_collapse_num_old = '';
8140  $i = 0;
8141 
8142  foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $label) {
8143  $i++;
8144 
8145  // Show only the key field in params
8146  if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) {
8147  continue;
8148  }
8149 
8150  // Test on 'enabled' ('enabled' is different than 'list' = 'visibility')
8151  $enabled = 1;
8152  if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
8153  $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
8154  }
8155  if (empty($enabled)) {
8156  continue;
8157  }
8158 
8159  $visibility = 1;
8160  if ($visibility && isset($extrafields->attributes[$this->table_element]['list'][$key])) {
8161  $visibility = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
8162  }
8163 
8164  $perms = 1;
8165  if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
8166  $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
8167  }
8168 
8169  if (($mode == 'create') && abs($visibility) != 1 && abs($visibility) != 3) {
8170  continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list
8171  } elseif (($mode == 'edit') && abs($visibility) != 1 && abs($visibility) != 3 && abs($visibility) != 4) {
8172  continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list and <> 4 = not visible at the creation
8173  } elseif ($mode == 'view' && empty($visibility)) {
8174  continue;
8175  }
8176  if (empty($perms)) {
8177  continue;
8178  }
8179 
8180  // Load language if required
8181  if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
8182  $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
8183  }
8184 
8185  $colspan = 0;
8186  if (is_array($params) && count($params) > 0 && $display_type=='card') {
8187  if (array_key_exists('cols', $params)) {
8188  $colspan = $params['cols'];
8189  } elseif (array_key_exists('colspan', $params)) { // For backward compatibility. Use cols instead now.
8190  $reg = array();
8191  if (preg_match('/colspan="(\d+)"/', $params['colspan'], $reg)) {
8192  $colspan = $reg[1];
8193  } else {
8194  $colspan = $params['colspan'];
8195  }
8196  }
8197  }
8198  $colspan = intval($colspan);
8199 
8200  switch ($mode) {
8201  case "view":
8202  $value = ((!empty($this->array_options) && array_key_exists("options_".$key.$keysuffix, $this->array_options)) ? $this->array_options["options_".$key.$keysuffix] : null); // Value may be cleaned or formated later
8203  break;
8204  case "create":
8205  case "edit":
8206  // We get the value of property found with GETPOST so it takes into account:
8207  // default values overwrite, restore back to list link, ... (but not 'default value in database' of field)
8208  $check = 'alphanohtml';
8209  if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text'))) {
8210  $check = 'restricthtml';
8211  }
8212  $getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, $check, 3); // GETPOST can get value from GET, POST or setup of default values overwrite.
8213  // GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
8214  if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)) {
8215  if (is_array($getposttemp)) {
8216  // $getposttemp is an array but following code expects a comma separated string
8217  $value = implode(",", $getposttemp);
8218  } else {
8219  $value = $getposttemp;
8220  }
8221  } else {
8222  $value = (!empty($this->array_options["options_".$key]) ? $this->array_options["options_".$key] : ''); // No GET, no POST, no default value, so we take value of object.
8223  }
8224  //var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
8225  break;
8226  }
8227 
8228  $nbofextrafieldsshown++;
8229 
8230  // Output value of the current field
8231  if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
8232  $extrafields_collapse_num = $key;
8233  /*
8234  $extrafield_param = $extrafields->attributes[$this->table_element]['param'][$key];
8235  if (!empty($extrafield_param) && is_array($extrafield_param)) {
8236  $extrafield_param_list = array_keys($extrafield_param['options']);
8237 
8238  if (count($extrafield_param_list) > 0) {
8239  $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
8240 
8241  if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
8242  //$extrafields_collapse_num = $extrafields->attributes[$this->table_element]['pos'][$key];
8243  $extrafields_collapse_num = $key;
8244  }
8245  }
8246  }
8247  */
8248 
8249  // if colspan=0 or 1, the second column is not extended, so the separator must be on 2 columns
8250  $out .= $extrafields->showSeparator($key, $this, ($colspan ? $colspan + 1 : 2), $display_type, $mode);
8251 
8252  $lastseparatorkeyfound = $key;
8253  } else {
8254  $collapse_group = $extrafields_collapse_num.(!empty($this->id) ? '_'.$this->id : '');
8255 
8256  $class = (!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
8257  $csstyle = '';
8258  if (is_array($params) && count($params) > 0) {
8259  if (array_key_exists('class', $params)) {
8260  $class .= $params['class'].' ';
8261  }
8262  if (array_key_exists('style', $params)) {
8263  $csstyle = $params['style'];
8264  }
8265  }
8266 
8267  // add html5 elements
8268  $domData = ' data-element="extrafield"';
8269  $domData .= ' data-targetelement="'.$this->element.'"';
8270  $domData .= ' data-targetid="'.$this->id.'"';
8271 
8272  $html_id = (empty($this->id) ? '' : 'extrarow-'.$this->element.'_'.$key.'_'.$this->id);
8273  if ($display_type=='card') {
8274  if (!empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0) {
8275  $colspan = 0;
8276  }
8277 
8278  if ($action == 'selectlines') {
8279  $colspan++;
8280  }
8281  }
8282 
8283  // Convert date into timestamp format (value in memory must be a timestamp)
8284  if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date'))) {
8285  $datenotinstring = null;
8286  if (array_key_exists('options_'.$key, $this->array_options)) {
8287  $datenotinstring = $this->array_options['options_'.$key];
8288  if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
8289  $datenotinstring = $this->db->jdate($datenotinstring);
8290  }
8291  }
8292  $datekey = $keyprefix.'options_'.$key.$keysuffix;
8293  $value = (GETPOSTISSET($datekey)) ? dol_mktime(12, 0, 0, GETPOST($datekey.'month', 'int', 3), GETPOST($datekey.'day', 'int', 3), GETPOST($datekey.'year', 'int', 3)) : $datenotinstring;
8294  }
8295  if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('datetime'))) {
8296  $datenotinstring = null;
8297  if (array_key_exists('options_'.$key, $this->array_options)) {
8298  $datenotinstring = $this->array_options['options_'.$key];
8299  if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
8300  $datenotinstring = $this->db->jdate($datenotinstring);
8301  }
8302  }
8303  $timekey = $keyprefix.'options_'.$key.$keysuffix;
8304  $value = (GETPOSTISSET($timekey)) ? dol_mktime(GETPOST($timekey.'hour', 'int', 3), GETPOST($timekey.'min', 'int', 3), GETPOST($timekey.'sec', 'int', 3), GETPOST($timekey.'month', 'int', 3), GETPOST($timekey.'day', 'int', 3), GETPOST($timekey.'year', 'int', 3), 'tzuserrel') : $datenotinstring;
8305  }
8306  // Convert float submited string into real php numeric (value in memory must be a php numeric)
8307  if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price', 'double'))) {
8308  if (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) {
8309  $value = price2num($value);
8310  } elseif (isset($this->array_options['options_'.$key])) {
8311  $value = $this->array_options['options_'.$key];
8312  }
8313  }
8314 
8315  // HTML, text, select, integer and varchar: take into account default value in database if in create mode
8316  if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text', 'varchar', 'select', 'int', 'boolean'))) {
8317  if ($action == 'create') {
8318  $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) ? $value : $extrafields->attributes[$this->table_element]['default'][$key];
8319  }
8320  }
8321 
8322  $labeltoshow = $langs->trans($label);
8323  $helptoshow = $langs->trans($extrafields->attributes[$this->table_element]['help'][$key]);
8324 
8325  if ($display_type == 'card') {
8326  $out .= '<tr '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="field_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
8327  if (!empty($conf->global->MAIN_VIEW_LINE_NUMBER) && ($action == 'view' || $action == 'valid' || $action == 'editline' || $action == 'confirm_valid' || $action == 'confirm_cancel')) {
8328  $out .= '<td></td>';
8329  }
8330  $out .= '<td class="'.(empty($params['tdclass']) ? 'titlefieldcreate' : $params['tdclass']).' wordbreak';
8331  } elseif ($display_type == 'line') {
8332  $out .= '<div '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="fieldline_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
8333  $out .= '<div style="display: inline-block; padding-right:4px" class="wordbreak';
8334  }
8335  //$out .= "titlefield";
8336  //if (GETPOST('action', 'restricthtml') == 'create') $out.='create';
8337  // BUG #11554 : For public page, use red dot for required fields, instead of bold label
8338  $tpl_context = isset($params["tpl_context"]) ? $params["tpl_context"] : "none";
8339  if ($tpl_context != "public") { // Public page : red dot instead of fieldrequired characters
8340  if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
8341  $out .= ' fieldrequired';
8342  }
8343  }
8344  $out .= '">';
8345  if ($tpl_context == "public") { // Public page : red dot instead of fieldrequired characters
8346  if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
8347  $out .= $form->textwithpicto($labeltoshow, $helptoshow);
8348  } else {
8349  $out .= $labeltoshow;
8350  }
8351  if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
8352  $out .= '&nbsp;<span style="color: red">*</span>';
8353  }
8354  } else {
8355  if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
8356  $out .= $form->textwithpicto($labeltoshow, $helptoshow);
8357  } else {
8358  $out .= $labeltoshow;
8359  }
8360  }
8361 
8362  $out .= ($display_type == 'card' ? '</td>' : '</div>');
8363 
8364  $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
8365  if ($display_type == 'card') {
8366  // a first td column was already output (and may be another on before if MAIN_VIEW_LINE_NUMBER set), so this td is the next one
8367  $out .= '<td '.($html_id ? 'id="'.$html_id.'" ' : '').' class="valuefieldcreate '.$this->element.'_extras_'.$key.'" '.($colspan ? ' colspan="'.$colspan.'"' : '').'>';
8368  } elseif ($display_type == 'line') {
8369  $out .= '<div '.($html_id ? 'id="'.$html_id.'" ' : '').' style="display: inline-block" class="valuefieldcreate '.$this->element.'_extras_'.$key.' extra_inline_'.$extrafields->attributes[$this->table_element]['type'][$key].'">';
8370  }
8371 
8372  switch ($mode) {
8373  case "view":
8374  $out .= $extrafields->showOutputField($key, $value, '', $this->table_element);
8375  break;
8376  case "create":
8377  $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
8378  break;
8379  case "edit":
8380  $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
8381  break;
8382  }
8383 
8384  $out .= ($display_type=='card' ? '</td>' : '</div>');
8385 
8386  if (!empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) {
8387  $out .= ($display_type=='card' ? '</tr>' : '</div>');
8388  } else {
8389  $out .= ($display_type=='card' ? '</tr>' : '</div>');
8390  }
8391 
8392  $e++;
8393  }
8394  }
8395  $out .= "\n";
8396  // Add code to manage list depending on others
8397  if (!empty($conf->use_javascript_ajax)) {
8398  $out .= $this->getJSListDependancies();
8399  }
8400 
8401  $out .= '<!-- commonobject:showOptionals end --> '."\n";
8402 
8403  if (empty($nbofextrafieldsshown)) {
8404  $out = '';
8405  }
8406  }
8407  }
8408 
8409  $out .= $hookmanager->resPrint;
8410 
8411  return $out;
8412  }
8413 
8418  public function getJSListDependancies($type = '_extra')
8419  {
8420  $out = '
8421  <script nonce="'.getNonce().'">
8422  jQuery(document).ready(function() {
8423  function showOptions'.$type.'(child_list, parent_list, orig_select)
8424  {
8425  var val = $("select[name=\""+parent_list+"\"]").val();
8426  var parentVal = parent_list + ":" + val;
8427  if(typeof val == "string"){
8428  if(val != "") {
8429  var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
8430  $("select[name=\""+child_list+"\"] option[parent]").remove();
8431  $("select[name=\""+child_list+"\"]").append(options);
8432  } else {
8433  var options = orig_select.find("option[parent]").clone();
8434  $("select[name=\""+child_list+"\"] option[parent]").remove();
8435  $("select[name=\""+child_list+"\"]").append(options);
8436  }
8437  } else if(val > 0) {
8438  var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
8439  $("select[name=\""+child_list+"\"] option[parent]").remove();
8440  $("select[name=\""+child_list+"\"]").append(options);
8441  } else {
8442  var options = orig_select.find("option[parent]").clone();
8443  $("select[name=\""+child_list+"\"] option[parent]").remove();
8444  $("select[name=\""+child_list+"\"]").append(options);
8445  }
8446  }
8447  function setListDependencies'.$type.'() {
8448  jQuery("select option[parent]").parent().each(function() {
8449  var orig_select = {};
8450  var child_list = $(this).attr("name");
8451  orig_select[child_list] = $(this).clone();
8452  var parent = $(this).find("option[parent]:first").attr("parent");
8453  var infos = parent.split(":");
8454  var parent_list = infos[0];
8455 
8456  //Hide daughters lists
8457  if ($("#"+child_list).val() == 0 && $("#"+parent_list).val() == 0){
8458  $("#"+child_list).hide();
8459  //Show mother lists
8460  } else if ($("#"+parent_list).val() != 0){
8461  $("#"+parent_list).show();
8462  }
8463  //Show the child list if the parent list value is selected
8464  $("select[name=\""+parent_list+"\"]").click(function() {
8465  if ($(this).val() != 0){
8466  $("#"+child_list).show()
8467  }
8468  });
8469 
8470  //When we change parent list
8471  $("select[name=\""+parent_list+"\"]").change(function() {
8472  showOptions'.$type.'(child_list, parent_list, orig_select[child_list]);
8473  //Select the value 0 on child list after a change on the parent list
8474  $("#"+child_list).val(0).trigger("change");
8475  //Hide child lists if the parent value is set to 0
8476  if ($(this).val() == 0){
8477  $("#"+child_list).hide();
8478  }
8479  });
8480  });
8481  }
8482 
8483  setListDependencies'.$type.'();
8484  });
8485  </script>'."\n";
8486  return $out;
8487  }
8488 
8493  public function getRights()
8494  {
8495  global $user;
8496 
8497  $module = $this->module;
8498  $element = $this->element;
8499 
8500  if ($element == 'facturerec') {
8501  $element = 'facture';
8502  } elseif ($element == 'invoice_supplier_rec') {
8503  return $user->rights->fournisseur->facture;
8504  } elseif ($module && !empty($user->rights->$module->$element)) {
8505  // for modules built with ModuleBuilder
8506  return $user->rights->$module->$element;
8507  }
8508 
8509  return $user->rights->$element;
8510  }
8511 
8524  public static function commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
8525  {
8526  foreach ($tables as $table) {
8527  $sql = 'UPDATE '.$dbs->prefix().$table.' SET fk_soc = '.((int) $dest_id).' WHERE fk_soc = '.((int) $origin_id);
8528 
8529  if (!$dbs->query($sql)) {
8530  if ($ignoreerrors) {
8531  return true; // TODO Not enough. If there is A-B on kept thirdparty and B-C on old one, we must get A-B-C after merge. Not A-B.
8532  }
8533  //$this->errors = $db->lasterror();
8534  return false;
8535  }
8536  }
8537 
8538  return true;
8539  }
8540 
8553  public static function commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
8554  {
8555  foreach ($tables as $table) {
8556  $sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_product = '.((int) $dest_id).' WHERE fk_product = '.((int) $origin_id);
8557 
8558  if (!$dbs->query($sql)) {
8559  if ($ignoreerrors) {
8560  return true; // TODO Not enough. If there is A-B on kept product and B-C on old one, we must get A-B-C after merge. Not A-B.
8561  }
8562  //$this->errors = $db->lasterror();
8563  return false;
8564  }
8565  }
8566 
8567  return true;
8568  }
8569 
8582  public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
8583  {
8584  global $conf;
8585 
8586  $buyPrice = 0;
8587 
8588  if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull > 0)) {
8589  // When ForceBuyingPriceIfNull is set
8590  $buyPrice = $unitPrice * (1 - $discountPercent / 100);
8591  } else {
8592  // Get cost price for margin calculation
8593  if (!empty($fk_product) && $fk_product > 0) {
8594  if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice') {
8595  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
8596  $product = new Product($this->db);
8597  $result = $product->fetch($fk_product);
8598  if ($result <= 0) {
8599  $this->errors[] = 'ErrorProductIdDoesNotExists';
8600  return -1;
8601  }
8602  if ($product->cost_price > 0) {
8603  $buyPrice = $product->cost_price;
8604  } elseif ($product->pmp > 0) {
8605  $buyPrice = $product->pmp;
8606  }
8607  } elseif (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp') {
8608  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
8609  $product = new Product($this->db);
8610  $result = $product->fetch($fk_product);
8611  if ($result <= 0) {
8612  $this->errors[] = 'ErrorProductIdDoesNotExists';
8613  return -1;
8614  }
8615  if ($product->pmp > 0) {
8616  $buyPrice = $product->pmp;
8617  }
8618  }
8619 
8620  if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1', 'pmp', 'costprice'))) {
8621  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
8622  $productFournisseur = new ProductFournisseur($this->db);
8623  if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0) {
8624  $buyPrice = $productFournisseur->fourn_unitprice;
8625  } elseif ($result < 0) {
8626  $this->errors[] = $productFournisseur->error;
8627  return -2;
8628  }
8629  }
8630  }
8631  }
8632  return $buyPrice;
8633  }
8634 
8635  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8655  public function show_photos($modulepart, $sdir, $size = 0, $nbmax = 0, $nbbyrow = 5, $showfilename = 0, $showaction = 0, $maxHeight = 120, $maxWidth = 160, $nolink = 0, $overwritetitle = 0, $usesharelink = 0, $cache = '', $addphotorefcss = 'photoref')
8656  {
8657  // phpcs:enable
8658  global $conf, $user, $langs;
8659 
8660  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
8661  include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
8662 
8663  $sortfield = 'position_name';
8664  $sortorder = 'asc';
8665 
8666  $dir = $sdir.'/';
8667  $pdir = '/';
8668 
8669  $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
8670  $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
8671 
8672  // For backward compatibility
8673  if ($modulepart == 'product') {
8674  if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
8675  $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
8676  $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
8677  }
8678  }
8679 
8680  // Defined relative dir to DOL_DATA_ROOT
8681  $relativedir = '';
8682  if ($dir) {
8683  $relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
8684  $relativedir = preg_replace('/^[\\/]/', '', $relativedir);
8685  $relativedir = preg_replace('/[\\/]$/', '', $relativedir);
8686  }
8687 
8688  $dirthumb = $dir.'thumbs/';
8689  $pdirthumb = $pdir.'thumbs/';
8690 
8691  $return = '<!-- Photo -->'."\n";
8692  $nbphoto = 0;
8693 
8694  $filearray = dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ?SORT_DESC:SORT_ASC), 1);
8695 
8696  /*if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) // For backward compatiblity, we scan also old dirs
8697  {
8698  $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
8699  $filearray=array_merge($filearray, $filearrayold);
8700  }*/
8701 
8702  completeFileArrayWithDatabaseInfo($filearray, $relativedir);
8703 
8704  if (count($filearray)) {
8705  if ($sortfield && $sortorder) {
8706  $filearray = dol_sort_array($filearray, $sortfield, $sortorder);
8707  }
8708 
8709  foreach ($filearray as $key => $val) {
8710  $photo = '';
8711  $file = $val['name'];
8712 
8713  //if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
8714  if (image_format_supported($file) >= 0) {
8715  $nbphoto++;
8716  $photo = $file;
8717  $viewfilename = $file;
8718 
8719  if ($size == 1 || $size == 'small') { // Format vignette
8720  // Find name of thumb file
8721  $photo_vignette = basename(getImageFileNameForSize($dir.$file, '_small'));
8722  if (!dol_is_file($dirthumb.$photo_vignette)) {
8723  // The thumb does not exists, so we will use the original file
8724  $dirthumb = $dir;
8725  $pdirthumb = $pdir;
8726  $photo_vignette = basename($file);
8727  }
8728 
8729  // Get filesize of original file
8730  $imgarray = dol_getImageSize($dir.$photo);
8731 
8732  if ($nbbyrow > 0) {
8733  if ($nbphoto == 1) {
8734  $return .= '<table class="valigntop center centpercent" style="border: 0; padding: 2px; border-spacing: 2px; border-collapse: separate;">';
8735  }
8736 
8737  if ($nbphoto % $nbbyrow == 1) {
8738  $return .= '<tr class="center valignmiddle" style="border: 1px">';
8739  }
8740  $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%" class="photo">'."\n";
8741  } elseif ($nbbyrow < 0) {
8742  $return .= '<div class="inline-block">'."\n";
8743  }
8744 
8745  $relativefile = preg_replace('/^\//', '', $pdir.$photo);
8746  if (empty($nolink)) {
8747  $urladvanced = getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
8748  if ($urladvanced) {
8749  $return .= '<a href="'.$urladvanced.'">';
8750  } else {
8751  $return .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank" rel="noopener noreferrer">';
8752  }
8753  }
8754 
8755  // Show image (width height=$maxHeight)
8756  // Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
8757  $alt = $langs->transnoentitiesnoconv('File').': '.$relativefile;
8758  $alt .= ' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
8759  if ($overwritetitle) {
8760  if (is_numeric($overwritetitle)) {
8761  $alt = '';
8762  } else {
8763  $alt = $overwritetitle;
8764  }
8765  }
8766 
8767  if ($usesharelink) {
8768  if ($val['share']) {
8769  if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
8770  $return .= '<!-- Show original file (thumb not yet available with shared links) -->';
8771  $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'"'.($maxHeight ?' height="'.$maxHeight.'"': '').' src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).($cache ? '&cache='.urlencode($cache) : '').'" title="'.dol_escape_htmltag($alt).'">';
8772  } else {
8773  $return .= '<!-- Show original file -->';
8774  $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).($cache ? '&cache='.urlencode($cache) : '').'" title="'.dol_escape_htmltag($alt).'">';
8775  }
8776  } else {
8777  $return .= '<!-- Show nophoto file (because file is not shared) -->';
8778  $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
8779  }
8780  } else {
8781  if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
8782  $return .= '<!-- Show thumb -->';
8783  $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').' maxwidth150onsmartphone maxwidth200"'.($maxHeight ?' height="'.$maxHeight.'"': '').' src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.($cache ? '&cache='.urlencode($cache) : '').'&file='.urlencode($pdirthumb.$photo_vignette).'" title="'.dol_escape_htmltag($alt).'">';
8784  } else {
8785  $return .= '<!-- Show original file -->';
8786  $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.($cache ? '&cache='.urlencode($cache) : '').'&file='.urlencode($pdir.$photo).'" title="'.dol_escape_htmltag($alt).'">';
8787  }
8788  }
8789 
8790  if (empty($nolink)) {
8791  $return .= '</a>';
8792  }
8793 
8794  if ($showfilename) {
8795  $return .= '<br>'.$viewfilename;
8796  }
8797  if ($showaction) {
8798  $return .= '<br>';
8799  // On propose la generation de la vignette si elle n'existe pas et si la taille est superieure aux limites
8800  if ($photo_vignette && (image_format_supported($photo) > 0) && ($this->imgWidth > $maxWidth || $this->imgHeight > $maxHeight)) {
8801  $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=addthumb&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">'.img_picto($langs->trans('GenerateThumb'), 'refresh').'&nbsp;&nbsp;</a>';
8802  }
8803  // Special cas for product
8804  if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
8805  // Link to resize
8806  $return .= '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?modulepart='.urlencode('produit|service').'&id='.$this->id.'&file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"), 'resize', '').'</a> &nbsp; ';
8807 
8808  // Link to delete
8809  $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
8810  $return .= img_delete().'</a>';
8811  }
8812  }
8813  $return .= "\n";
8814 
8815  if ($nbbyrow > 0) {
8816  $return .= '</td>';
8817  if (($nbphoto % $nbbyrow) == 0) {
8818  $return .= '</tr>';
8819  }
8820  } elseif ($nbbyrow < 0) {
8821  $return .= '</div>'."\n";
8822  }
8823  }
8824 
8825  if (empty($size)) { // Format origine
8826  $return .= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
8827 
8828  if ($showfilename) {
8829  $return .= '<br>'.$viewfilename;
8830  }
8831  if ($showaction) {
8832  // Special case for product
8833  if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
8834  // Link to resize
8835  $return .= '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?modulepart='.urlencode('produit|service').'&id='.$this->id.'&file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"), 'resize', '').'</a> &nbsp; ';
8836 
8837  // Link to delete
8838  $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
8839  $return .= img_delete().'</a>';
8840  }
8841  }
8842  }
8843 
8844  // On continue ou on arrete de boucler ?
8845  if ($nbmax && $nbphoto >= $nbmax) {
8846  break;
8847  }
8848  }
8849  }
8850 
8851  if ($size == 1 || $size == 'small') {
8852  if ($nbbyrow > 0) {
8853  // Ferme tableau
8854  while ($nbphoto % $nbbyrow) {
8855  $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%">&nbsp;</td>';
8856  $nbphoto++;
8857  }
8858 
8859  if ($nbphoto) {
8860  $return .= '</table>';
8861  }
8862  }
8863  }
8864  }
8865 
8866  $this->nbphoto = $nbphoto;
8867 
8868  return $return;
8869  }
8870 
8871 
8878  protected function isArray($info)
8879  {
8880  if (is_array($info)) {
8881  if (isset($info['type']) && $info['type'] == 'array') {
8882  return true;
8883  } else {
8884  return false;
8885  }
8886  }
8887  return false;
8888  }
8889 
8896  public function isDate($info)
8897  {
8898  if (isset($info['type']) && ($info['type'] == 'date' || $info['type'] == 'datetime' || $info['type'] == 'timestamp')) {
8899  return true;
8900  }
8901  return false;
8902  }
8903 
8910  public function isDuration($info)
8911  {
8912  if (is_array($info)) {
8913  if (isset($info['type']) && ($info['type'] == 'duration')) {
8914  return true;
8915  } else {
8916  return false;
8917  }
8918  } else {
8919  return false;
8920  }
8921  }
8922 
8929  public function isInt($info)
8930  {
8931  if (is_array($info)) {
8932  if (isset($info['type']) && (preg_match('/(^int|int$)/i', $info['type']))) {
8933  return true;
8934  } else {
8935  return false;
8936  }
8937  } else {
8938  return false;
8939  }
8940  }
8941 
8948  public function isFloat($info)
8949  {
8950  if (is_array($info)) {
8951  if (isset($info['type']) && (preg_match('/^(double|real|price)/i', $info['type']))) {
8952  return true;
8953  } else {
8954  return false;
8955  }
8956  }
8957  return false;
8958  }
8959 
8966  public function isText($info)
8967  {
8968  if (is_array($info)) {
8969  if (isset($info['type']) && $info['type'] == 'text') {
8970  return true;
8971  } else {
8972  return false;
8973  }
8974  }
8975  return false;
8976  }
8977 
8984  protected function canBeNull($info)
8985  {
8986  if (is_array($info)) {
8987  if (isset($info['notnull']) && $info['notnull'] != '1') {
8988  return true;
8989  } else {
8990  return false;
8991  }
8992  }
8993  return true;
8994  }
8995 
9002  protected function isForcedToNullIfZero($info)
9003  {
9004  if (is_array($info)) {
9005  if (isset($info['notnull']) && $info['notnull'] == '-1') {
9006  return true;
9007  } else {
9008  return false;
9009  }
9010  }
9011  return false;
9012  }
9013 
9020  protected function isIndex($info)
9021  {
9022  if (is_array($info)) {
9023  if (isset($info['index']) && $info['index'] == true) {
9024  return true;
9025  } else {
9026  return false;
9027  }
9028  }
9029  return false;
9030  }
9031 
9032 
9041  protected function setSaveQuery()
9042  {
9043  global $conf;
9044 
9045  $queryarray = array();
9046  foreach ($this->fields as $field => $info) { // Loop on definition of fields
9047  // Depending on field type ('datetime', ...)
9048  if ($this->isDate($info)) {
9049  if (empty($this->{$field})) {
9050  $queryarray[$field] = null;
9051  } else {
9052  $queryarray[$field] = $this->db->idate($this->{$field});
9053  }
9054  } elseif ($this->isDuration($info)) {
9055  // $this->{$field} may be null, '', 0, '0', 123, '123'
9056  if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
9057  if (!isset($this->{$field})) {
9058  if (!empty($info['default'])) {
9059  $queryarray[$field] = $info['default'];
9060  } else {
9061  $queryarray[$field] = 0;
9062  }
9063  } else {
9064  $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9065  }
9066  } else {
9067  $queryarray[$field] = null;
9068  }
9069  } elseif ($this->isInt($info) || $this->isFloat($info)) {
9070  if ($field == 'entity' && is_null($this->{$field})) {
9071  $queryarray[$field] = ((int) $conf->entity);
9072  } else {
9073  // $this->{$field} may be null, '', 0, '0', 123, '123'
9074  if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
9075  if (!isset($this->{$field})) {
9076  $queryarray[$field] = 0;
9077  } elseif ($this->isInt($info)) {
9078  $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9079  } elseif ($this->isFloat($info)) {
9080  $queryarray[$field] = (double) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9081  }
9082  } else {
9083  $queryarray[$field] = null;
9084  }
9085  }
9086  } else {
9087  // Note: If $this->{$field} is not defined, it means there is a bug into definition of ->fields or a missing declaration of property
9088  // We should keep the warning generated by this because it is a bug somewhere else in code, not here.
9089  $queryarray[$field] = $this->{$field};
9090  }
9091 
9092  if ($info['type'] == 'timestamp' && empty($queryarray[$field])) {
9093  unset($queryarray[$field]);
9094  }
9095  if (!empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) {
9096  $queryarray[$field] = null; // May force 0 to null
9097  }
9098  }
9099 
9100  return $queryarray;
9101  }
9102 
9109  public function setVarsFromFetchObj(&$obj)
9110  {
9111  global $db;
9112 
9113  foreach ($this->fields as $field => $info) {
9114  if ($this->isDate($info)) {
9115  if (is_null($obj->$field) || $obj->$field === '' || $obj->$field === '0000-00-00 00:00:00' || $obj->$field === '1000-01-01 00:00:00') {
9116  $this->$field = '';
9117  } else {
9118  $this->$field = $db->jdate($obj->$field);
9119  }
9120  } elseif ($this->isInt($info)) {
9121  if ($field == 'rowid') {
9122  $this->id = (int) $obj->$field;
9123  } else {
9124  if ($this->isForcedToNullIfZero($info)) {
9125  if (empty($obj->$field)) {
9126  $this->$field = null;
9127  } else {
9128  $this->$field = (double) $obj->$field;
9129  }
9130  } else {
9131  if (!is_null($obj->$field) || (isset($info['notnull']) && $info['notnull'] == 1)) {
9132  $this->$field = (int) $obj->$field;
9133  } else {
9134  $this->$field = null;
9135  }
9136  }
9137  }
9138  } elseif ($this->isFloat($info)) {
9139  if ($this->isForcedToNullIfZero($info)) {
9140  if (empty($obj->$field)) {
9141  $this->$field = null;
9142  } else {
9143  $this->$field = (double) $obj->$field;
9144  }
9145  } else {
9146  if (!is_null($obj->$field) || (isset($info['notnull']) && $info['notnull'] == 1)) {
9147  $this->$field = (double) $obj->$field;
9148  } else {
9149  $this->$field = null;
9150  }
9151  }
9152  } else {
9153  $this->$field = isset($obj->$field) ? $obj->$field : null;
9154  }
9155  }
9156 
9157  // If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
9158  if (!isset($this->fields['ref']) && isset($this->id)) {
9159  $this->ref = $this->id;
9160  }
9161  }
9162 
9167  public function emtpyObjectVars()
9168  {
9169  foreach ($this->fields as $field => $arr) {
9170  $this->$field = null;
9171  }
9172  }
9173 
9180  public function getFieldList($alias = '')
9181  {
9182  $keys = array_keys($this->fields);
9183  if (!empty($alias)) {
9184  $keys_with_alias = array();
9185  foreach ($keys as $fieldname) {
9186  $keys_with_alias[] = $alias . '.' . $fieldname;
9187  }
9188  return implode(',', $keys_with_alias);
9189  } else {
9190  return implode(',', $keys);
9191  }
9192  }
9193 
9201  protected function quote($value, $fieldsentry)
9202  {
9203  if (is_null($value)) {
9204  return 'NULL';
9205  } elseif (preg_match('/^(int|double|real|price)/i', $fieldsentry['type'])) {
9206  return price2num("$value");
9207  } elseif (preg_match('/int$/i', $fieldsentry['type'])) {
9208  return (int) $value;
9209  } elseif ($fieldsentry['type'] == 'boolean') {
9210  if ($value) {
9211  return 'true';
9212  } else {
9213  return 'false';
9214  }
9215  } else {
9216  return "'".$this->db->escape($value)."'";
9217  }
9218  }
9219 
9220 
9228  public function createCommon(User $user, $notrigger = false)
9229  {
9230  global $langs;
9231  dol_syslog(get_class($this)."::createCommon create", LOG_DEBUG);
9232 
9233  $error = 0;
9234 
9235  $now = dol_now();
9236 
9237  $fieldvalues = $this->setSaveQuery();
9238 
9239  if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) {
9240  $fieldvalues['date_creation'] = $this->db->idate($now);
9241  }
9242  if (array_key_exists('fk_user_creat', $fieldvalues) && !($fieldvalues['fk_user_creat'] > 0)) {
9243  $fieldvalues['fk_user_creat'] = $user->id;
9244  }
9245  unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
9246  if (array_key_exists('ref', $fieldvalues)) {
9247  $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
9248  }
9249 
9250  $keys = array();
9251  $values = array(); // Array to store string forged for SQL syntax
9252  foreach ($fieldvalues as $k => $v) {
9253  $keys[$k] = $k;
9254  $value = $this->fields[$k];
9255  $values[$k] = $this->quote($v, $value); // May return string 'NULL' if $value is null
9256  }
9257 
9258  // Clean and check mandatory
9259  foreach ($keys as $key) {
9260  // If field is an implicit foreign key field (so type = 'integer:...')
9261  if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
9262  $values[$key] = '';
9263  }
9264  if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
9265  $values[$key] = '';
9266  }
9267 
9268  if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && is_null($this->fields[$key]['default'])) {
9269  $error++;
9270  $langs->load("errors");
9271  dol_syslog("Mandatory field '".$key."' is empty and required into ->fields definition of class");
9272  $this->errors[] = $langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
9273  }
9274 
9275  // If value is null and there is a default value for field
9276  if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && !is_null($this->fields[$key]['default'])) {
9277  $values[$key] = $this->quote($this->fields[$key]['default'], $this->fields[$key]);
9278  }
9279 
9280  // If field is an implicit foreign key field (so type = 'integer:...')
9281  if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) {
9282  if (isset($this->fields[$key]['default'])) {
9283  $values[$key] = ((int) $this->fields[$key]['default']);
9284  } else {
9285  $values[$key] = 'null';
9286  }
9287  }
9288  if (!empty($this->fields[$key]['foreignkey']) && empty($values[$key])) {
9289  $values[$key] = 'null';
9290  }
9291  }
9292 
9293  if ($error) {
9294  return -1;
9295  }
9296 
9297  $this->db->begin();
9298 
9299  if (!$error) {
9300  $sql = "INSERT INTO ".$this->db->prefix().$this->table_element;
9301  $sql .= " (".implode(", ", $keys).')';
9302  $sql .= " VALUES (".implode(", ", $values).")"; // $values can contains 'abc' or 123
9303 
9304  $res = $this->db->query($sql);
9305  if (!$res) {
9306  $error++;
9307  if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
9308  $this->errors[] = "ErrorRefAlreadyExists";
9309  } else {
9310  $this->errors[] = $this->db->lasterror();
9311  }
9312  }
9313  }
9314 
9315  if (!$error) {
9316  $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
9317  }
9318 
9319  // If we have a field ref with a default value of (PROV)
9320  if (!$error) {
9321  if (key_exists('ref', $this->fields) && $this->fields['ref']['notnull'] > 0 && key_exists('default', $this->fields['ref']) && $this->fields['ref']['default'] == '(PROV)') {
9322  $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ref = '(PROV".((int) $this->id).")' WHERE (ref = '(PROV)' OR ref = '') AND rowid = ".((int) $this->id);
9323  $resqlupdate = $this->db->query($sql);
9324 
9325  if ($resqlupdate === false) {
9326  $error++;
9327  $this->errors[] = $this->db->lasterror();
9328  } else {
9329  $this->ref = '(PROV'.$this->id.')';
9330  }
9331  }
9332  }
9333 
9334  // Create extrafields
9335  if (!$error) {
9336  $result = $this->insertExtraFields();
9337  if ($result < 0) {
9338  $error++;
9339  }
9340  }
9341 
9342  // Create lines
9343  if (!empty($this->table_element_line) && !empty($this->fk_element)) {
9344  $num = (is_array($this->lines) ? count($this->lines) : 0);
9345  for ($i = 0; $i < $num; $i++) {
9346  $line = $this->lines[$i];
9347 
9348  $keyforparent = $this->fk_element;
9349  $line->$keyforparent = $this->id;
9350 
9351  // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
9352  //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
9353  if (!is_object($line)) {
9354  $line = (object) $line;
9355  }
9356 
9357  $result = $line->create($user, 1);
9358  if ($result < 0) {
9359  $this->error = $line->error;
9360  $this->db->rollback();
9361  return -1;
9362  }
9363  }
9364  }
9365 
9366  // Triggers
9367  if (!$error && !$notrigger) {
9368  // Call triggers
9369  $result = $this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user);
9370  if ($result < 0) {
9371  $error++;
9372  }
9373  // End call triggers
9374  }
9375 
9376  // Commit or rollback
9377  if ($error) {
9378  $this->db->rollback();
9379  return -1;
9380  } else {
9381  $this->db->commit();
9382  return $this->id;
9383  }
9384  }
9385 
9386 
9395  public function fetchCommon($id, $ref = null, $morewhere = '')
9396  {
9397  if (empty($id) && empty($ref) && empty($morewhere)) {
9398  return -1;
9399  }
9400 
9401  $fieldlist = $this->getFieldList('t');
9402  if (empty($fieldlist)) {
9403  return 0;
9404  }
9405 
9406  $sql = "SELECT ".$fieldlist;
9407  $sql .= " FROM ".$this->db->prefix().$this->table_element.' as t';
9408 
9409  if (!empty($id)) {
9410  $sql .= ' WHERE t.rowid = '.((int) $id);
9411  } elseif (!empty($ref)) {
9412  $sql .= " WHERE t.ref = '".$this->db->escape($ref)."'";
9413  } else {
9414  $sql .= ' WHERE 1 = 1'; // usage with empty id and empty ref is very rare
9415  }
9416  if (empty($id) && isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
9417  $sql .= ' AND t.entity IN ('.getEntity($this->element).')';
9418  }
9419  if ($morewhere) {
9420  $sql .= $morewhere;
9421  }
9422  $sql .= ' LIMIT 1'; // This is a fetch, to be sure to get only one record
9423 
9424  $res = $this->db->query($sql);
9425  if ($res) {
9426  $obj = $this->db->fetch_object($res);
9427  if ($obj) {
9428  $this->setVarsFromFetchObj($obj);
9429 
9430  // Retrieve all extrafield
9431  // fetch optionals attributes and labels
9432  $this->fetch_optionals();
9433 
9434  return $this->id;
9435  } else {
9436  return 0;
9437  }
9438  } else {
9439  $this->error = $this->db->lasterror();
9440  $this->errors[] = $this->error;
9441  return -1;
9442  }
9443  }
9444 
9451  public function fetchLinesCommon($morewhere = '')
9452  {
9453  $objectlineclassname = get_class($this).'Line';
9454  if (!class_exists($objectlineclassname)) {
9455  $this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
9456  return -1;
9457  }
9458 
9459  $objectline = new $objectlineclassname($this->db);
9460 
9461  $sql = "SELECT ".$objectline->getFieldList('l');
9462  $sql .= " FROM ".$this->db->prefix().$objectline->table_element." as l";
9463  $sql .= " WHERE l.fk_".$this->db->escape($this->element)." = ".((int) $this->id);
9464  if ($morewhere) {
9465  $sql .= $morewhere;
9466  }
9467  if (isset($objectline->fields['position'])) {
9468  $sql .= $this->db->order('position', 'ASC');
9469  }
9470 
9471  $resql = $this->db->query($sql);
9472  if ($resql) {
9473  $num_rows = $this->db->num_rows($resql);
9474  $i = 0;
9475  while ($i < $num_rows) {
9476  $obj = $this->db->fetch_object($resql);
9477  if ($obj) {
9478  $newline = new $objectlineclassname($this->db);
9479  $newline->setVarsFromFetchObj($obj);
9480 
9481  $this->lines[] = $newline;
9482  }
9483  $i++;
9484  }
9485 
9486  return 1;
9487  } else {
9488  $this->error = $this->db->lasterror();
9489  $this->errors[] = $this->error;
9490  return -1;
9491  }
9492  }
9493 
9501  public function updateCommon(User $user, $notrigger = false)
9502  {
9503  global $conf, $langs;
9504  dol_syslog(get_class($this)."::updateCommon update", LOG_DEBUG);
9505 
9506  $error = 0;
9507 
9508  $now = dol_now();
9509 
9510  // $this->oldcopy should have been set by the caller of update
9511  //if (empty($this->oldcopy)) {
9512  // $this->oldcopy = dol_clone($this);
9513  //}
9514 
9515  $fieldvalues = $this->setSaveQuery();
9516 
9517  if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) {
9518  $fieldvalues['date_modification'] = $this->db->idate($now);
9519  }
9520  if (array_key_exists('fk_user_modif', $fieldvalues) && !($fieldvalues['fk_user_modif'] > 0)) {
9521  $fieldvalues['fk_user_modif'] = $user->id;
9522  }
9523  unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
9524  if (array_key_exists('ref', $fieldvalues)) {
9525  $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
9526  }
9527 
9528  // Add quotes and escape on fields with type string
9529  $keys = array();
9530  $values = array();
9531  $tmp = array();
9532  foreach ($fieldvalues as $k => $v) {
9533  $keys[$k] = $k;
9534  $value = $this->fields[$k];
9535  $values[$k] = $this->quote($v, $value);
9536  $tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
9537  }
9538 
9539  // Clean and check mandatory fields
9540  foreach ($keys as $key) {
9541  if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
9542  $values[$key] = ''; // This is an implicit foreign key field
9543  }
9544  if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
9545  $values[$key] = ''; // This is an explicit foreign key field
9546  }
9547 
9548  //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
9549  /*
9550  if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
9551  {
9552  $error++;
9553  $this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
9554  }*/
9555  }
9556 
9557  $sql = 'UPDATE '.$this->db->prefix().$this->table_element.' SET '.implode(', ', $tmp).' WHERE rowid='.((int) $this->id);
9558 
9559  $this->db->begin();
9560 
9561  if (!$error) {
9562  $res = $this->db->query($sql);
9563  if (!$res) {
9564  $error++;
9565  $this->errors[] = $this->db->lasterror();
9566  }
9567  }
9568 
9569  // Update extrafield
9570  if (!$error) {
9571  $result = $this->insertExtraFields();
9572  if ($result < 0) {
9573  $error++;
9574  }
9575  }
9576 
9577  // Triggers
9578  if (!$error && !$notrigger) {
9579  // Call triggers
9580  $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
9581  if ($result < 0) {
9582  $error++;
9583  } //Do also here what you must do to rollback action if trigger fail
9584  // End call triggers
9585  }
9586 
9587  // Commit or rollback
9588  if ($error) {
9589  $this->db->rollback();
9590  return -1;
9591  } else {
9592  $this->db->commit();
9593  return $this->id;
9594  }
9595  }
9596 
9605  public function deleteCommon(User $user, $notrigger = false, $forcechilddeletion = 0)
9606  {
9607  dol_syslog(get_class($this)."::deleteCommon delete", LOG_DEBUG);
9608 
9609  $error = 0;
9610 
9611  $this->db->begin();
9612 
9613  if ($forcechilddeletion) { // Force also delete of childtables that should lock deletion in standard case when option force is off
9614  foreach ($this->childtables as $table) {
9615  $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
9616  $resql = $this->db->query($sql);
9617  if (!$resql) {
9618  $this->error = $this->db->lasterror();
9619  $this->errors[] = $this->error;
9620  $this->db->rollback();
9621  return -1;
9622  }
9623  }
9624  } elseif (!empty($this->childtables)) { // If object has childs linked with a foreign key field, we check all child tables.
9625  $objectisused = $this->isObjectUsed($this->id);
9626  if (!empty($objectisused)) {
9627  dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
9628  $this->error = 'ErrorRecordHasChildren';
9629  $this->errors[] = $this->error;
9630  $this->db->rollback();
9631  return 0;
9632  }
9633  }
9634 
9635  // Delete cascade first
9636  if (is_array($this->childtablesoncascade) && !empty($this->childtablesoncascade)) {
9637  foreach ($this->childtablesoncascade as $table) {
9638  $deleteFromObject = explode(':', $table);
9639  if (count($deleteFromObject) >= 2) {
9640  $className = str_replace('@', '', $deleteFromObject[0]);
9641  $filePath = $deleteFromObject[1];
9642  $columnName = $deleteFromObject[2];
9643  $TMoreSQL = array();
9644  $more_sql = $deleteFromObject[3];
9645  if (!empty($more_sql)) {
9646  $TMoreSQL['customsql'] = $more_sql;
9647  }
9648  if (dol_include_once($filePath)) {
9649  $childObject = new $className($this->db);
9650  if (method_exists($childObject, 'deleteByParentField')) {
9651  $result = $childObject->deleteByParentField($this->id, $columnName, $TMoreSQL);
9652  if ($result < 0) {
9653  $error++;
9654  $this->errors[] = $childObject->error;
9655  break;
9656  }
9657  } else {
9658  $error++;
9659  $this->errors[] = "You defined a cascade delete on an object $childObject but there is no method deleteByParentField for it";
9660  break;
9661  }
9662  } else {
9663  $error++;
9664  $this->errors[] = 'Cannot include child class file '.$filePath;
9665  break;
9666  }
9667  } else {
9668  // Delete record in child table
9669  $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
9670 
9671  $resql = $this->db->query($sql);
9672  if (!$resql) {
9673  $error++;
9674  $this->error = $this->db->lasterror();
9675  $this->errors[] = $this->error;
9676  break;
9677  }
9678  }
9679  }
9680  }
9681 
9682  if (!$error) {
9683  if (!$notrigger) {
9684  // Call triggers
9685  $result = $this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
9686  if ($result < 0) {
9687  $error++;
9688  } // Do also here what you must do to rollback action if trigger fail
9689  // End call triggers
9690  }
9691  }
9692 
9693  // Delete llx_ecm_files
9694  if (!$error) {
9695  $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
9696  if (!$res) {
9697  $error++;
9698  }
9699  }
9700 
9701  // Delete linked object
9702  $res = $this->deleteObjectLinked();
9703  if ($res < 0) {
9704  $error++;
9705  }
9706 
9707  if (!$error && !empty($this->isextrafieldmanaged)) {
9708  $result = $this->deleteExtraFields();
9709  if ($result < 0) {
9710  $error++;
9711  }
9712  }
9713 
9714  if (!$error) {
9715  $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.' WHERE rowid='.((int) $this->id);
9716 
9717  $resql = $this->db->query($sql);
9718  if (!$resql) {
9719  $error++;
9720  $this->errors[] = $this->db->lasterror();
9721  }
9722  }
9723 
9724  // Commit or rollback
9725  if ($error) {
9726  $this->db->rollback();
9727  return -1;
9728  } else {
9729  $this->db->commit();
9730  return 1;
9731  }
9732  }
9733 
9744  public function deleteByParentField($parentId = 0, $parentField = '', $filter = array(), $filtermode = "AND")
9745  {
9746  global $user;
9747 
9748  $error = 0;
9749  $deleted = 0;
9750 
9751  if (!empty($parentId) && !empty($parentField)) {
9752  $this->db->begin();
9753 
9754  $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
9755  $sql .= " WHERE ".$parentField." = ".(int) $parentId;
9756 
9757  // Manage filters
9758  $sqlwhere = array();
9759  if (count($filter) > 0) {
9760  foreach ($filter as $key => $value) {
9761  if ($key == 'customsql') {
9762  $sqlwhere[] = $value;
9763  } elseif (strpos($value, '%') === false) {
9764  $sqlwhere[] = $key." IN (".$this->db->sanitize($this->db->escape($value)).")";
9765  } else {
9766  $sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'";
9767  }
9768  }
9769  }
9770  if (count($sqlwhere) > 0) {
9771  $sql .= " AND (".implode(" ".$filtermode." ", $sqlwhere).")";
9772  }
9773 
9774  $resql = $this->db->query($sql);
9775  if (!$resql) {
9776  $this->errors[] = $this->db->lasterror();
9777  $error++;
9778  } else {
9779  while ($obj = $this->db->fetch_object($resql)) {
9780  $result = $this->fetch($obj->rowid);
9781  if ($result < 0) {
9782  $error++;
9783  $this->errors[] = $this->error;
9784  } else {
9785  if (get_class($this) == 'Contact') { // TODO special code because delete() for contact has not been standardized like other delete.
9786  $result = $this->delete();
9787  } else {
9788  $result = $this->delete($user);
9789  }
9790  if ($result < 0) {
9791  $error++;
9792  $this->errors[] = $this->error;
9793  } else {
9794  $deleted++;
9795  }
9796  }
9797  }
9798  }
9799 
9800  if (empty($error)) {
9801  $this->db->commit();
9802  return $deleted;
9803  } else {
9804  $this->error = implode(', ', $this->errors);
9805  $this->db->rollback();
9806  return $error * -1;
9807  }
9808  }
9809 
9810  return $deleted;
9811  }
9812 
9821  public function deleteLineCommon(User $user, $idline, $notrigger = false)
9822  {
9823  global $conf;
9824 
9825  $error = 0;
9826 
9827  $tmpforobjectclass = get_class($this);
9828  $tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line';
9829 
9830  $this->db->begin();
9831 
9832  // Call trigger
9833  $result = $this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user);
9834  if ($result < 0) {
9835  $error++;
9836  }
9837  // End call triggers
9838 
9839  if (empty($error)) {
9840  $sql = "DELETE FROM ".$this->db->prefix().$this->table_element_line;
9841  $sql .= " WHERE rowid = ".((int) $idline);
9842 
9843  $resql = $this->db->query($sql);
9844  if (!$resql) {
9845  $this->error = "Error ".$this->db->lasterror();
9846  $error++;
9847  }
9848  }
9849 
9850  if (empty($error)) {
9851  // Remove extrafields
9852  $tmpobjectline = new $tmpforobjectlineclass($this->db);
9853  if (!isset($tmpobjectline->isextrafieldmanaged) || !empty($tmpobjectline->isextrafieldmanaged)) {
9854  $tmpobjectline->id = $idline;
9855  $result = $tmpobjectline->deleteExtraFields();
9856  if ($result < 0) {
9857  $error++;
9858  $this->error = "Error ".get_class($this)."::deleteLineCommon deleteExtraFields error -4 ".$tmpobjectline->error;
9859  }
9860  }
9861  }
9862 
9863  if (empty($error)) {
9864  $this->db->commit();
9865  return 1;
9866  } else {
9867  dol_syslog(get_class($this)."::deleteLineCommon ERROR:".$this->error, LOG_ERR);
9868  $this->db->rollback();
9869  return -1;
9870  }
9871  }
9872 
9873 
9883  public function setStatusCommon($user, $status, $notrigger = 0, $triggercode = '')
9884  {
9885  $error = 0;
9886 
9887  $this->db->begin();
9888 
9889  $statusfield = 'status';
9890  if (in_array($this->element, array('don', 'donation', 'shipping'))) {
9891  $statusfield = 'fk_statut';
9892  }
9893 
9894  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
9895  $sql .= " SET ".$statusfield." = ".((int) $status);
9896  $sql .= " WHERE rowid = ".((int) $this->id);
9897 
9898  if ($this->db->query($sql)) {
9899  if (!$error) {
9900  $this->oldcopy = clone $this;
9901  }
9902 
9903  if (!$error && !$notrigger) {
9904  // Call trigger
9905  $result = $this->call_trigger($triggercode, $user);
9906  if ($result < 0) {
9907  $error++;
9908  }
9909  }
9910 
9911  if (!$error) {
9912  $this->status = $status;
9913  $this->db->commit();
9914  return 1;
9915  } else {
9916  $this->db->rollback();
9917  return -1;
9918  }
9919  } else {
9920  $this->error = $this->db->error();
9921  $this->db->rollback();
9922  return -1;
9923  }
9924  }
9925 
9926 
9933  public function initAsSpecimenCommon()
9934  {
9935  global $user;
9936 
9937  $this->id = 0;
9938  $this->specimen = 1;
9939  $fields = array(
9940  'label' => 'This is label',
9941  'ref' => 'ABCD1234',
9942  'description' => 'This is a description',
9943  'qty' => 123.12,
9944  'note_public' => 'Public note',
9945  'note_private' => 'Private note',
9946  'date_creation' => (dol_now() - 3600 * 48),
9947  'date_modification' => (dol_now() - 3600 * 24),
9948  'fk_user_creat' => $user->id,
9949  'fk_user_modif' => $user->id,
9950  'date' => dol_now(),
9951  );
9952  foreach ($fields as $key => $value) {
9953  if (array_key_exists($key, $this->fields)) {
9954  $this->{$key} = $value;
9955  }
9956  }
9957 
9958  // Force values to default values when known
9959  if (property_exists($this, 'fields')) {
9960  foreach ($this->fields as $key => $value) {
9961  // If fields are already set, do nothing
9962  if (array_key_exists($key, $fields)) {
9963  continue;
9964  }
9965 
9966  if (!empty($value['default'])) {
9967  $this->$key = $value['default'];
9968  }
9969  }
9970  }
9971 
9972  return 1;
9973  }
9974 
9975 
9976  /* Part for comments */
9977 
9982  public function fetchComments()
9983  {
9984  require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
9985 
9986  $comment = new Comment($this->db);
9987  $result = $comment->fetchAllFor($this->element, $this->id);
9988  if ($result < 0) {
9989  $this->errors = array_merge($this->errors, $comment->errors);
9990  return -1;
9991  } else {
9992  $this->comments = $comment->comments;
9993  }
9994  return count($this->comments);
9995  }
9996 
10002  public function getNbComments()
10003  {
10004  return count($this->comments);
10005  }
10006 
10013  public function trimParameters($parameters)
10014  {
10015  if (!is_array($parameters)) {
10016  return;
10017  }
10018  foreach ($parameters as $parameter) {
10019  if (isset($this->$parameter)) {
10020  $this->$parameter = trim($this->$parameter);
10021  }
10022  }
10023  }
10024 
10025  /* Part for categories/tags */
10026 
10037  public function getCategoriesCommon($type_categ)
10038  {
10039  require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10040 
10041  // Get current categories
10042  $c = new Categorie($this->db);
10043  $existing = $c->containing($this->id, $type_categ, 'id');
10044 
10045  return $existing;
10046  }
10047 
10060  public function setCategoriesCommon($categories, $type_categ = '', $remove_existing = true)
10061  {
10062  // Handle single category
10063  if (!is_array($categories)) {
10064  $categories = array($categories);
10065  }
10066 
10067  dol_syslog(get_class($this)."::setCategoriesCommon Oject Id:".$this->id.' type_categ:'.$type_categ.' nb tag add:'.count($categories), LOG_DEBUG);
10068 
10069  require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10070 
10071  if (empty($type_categ)) {
10072  dol_syslog(__METHOD__.': Type '.$type_categ.'is an unknown category type. Done nothing.', LOG_ERR);
10073  return -1;
10074  }
10075 
10076  // Get current categories
10077  $c = new Categorie($this->db);
10078  $existing = $c->containing($this->id, $type_categ, 'id');
10079  if ($remove_existing) {
10080  // Diff
10081  if (is_array($existing)) {
10082  $to_del = array_diff($existing, $categories);
10083  $to_add = array_diff($categories, $existing);
10084  } else {
10085  $to_del = array(); // Nothing to delete
10086  $to_add = $categories;
10087  }
10088  } else {
10089  $to_del = array(); // Nothing to delete
10090  $to_add = array_diff($categories, $existing);
10091  }
10092 
10093  $error = 0;
10094  $ok = 0;
10095 
10096  // Process
10097  foreach ($to_del as $del) {
10098  if ($c->fetch($del) > 0) {
10099  $result=$c->del_type($this, $type_categ);
10100  if ($result < 0) {
10101  $error++;
10102  $this->error = $c->error;
10103  $this->errors = $c->errors;
10104  break;
10105  } else {
10106  $ok += $result;
10107  }
10108  }
10109  }
10110  foreach ($to_add as $add) {
10111  if ($c->fetch($add) > 0) {
10112  $result = $c->add_type($this, $type_categ);
10113  if ($result < 0) {
10114  $error++;
10115  $this->error = $c->error;
10116  $this->errors = $c->errors;
10117  break;
10118  } else {
10119  $ok += $result;
10120  }
10121  }
10122  }
10123 
10124  return $error ? (-1 * $error) : $ok;
10125  }
10126 
10135  public function cloneCategories($fromId, $toId, $type = '')
10136  {
10137  $this->db->begin();
10138 
10139  if (empty($type)) {
10140  $type = $this->table_element;
10141  }
10142 
10143  require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10144  $categorystatic = new Categorie($this->db);
10145 
10146  $sql = "INSERT INTO ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type])." (fk_categorie, fk_product)";
10147  $sql .= " SELECT fk_categorie, $toId FROM ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type]);
10148  $sql .= " WHERE fk_product = ".((int) $fromId);
10149 
10150  if (!$this->db->query($sql)) {
10151  $this->error = $this->db->lasterror();
10152  $this->db->rollback();
10153  return -1;
10154  }
10155 
10156  $this->db->commit();
10157  return 1;
10158  }
10159 
10166  public function deleteEcmFiles($mode = 0)
10167  {
10168  global $conf;
10169 
10170  $this->db->begin();
10171 
10172  // Delete in database with mode 0
10173  if ($mode == 0) {
10174  switch ($this->element) {
10175  case 'propal':
10176  $element = 'propale';
10177  break;
10178  case 'product':
10179  $element = 'produit';
10180  break;
10181  case 'order_supplier':
10182  $element = 'fournisseur/commande';
10183  break;
10184  case 'invoice_supplier':
10185  $element = 'fournisseur/facture/'.get_exdir($this->id, 2, 0, 1, $this, 'invoice_supplier');
10186  break;
10187  case 'shipping':
10188  $element = 'expedition/sending';
10189  break;
10190  default:
10191  $element = $this->element;
10192  }
10193 
10194  // Delete ecm_files extrafields
10195  $sql = "DELETE FROM ".$this->db->prefix()."ecm_files_extrafields WHERE fk_object IN (";
10196  $sql .= " SELECT rowid FROM ".$this->db->prefix()."ecm_files WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
10197  $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
10198  $sql .= ")";
10199 
10200  if (!$this->db->query($sql)) {
10201  $this->error = $this->db->lasterror();
10202  $this->db->rollback();
10203  return false;
10204  }
10205 
10206  // Delete ecm_files
10207  $sql = "DELETE FROM ".$this->db->prefix()."ecm_files";
10208  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
10209  $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
10210 
10211  if (!$this->db->query($sql)) {
10212  $this->error = $this->db->lasterror();
10213  $this->db->rollback();
10214  return false;
10215  }
10216  }
10217 
10218  // Delete in database with mode 1
10219  if ($mode == 1) {
10220  $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files_extrafields";
10221  $sql .= " WHERE fk_object IN (SELECT rowid FROM ".$this->db->prefix()."ecm_files WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? "" : "@".$this->module))."' AND src_object_id = ".((int) $this->id).")";
10222  $resql = $this->db->query($sql);
10223  if (!$resql) {
10224  $this->error = $this->db->lasterror();
10225  $this->db->rollback();
10226  return false;
10227  }
10228 
10229  $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files";
10230  $sql .= " WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? "" : "@".$this->module))."' AND src_object_id = ".((int) $this->id);
10231  $resql = $this->db->query($sql);
10232  if (!$resql) {
10233  $this->error = $this->db->lasterror();
10234  $this->db->rollback();
10235  return false;
10236  }
10237  }
10238 
10239  $this->db->commit();
10240  return true;
10241  }
10242 }
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition: ajax.lib.php:449
$object ref
Definition: info.php:78
Class to manage categories.
Class to manage comment.
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
getCategoriesCommon($type_categ)
Sets object to given categories.
indexFile($destfull, $update_main_doc_field)
Index a file into the ECM database.
getFormatedSupplierRef($objref)
Return supplier ref for screen output.
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
clearFieldError($fieldKey)
clear validation message result for a field
deleteEcmFiles($mode=0)
Delete related files of object in database.
getLastMainDocLink($modulepart, $initsharekey=0, $relativelink=0)
Return the link of last main doc file for direct public download.
liste_type_contact($source='internal', $order='position', $option=0, $activeonly=0, $code='')
Return array with list of possible values for type of contacts.
getTooltipContent($params)
getTooltipContent
swapContactStatus($rowid)
Update status of a contact linked to object.
getFieldError($fieldKey)
get field error message
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
updateObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $f_user=null, $notrigger=0)
Update object linked of a current object.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
fetchObjectFrom($table, $field, $key, $element=null)
Load object from specific field.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteLineCommon(User $user, $idline, $notrigger=false)
Delete a line of object in database.
static deleteAllItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
Function used to remove all items linked to an object id in association table.
deleteByParentField($parentId=0, $parentField='', $filter=array(), $filtermode="AND")
Delete all child object from a parent ID.
setFieldError($fieldKey, $msg='')
set validation error message a field
getFieldList($alias='')
Function to concat keys of fields.
setMulticurrencyCode($code)
Change the multicurrency code.
add_element_resource($resource_id, $resource_type, $busy=0, $mandatory=0)
Add resources to the current object : add entry into llx_element_resources Need $this->element & $thi...
emtpyObjectVars()
Sets all object fields to null.
fetch_projet()
Load the project with id $this->fk_project into this->project.
getIdContact($source, $code, $status=0)
Return id of contacts for a source and a contact code.
setDocModel($user, $modelpdf)
Set last model used by doc generator.
updateExtraField($key, $trigger=null, $userused=null)
Update 1 extra field value for the current object.
setPaymentTerms($id, $deposit_percent=null)
Change the payments terms.
isFloat($info)
Function test if type is float.
setExtraParameters()
Set extra parameters.
setErrorsFromObject($object)
setErrorsFromObject
update_note_public($note)
Update public note (kept for backward compatibility)
getSpecialCode($lineid)
Get special code of a line.
fetchCommon($id, $ref=null, $morewhere='')
Load object in memory from the database.
clearObjectLinkedCache()
Clear the cache saying that all linked object were already loaded.
update_ref_ext($ref_ext)
Update external ref of element.
fetchOneLike($ref)
Looks for an object with ref matching the wildcard provided It does only work when $this->table_ref_f...
showOptionals($extrafields, $mode='view', $params=null, $keysuffix='', $keyprefix='', $onetrtd=0, $display_type='card')
Function to show lines of extrafields with output datas.
hasProductsOrServices($predefined=-1)
Function to say how many lines object contains.
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check an object id/ref exists If you don't need/want to instantiate object and just need to know if o...
isObjectUsed($id=0, $entity=0)
Function to check if an object is used by others (by children).
createCommon(User $user, $notrigger=false)
Create object into database.
updateRangOfLine($rowid, $rang)
Update position of line (rang)
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='', $f_user=null, $notrigger=0)
Delete all links between an object $this.
getDefaultCreateValueFor($fieldname, $alternatevalue=null, $type='alphanohtml')
Return the default value to use for a field when showing the create form of object.
getTotalWeightVolume()
Return into unit=0, the calculated total of weight and volume of all lines * qty Calculate by adding ...
deleteCommon(User $user, $notrigger=false, $forcechilddeletion=0)
Delete object in database.
getFullAddress($withcountry=0, $sep="\n", $withregion=0, $extralangcode='')
Return full address of contact.
fetch_project()
Load the project with id $this->fk_project into this->project.
setStatut($status, $elementId=null, $elementType='', $trigkey='', $fieldstatus='fk_statut')
Set status of an object.
update_price($exclspec=0, $roundingadjust='none', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
getNbComments()
Return nb comments already posted.
setVarsFromFetchObj(&$obj)
Function to load data from a SQL pointer into properties of current object $this.
printObjectLines($action, $seller, $buyer, $selected=0, $dateSelector=0, $defaulttpldir='/core/tpl')
Return HTML table for object lines TODO Move this into an output class file (htmlline....
getChildrenOfLine($id, $includealltree=0)
Get children of line.
updateExtraLanguages($key, $trigger=null, $userused=null)
Update an extra language value for the current object.
deleteExtraFields()
Delete all extra fields values for the current object.
setStatusCommon($user, $status, $notrigger=0, $triggercode='')
Set to a status.
addThumbs($file)
Build thumb.
setSaveQuery()
Function to prepare a part of the query for insert by returning an array with all properties of objec...
setValuesForExtraLanguages($onlykey='')
Fill array_options property of object by extrafields value (using for data sent by forms)
insertExtraLanguages($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
setTransportMode($id)
Change the transport mode methods.
isArray($info)
Function test if type is array.
isInt($info)
Function test if type is integer.
validateField($fields, $fieldKey, $fieldValue)
Return validation test result for a field.
delete_contact($rowid, $notrigger=0)
Delete a link to contact line.
updateLineUp($rowid, $rang)
Update position of line up (rang)
fetch_user($userid)
Load the user with id $userid into this->user.
errorsToString()
Method to output saved errors.
setBankAccount($fk_account, $notrigger=false, $userused=null)
Change the bank account.
getListContactId($source='external')
Return list of id of contacts of object.
setWarehouse($warehouse_id)
Change the warehouse.
setDeliveryAddress($id)
Define delivery address.
getTotalDiscount()
Function that returns the total amount HT of discounts applied for all lines.
setShippingMethod($shipping_method_id, $notrigger=false, $userused=null)
Change the shipping method.
initAsSpecimenCommon()
Initialise object with example values Id must be 0 if object instance is a specimen.
printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected=0, $extrafields=null, $defaulttpldir='/core/tpl')
Return HTML content of a detail line TODO Move this into an output class file (htmlline....
copy_linked_contact($objFrom, $source='internal')
Copy contact from one element to current.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
getValueFrom($table, $id, $field)
Getter generic.
trimParameters($parameters)
Trim object parameters.
fetch_product()
Load the product with id $this->fk_product into this->product.
isIndex($info)
Function test if is indexed.
quote($value, $fieldsentry)
Add quote to field value if necessary.
formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir='/core/tpl')
Show add free and predefined products/services form.
fetchComments()
Load comments linked with current task.
line_down($rowid, $fk_parent_line=true)
Update a line to have a higher rank.
setValueFrom($field, $value, $table='', $id=null, $format='', $id_field='', $fuser=null, $trigkey='', $fk_user_field='fk_user_modif')
Setter generic.
fetch_contact($contactid=null)
Load object contact with id=$this->contact_id into $this->contact.
setRetainedWarrantyPaymentTerms($id)
Change the retained warranty payments terms.
fetchObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $clause='OR', $alsosametype=1, $orderby='sourcetype', $loadalsoobjects=1)
Fetch array of objects linked to current object (object of enabled modules only).
delThumbs($file)
Delete thumbs.
show_photos($modulepart, $sdir, $size=0, $nbmax=0, $nbbyrow=5, $showfilename=0, $showaction=0, $maxHeight=120, $maxWidth=160, $nolink=0, $overwritetitle=0, $usesharelink=0, $cache='', $addphotorefcss='photoref')
Show photos of an object (nbmax maximum), into several columns.
updateCommon(User $user, $notrigger=false)
Update object into database.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
getRangOfLine($rowid)
Get position of line (rang)
showInputField($val, $key, $value, $moreparam='', $keysuffix='', $keyprefix='', $morecss=0, $nonewbutton=0)
Return HTML string to put an input field into a page Code very similar with showInputField of extra f...
delete_resource($rowid, $element, $notrigger=0)
Delete a link to resource line.
update_contact($rowid, $statut, $type_contact_id=0, $fk_socpeople=0)
Update a link to contact line.
update_note($note, $suffix='')
Update note of element.
load_previous_next_ref($filter, $fieldid, $nodbprefix=0)
Load properties id_previous and id_next by comparing $fieldid with $this->ref.
setCategoriesCommon($categories, $type_categ='', $remove_existing=true)
Sets object to given categories.
fetchValuesForExtraLanguages()
Function to get alternative languages of a data into $this->array_languages This method is NOT called...
fetchNoCompute($id)
Function to make a fetch but set environment to avoid to load computed values before.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
getJSListDependancies($type='_extra')
setProject($projectid, $notrigger=0)
Link element with a project.
isForcedToNullIfZero($info)
Function test if field is forced to null if zero or empty.
liste_contact($statusoflink=-1, $source='external', $list=0, $code='', $status=-1, $arrayoftcids=array())
Get array of all contacts for an object.
line_ajaxorder($rows)
Update position of line with ajax (rang)
printOriginLinesList($restrictlist='', $selectedLines=array())
Return HTML table table of source object lines TODO Move this and previous function into output html ...
fetchLinesCommon($morewhere='')
Load object in memory from the database.
fetch_origin()
Read linked origin object.
getIdOfLine($rang)
Get rowid of the line relative to its position.
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
static getAllItemsLinkedByObjectID($fk_object_where, $field_select, $field_where, $table_element)
Function used to get an array with all items linked to an object id in association table.
__clone()
Overwrite magic function to solve problem of cloning object that are kept as references.
setPaymentMethods($id)
Change the payments methods.
updateLineDown($rowid, $rang, $max)
Update position of line down (rang)
delete_linked_contact($source='', $code='')
Delete all links between an object $this and all its contacts in llx_element_contact.
getFormatedCustomerRef($objref)
Return customer ref for screen output.
getTooltipContentArray($params)
Return array of datas to show into a tooltip.
isText($info)
Function test if type is text.
isDate($info)
Function test if type is date.
printOriginLine($line, $var, $restrictlist='', $defaulttpldir='/core/tpl', $selectedLines=array())
Return HTML with a line of table array of source object lines TODO Move this and previous function in...
getCanvas($id=0, $ref='')
Load type of canvas of an object if it exists.
line_up($rowid, $fk_parent_line=true)
Update a line to have a lower rank.
isDuration($info)
Function test if type is duration.
canBeNull($info)
Function test if field can be null.
listeTypeContacts($source='internal', $option=0, $activeonly=0, $code='', $element='', $excludeelement='')
Return array with list of possible values for type of contacts.
call_trigger($triggerName, $user)
Call trigger based on this instance.
getRights()
Returns the rights used for this class.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
cloneCategories($fromId, $toId, $type='')
Copy related categories to another object.
fetch_barcode()
Load data for barcode into properties ->barcode_type* Properties ->barcode_type that is id of barcode...
Class to manage contact/addresses.
Class to manage absolute discounts.
Class to manage a WYSIWYG editor.
Class to manage Dolibarr database access.
Class to manage ECM files.
Class to manage standard extra fields.
Class to manage generation of HTML components Only common components must be here.
Class to manage triggers.
static getIdAndTxFromCode($dbs, $code, $date_document='')
Get id and rate of currency from code.
Class to manage predefined suppliers products.
Class to manage products or services.
const TYPE_SERVICE
Service.
Class to manage projects.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage translations.
Class to manage Dolibarr users.
Definition: user.class.php:48
Class toolbox to validate values.
getCountry($searchkey, $withcode='', $dbtouse=0, $outputlangs='', $entconv=1, $searchlabel='')
Return country label, code or id from an id, code or label.
getState($id, $withcode='', $dbtouse=0, $withregion=0, $outputlangs='', $entconv=1)
Return state translated from an id.
if(isModEnabled('facture') && $user->hasRight('facture', 'lire')) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("supplier_invoice", "lire"))) if(isModEnabled('don') && $user->hasRight('don', 'lire')) if(isModEnabled('tax') &&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture') &&isModEnabled('commande') && $user->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $sql
Social contributions to pay.
Definition: index.php:746
if($cancel &&! $id) if($action=='add' &&! $cancel) if($action=='delete') if($id) $form
Actions.
Definition: card.php:143
convertSecondToTime($iSecond, $format='all', $lengthOfDay=86400, $lengthOfWeek=7)
Return, in clear text, value of a number of seconds in days, hours and minutes.
Definition: date.lib.php:239
print *****$script_file(".$version.") pid c cd cd cd description as p label as s rowid
dol_meta_create($object)
Create a meta file with document file into same directory.
Definition: files.lib.php:1614
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
Definition: files.lib.php:1334
dol_is_file($pathoffile)
Return if path is a file.
Definition: files.lib.php:483
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:62
completeFileArrayWithDatabaseInfo(&$filearray, $relativedir)
Complete $filearray with data from database.
Definition: files.lib.php:315
dol_delete_preview($object)
Delete all preview files linked to object instance.
Definition: files.lib.php:1537
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed informations (by default a local PHP server timestamp) Re...
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formated for view output Used into pdf and HTML pages.
dol_print_ip($ip, $mode=0)
Return an IP formated to be shown on screen.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
get_date_range($date_start, $date_end, $format='', $outputlangs='', $withparenthesis=1)
Format output for start and end date.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
dol_format_address($object, $withcountry=0, $sep="\n", $outputlangs='', $mode=0, $extralangcode='')
Return a formated address (part address/zip/town/state) according to country rules.
dol_string_nospecial($str, $newstr='_', $badcharstoreplace='', $badcharstoremove='', $keepspaces=0)
Clean a string from all punctuation characters to use it as a ref or login.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0)
Show EMail link formatted for HTML output.
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_print_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0)
Format phone numbers according to country.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by second index function, which produces ascending (default) or descending output...
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dol_eval($s, $returnvalue=0, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
getImageFileNameForSize($file, $extName, $extImgTarget='')
Return the filename of file to get the thumbs.
getAdvancedPreviewUrl($modulepart, $relativepath, $alldata=0, $param='')
Return URL we can use for advanced preview links.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
dol_print_url($url, $target='_blank', $max=32, $withpicto=0, $morecss='float')
Show Url link.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
This function is called to encode a string into a HTML string but differs from htmlentities because a...
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
getDictionaryValue($tablename, $field, $id, $checkentity=false, $rowidfield='rowid')
Return the value of a filed into a dictionary for the record $id.
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
vignette($file, $maxWidth=160, $maxHeight=120, $extName='_small', $quality=50, $outdir='thumbs', $targetformat=0)
Create a thumbnail from an image file (Supported extensions are gif, jpg, png and bmp).
Definition: images.lib.php:509
getDefaultImageSizes()
Return default values for image sizes.
Definition: images.lib.php:38
dol_getImageSize($file, $url=false)
Return size of image file on disk (Supported extensions are gif, jpg, png, bmp and webp)
Definition: images.lib.php:140
image_format_supported($file, $acceptsvg=0)
Return if a filename is file name of a supported image format.
Definition: images.lib.php:80
query($query, $usesavepoint=0, $type='auto', $result_mode=0)
Execute a SQL request and return the resultset.
if(!defined( 'CSRFCHECK_WITH_TOKEN'))
div float
Buy price without taxes.
Definition: style.css.php:921
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array='', $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition: price.lib.php:86
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition: repair.php:120
$conf db user
Definition: repair.php:124
getRandomPassword($generic=false, $replaceambiguouschars=null, $length=32)
Return a generated password using default module.
dol_hash($chain, $type='0')
Returns a hash (non reversible encryption) of a string.