dolibarr  18.0.6
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 
196  public $canvas;
197 
202  public $project;
203 
208  public $fk_project;
209 
215  public $projet;
216 
221  public $fk_projet;
222 
227  public $contact;
228 
233  public $contact_id;
234 
239  public $thirdparty;
240 
245  public $user;
246 
251  public $origin;
252 
257  public $origin_id;
258 
262  public $ref;
263 
267  public $ref_ext;
268 
272  public $ref_previous;
273 
277  public $ref_next;
278 
282  public $newref;
283 
288  public $statut;
289 
294  public $status;
295 
296 
301  public $country;
302 
307  public $country_id;
308 
313  public $country_code;
314 
319  public $state;
320 
325  public $state_id;
326 
331  public $state_code;
332 
337  public $region_id;
338 
343  public $region_code;
344 
349  public $region;
350 
351 
356  public $barcode_type;
357 
362  public $barcode_type_code;
363 
368  public $barcode_type_label;
369 
374  public $barcode_type_coder;
375 
380  public $mode_reglement_id;
381 
386  public $cond_reglement_id;
387 
391  public $demand_reason_id;
392 
397  public $transport_mode_id;
398 
404  public $cond_reglement;
405 
411  public $fk_delivery_address;
412 
417  public $shipping_method_id;
418 
423  public $shipping_method;
424 
428  public $multicurrency_code;
429 
433  public $multicurrency_tx;
434 
439  public $model_pdf;
440 
446  public $modelpdf;
447 
452  public $last_main_doc;
453 
459  public $fk_bank;
460 
465  public $fk_account;
466 
470  public $openid;
471 
476  public $note_public;
477 
482  public $note_private;
483 
488  public $note;
489 
494  public $total_ht;
495 
500  public $total_tva;
501 
506  public $total_localtax1;
507 
512  public $total_localtax2;
513 
518  public $total_ttc;
519 
523  public $lines;
524 
529  public $comments = array();
530 
534  public $name;
535 
539  public $lastname;
540 
544  public $firstname;
545 
549  public $civility_id;
550 
551  // Dates
555  public $date_creation;
556 
560  public $date_validation; // Date validation
561 
565  public $date_modification; // Date last change (tms field)
570  public $date_update;
571 
575  public $date_cloture; // Date closing (tms field)
576 
581  public $user_author;
586  public $user_creation;
590  public $user_creation_id;
591 
596  public $user_valid;
601  public $user_validation;
605  public $user_validation_id;
609  public $user_closing_id;
610 
615  public $user_modification;
619  public $user_modification_id;
620 
621 
626  public $fk_user_creat;
627 
632  public $fk_user_modif;
633 
634 
635  public $next_prev_filter;
636 
640  public $specimen = 0;
641 
645  public $sendtoid;
646 
650  public $alreadypaid;
651 
652 
653  public $labelStatus;
654  protected $labelStatusShort;
655 
659  public $showphoto_on_popup;
660 
664  public $nb = array();
665 
669  public $output;
670 
674  public $extraparams = array();
675 
679  protected $childtables = array();
680 
686  protected $childtablesoncascade = array();
687 
688 
689  // No constructor as it is an abstract class
690 
691 
702  public static function isExistingObject($element, $id, $ref = '', $ref_ext = '')
703  {
704  global $db, $conf;
705 
706  $sql = "SELECT rowid, ref, ref_ext";
707  $sql .= " FROM ".$db->prefix().$element;
708  $sql .= " WHERE entity IN (".getEntity($element).")";
709 
710  if ($id > 0) {
711  $sql .= " AND rowid = ".((int) $id);
712  } elseif ($ref) {
713  $sql .= " AND ref = '".$db->escape($ref)."'";
714  } elseif ($ref_ext) {
715  $sql .= " AND ref_ext = '".$db->escape($ref_ext)."'";
716  } else {
717  $error = 'ErrorWrongParameters';
718  dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
719  return -1;
720  }
721  if ($ref || $ref_ext) { // Because the same ref can exists in 2 different entities, we force the current one in priority
722  $sql .= " AND entity = ".((int) $conf->entity);
723  }
724 
725  dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
726  $resql = $db->query($sql);
727  if ($resql) {
728  $num = $db->num_rows($resql);
729  if ($num > 0) {
730  return 1;
731  } else {
732  return 0;
733  }
734  }
735  return -1;
736  }
737 
744  public function setErrorsFromObject($object)
745  {
746  if (!empty($object->error)) {
747  $this->error = $object->error;
748  }
749  if (!empty($object->errors)) {
750  $this->errors = array_merge($this->errors, $object->errors);
751  }
752  }
753 
761  public function getTooltipContentArray($params)
762  {
763  return [];
764  }
765 
773  public function getTooltipContent($params)
774  {
775  global $action, $extrafields, $langs, $hookmanager;
776 
777  // If there is too much extrafields, we do not include them into tooltip
778  $MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP = getDolGlobalInt('MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP', 3);
779 
780  $datas = $this->getTooltipContentArray($params);
781  $count = 0;
782 
783  // Add extrafields
784  if (!empty($extrafields->attributes[$this->table_element]['label'])) {
785  foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
786  if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
787  continue;
788  }
789  if ($count >= abs($MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP)) {
790  $datas['more_extrafields'] = '<br>...';
791  break;
792  }
793  $enabled = 1;
794  if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
795  $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
796  }
797  if ($enabled && isset($extrafields->attributes[$this->table_element]['list'][$key])) {
798  $enabled = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
799  }
800  $perms = 1;
801  if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
802  $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
803  }
804  if (empty($enabled)) {
805  continue; // 0 = Never visible field
806  }
807  if (abs($enabled) != 1 && abs($enabled) != 3 && abs($enabled) != 5 && abs($enabled) != 4) {
808  continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list <> 4 = not visible at the creation
809  }
810  if (empty($perms)) {
811  continue; // 0 = Not visible
812  }
813  if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
814  $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
815  }
816  $labelextra = $langs->trans((string) $extrafields->attributes[$this->table_element]['label'][$key]);
817  if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
818  $datas[$key]= '<br><b><u>'. $labelextra . '</u></b>';
819  } else {
820  $value = (empty($this->array_options['options_' . $key]) ? '' : $this->array_options['options_' . $key]);
821  $datas[$key]= '<br><b>'. $labelextra . ':</b> ' . $extrafields->showOutputField($key, $value, '', $this->table_element);
822  $count++;
823  }
824  }
825  }
826 
827  $hookmanager->initHooks(array($this->element . 'dao'));
828  $parameters = array(
829  'tooltipcontentarray' => &$datas,
830  'params' => $params,
831  );
832  // Note that $action and $object may have been modified by some hooks
833  $hookmanager->executeHooks('getTooltipContent', $parameters, $this, $action);
834 
835  //var_dump($datas);
836  $label = implode($datas);
837 
838  return $label;
839  }
840 
841 
847  public function errorsToString()
848  {
849  return $this->error.(is_array($this->errors) ? (($this->error != '' ? ', ' : '').join(', ', $this->errors)) : '');
850  }
851 
852 
859  public function getFormatedCustomerRef($objref)
860  {
861  global $hookmanager;
862 
863  $parameters = array('objref'=>$objref);
864  $action = '';
865  $reshook = $hookmanager->executeHooks('getFormatedCustomerRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
866  if ($reshook > 0) {
867  return $hookmanager->resArray['objref'];
868  }
869  return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
870  }
871 
878  public function getFormatedSupplierRef($objref)
879  {
880  global $hookmanager;
881 
882  $parameters = array('objref'=>$objref);
883  $action = '';
884  $reshook = $hookmanager->executeHooks('getFormatedSupplierRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
885  if ($reshook > 0) {
886  return $hookmanager->resArray['objref'];
887  }
888  return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
889  }
890 
900  public function getFullAddress($withcountry = 0, $sep = "\n", $withregion = 0, $extralangcode = '')
901  {
902  if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country))) {
903  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
904  $tmparray = getCountry($this->country_id, 'all');
905  $this->country_code = $tmparray['code'];
906  $this->country = $tmparray['label'];
907  }
908 
909  if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_code))) {
910  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
911  $tmparray = getState($this->state_id, 'all', 0, 1);
912  $this->state_code = $tmparray['code'];
913  $this->state = $tmparray['label'];
914  $this->region_code = $tmparray['region_code'];
915  $this->region = $tmparray['region'];
916  }
917 
918  return dol_format_address($this, $withcountry, $sep, '', 0, $extralangcode);
919  }
920 
921 
930  public function getLastMainDocLink($modulepart, $initsharekey = 0, $relativelink = 0)
931  {
932  global $user, $dolibarr_main_url_root;
933 
934  if (empty($this->last_main_doc)) {
935  return ''; // No way to known which document name to use
936  }
937 
938  include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
939  $ecmfile = new EcmFiles($this->db);
940  $result = $ecmfile->fetch(0, '', $this->last_main_doc);
941  if ($result < 0) {
942  $this->error = $ecmfile->error;
943  $this->errors = $ecmfile->errors;
944  return -1;
945  }
946 
947  if (empty($ecmfile->id)) {
948  // Add entry into index
949  if ($initsharekey) {
950  require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
951 
952  // 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
953  /*
954  $ecmfile->filepath = $rel_dir;
955  $ecmfile->filename = $filename;
956  $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
957  $ecmfile->fullpath_orig = '';
958  $ecmfile->gen_or_uploaded = 'generated';
959  $ecmfile->description = ''; // indexed content
960  $ecmfile->keywords = ''; // keyword content
961  $ecmfile->share = getRandomPassword(true);
962  $result = $ecmfile->create($user);
963  if ($result < 0)
964  {
965  $this->error = $ecmfile->error;
966  $this->errors = $ecmfile->errors;
967  }
968  */
969  } else {
970  return '';
971  }
972  } elseif (empty($ecmfile->share)) {
973  // Add entry into index
974  if ($initsharekey) {
975  require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
976  $ecmfile->share = getRandomPassword(true);
977  $ecmfile->update($user);
978  } else {
979  return '';
980  }
981  }
982  // Define $urlwithroot
983  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
984  // This is to use external domain name found into config file
985  //if (DOL_URL_ROOT && ! preg_match('/\/$/', $urlwithouturlroot) && ! preg_match('/^\//', DOL_URL_ROOT)) $urlwithroot=$urlwithouturlroot.'/'.DOL_URL_ROOT;
986  //else
987  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT;
988  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
989 
990  $forcedownload = 0;
991 
992  $paramlink = '';
993  //if (!empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart; // For sharing with hash (so public files), modulepart is not required.
994  //if (!empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; // For sharing with hash (so public files), entity is not required.
995  //$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath); // No need of name of file for public link, we will use the hash
996  if (!empty($ecmfile->share)) {
997  $paramlink .= ($paramlink ? '&' : '').'hashp='.$ecmfile->share; // Hash for public share
998  }
999  if ($forcedownload) {
1000  $paramlink .= ($paramlink ? '&' : '').'attachment=1';
1001  }
1002 
1003  if ($relativelink) {
1004  $linktoreturn = 'document.php'.($paramlink ? '?'.$paramlink : '');
1005  } else {
1006  $linktoreturn = $urlwithroot.'/document.php'.($paramlink ? '?'.$paramlink : '');
1007  }
1008 
1009  // Here $ecmfile->share is defined
1010  return $linktoreturn;
1011  }
1012 
1013 
1014  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1024  public function add_contact($fk_socpeople, $type_contact, $source = 'external', $notrigger = 0)
1025  {
1026  // phpcs:enable
1027  global $user, $langs;
1028 
1029 
1030  dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
1031 
1032  // Check parameters
1033  if ($fk_socpeople <= 0) {
1034  $langs->load("errors");
1035  $this->error = $langs->trans("ErrorWrongValueForParameterX", "1");
1036  dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
1037  return -1;
1038  }
1039  if (!$type_contact) {
1040  $langs->load("errors");
1041  $this->error = $langs->trans("ErrorWrongValueForParameterX", "2");
1042  dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
1043  return -2;
1044  }
1045 
1046  $id_type_contact = 0;
1047  if (is_numeric($type_contact)) {
1048  $id_type_contact = $type_contact;
1049  } else {
1050  // We look for id type_contact
1051  $sql = "SELECT tc.rowid";
1052  $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1053  $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1054  $sql .= " AND tc.source='".$this->db->escape($source)."'";
1055  $sql .= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
1056  //print $sql;
1057  $resql = $this->db->query($sql);
1058  if ($resql) {
1059  $obj = $this->db->fetch_object($resql);
1060  if ($obj) {
1061  $id_type_contact = $obj->rowid;
1062  }
1063  }
1064  }
1065 
1066  if ($id_type_contact == 0) {
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 0;
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  $error = 0;
1190 
1191  $this->db->begin();
1192 
1193  if (!$error && empty($notrigger)) {
1194  // Call trigger
1195  $this->context['contact_id'] = ((int) $rowid);
1196  $result = $this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
1197  if ($result < 0) {
1198  $error++;
1199  }
1200  // End call triggers
1201  }
1202 
1203  if (!$error) {
1204  dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
1205 
1206  $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
1207  $sql .= " WHERE rowid = ".((int) $rowid);
1208 
1209  $result = $this->db->query($sql);
1210  if (!$result) {
1211  $error++;
1212  $this->errors[] = $this->db->lasterror();
1213  }
1214  }
1215 
1216  if (!$error) {
1217  $this->db->commit();
1218  return 1;
1219  } else {
1220  $this->error = $this->db->lasterror();
1221  $this->db->rollback();
1222  return -1;
1223  }
1224  }
1225 
1226  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1234  public function delete_linked_contact($source = '', $code = '')
1235  {
1236  // phpcs:enable
1237  $listId = '';
1238  $temp = array();
1239  $typeContact = $this->liste_type_contact($source, '', 0, 0, $code);
1240 
1241  if (!empty($typeContact)) {
1242  foreach ($typeContact as $key => $value) {
1243  array_push($temp, $key);
1244  }
1245  $listId = implode(",", $temp);
1246  }
1247 
1248  // If $listId is empty, we have not criteria on fk_c_type_contact so we will delete record on element_id for
1249  // any type or record instead of only the ones of the current object. So we do nothing in such a case.
1250  if (empty($listId)) {
1251  return 0;
1252  }
1253 
1254  $sql = "DELETE FROM ".$this->db->prefix()."element_contact";
1255  $sql .= " WHERE element_id = ".((int) $this->id);
1256  $sql .= " AND fk_c_type_contact IN (".$this->db->sanitize($listId).")";
1257 
1258  dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
1259  if ($this->db->query($sql)) {
1260  return 1;
1261  } else {
1262  $this->error = $this->db->lasterror();
1263  return -1;
1264  }
1265  }
1266 
1267  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1279  public function liste_contact($statusoflink = -1, $source = 'external', $list = 0, $code = '', $status = -1, $arrayoftcids = array())
1280  {
1281  // phpcs:enable
1282  global $langs;
1283 
1284  $tab = array();
1285 
1286  $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
1287  if ($source == 'internal') {
1288  $sql .= ", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
1289  }
1290  if ($source == 'external' || $source == 'thirdparty') {
1291  $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
1292  }
1293  $sql .= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
1294  $sql .= ", tc.source, tc.element, tc.code, tc.libelle";
1295  $sql .= " FROM ".$this->db->prefix()."c_type_contact tc,";
1296  $sql .= " ".$this->db->prefix()."element_contact ec";
1297  if ($source == 'internal') { // internal contact (user)
1298  $sql .= " LEFT JOIN ".$this->db->prefix()."user t on ec.fk_socpeople = t.rowid";
1299  }
1300  if ($source == 'external' || $source == 'thirdparty') { // external contact (socpeople)
1301  $sql .= " LEFT JOIN ".$this->db->prefix()."socpeople t on ec.fk_socpeople = t.rowid";
1302  }
1303  $sql .= " WHERE ec.element_id = ".((int) $this->id);
1304  $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1305  $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1306  if ($code) {
1307  $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1308  }
1309  if ($source == 'internal') {
1310  $sql .= " AND tc.source = 'internal'";
1311  if ($status >= 0) {
1312  $sql .= " AND t.statut = ".((int) $status);
1313  }
1314  }
1315  if ($source == 'external' || $source == 'thirdparty') {
1316  $sql .= " AND tc.source = 'external'";
1317  if ($status >= 0) {
1318  $sql .= " AND t.statut = ".((int) $status); // t is llx_socpeople
1319  }
1320  }
1321  $sql .= " AND tc.active = 1";
1322  if ($statusoflink >= 0) {
1323  $sql .= " AND ec.statut = ".((int) $statusoflink);
1324  }
1325  $sql .= " ORDER BY t.lastname ASC";
1326 
1327  dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
1328  $resql = $this->db->query($sql);
1329  if ($resql) {
1330  $num = $this->db->num_rows($resql);
1331  $i = 0;
1332  while ($i < $num) {
1333  $obj = $this->db->fetch_object($resql);
1334 
1335  if (!$list) {
1336  $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
1337  $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1338  $tab[$i] = array(
1339  'parentId' => $this->id,
1340  'source' => $obj->source,
1341  'socid' => $obj->socid,
1342  'id' => $obj->id,
1343  'nom' => $obj->lastname, // For backward compatibility
1344  'civility' => $obj->civility,
1345  'lastname' => $obj->lastname,
1346  'firstname' => $obj->firstname,
1347  'email'=>$obj->email,
1348  'login'=> (empty($obj->login) ? '' : $obj->login),
1349  'photo' => (empty($obj->photo) ? '' : $obj->photo),
1350  'statuscontact' => $obj->statuscontact,
1351  'rowid' => $obj->rowid,
1352  'code' => $obj->code,
1353  'libelle' => $libelle_type,
1354  'status' => $obj->statuslink,
1355  'fk_c_type_contact' => $obj->fk_c_type_contact
1356  );
1357  } else {
1358  $tab[$i] = $obj->id;
1359  }
1360 
1361  $i++;
1362  }
1363 
1364  return $tab;
1365  } else {
1366  $this->error = $this->db->lasterror();
1367  dol_print_error($this->db);
1368  return -1;
1369  }
1370  }
1371 
1372 
1379  public function swapContactStatus($rowid)
1380  {
1381  $sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1382  $sql .= " tc.code, tc.libelle";
1383  $sql .= " FROM (".$this->db->prefix()."element_contact as ec, ".$this->db->prefix()."c_type_contact as tc)";
1384  $sql .= " WHERE ec.rowid =".((int) $rowid);
1385  $sql .= " AND ec.fk_c_type_contact=tc.rowid";
1386  $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1387 
1388  dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1389  $resql = $this->db->query($sql);
1390  if ($resql) {
1391  $obj = $this->db->fetch_object($resql);
1392  $newstatut = ($obj->statut == 4) ? 5 : 4;
1393  $result = $this->update_contact($rowid, $newstatut);
1394  $this->db->free($resql);
1395  return $result;
1396  } else {
1397  $this->error = $this->db->error();
1398  dol_print_error($this->db);
1399  return -1;
1400  }
1401  }
1402 
1403  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1414  public function liste_type_contact($source = 'internal', $order = 'position', $option = 0, $activeonly = 0, $code = '')
1415  {
1416  // phpcs:enable
1417  global $langs;
1418 
1419  if (empty($order)) {
1420  $order = 'position';
1421  }
1422  if ($order == 'position') {
1423  $order .= ',code';
1424  }
1425 
1426  $tab = array();
1427  $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position";
1428  $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1429  $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1430  if ($activeonly == 1) {
1431  $sql .= " AND tc.active=1"; // only the active types
1432  }
1433  if (!empty($source) && $source != 'all') {
1434  $sql .= " AND tc.source='".$this->db->escape($source)."'";
1435  }
1436  if (!empty($code)) {
1437  $sql .= " AND tc.code='".$this->db->escape($code)."'";
1438  }
1439  $sql .= $this->db->order($order, 'ASC');
1440 
1441  //print "sql=".$sql;
1442  $resql = $this->db->query($sql);
1443  if ($resql) {
1444  $num = $this->db->num_rows($resql);
1445  $i = 0;
1446  while ($i < $num) {
1447  $obj = $this->db->fetch_object($resql);
1448 
1449  $transkey = "TypeContact_".$this->element."_".$source."_".$obj->code;
1450  $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1451  if (empty($option)) {
1452  $tab[$obj->rowid] = $libelle_type;
1453  } else {
1454  $tab[$obj->code] = $libelle_type;
1455  }
1456  $i++;
1457  }
1458  return $tab;
1459  } else {
1460  $this->error = $this->db->lasterror();
1461  //dol_print_error($this->db);
1462  return null;
1463  }
1464  }
1465 
1477  public function listeTypeContacts($source = 'internal', $option = 0, $activeonly = 0, $code = '', $element = '', $excludeelement = '')
1478  {
1479  global $langs, $conf;
1480 
1481  $langs->loadLangs(array('bills', 'contracts', 'interventions', 'orders', 'projects', 'propal', 'ticket', 'agenda'));
1482 
1483  $tab = array();
1484 
1485  $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position, tc.element";
1486  $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1487 
1488  $sqlWhere = array();
1489  if (!empty($element)) {
1490  $sqlWhere[] = " tc.element='".$this->db->escape($element)."'";
1491  }
1492  if (!empty($excludeelement)) {
1493  $sqlWhere[] = " tc.element <> '".$this->db->escape($excludeelement)."'";
1494  }
1495 
1496  if ($activeonly == 1) {
1497  $sqlWhere[] = " tc.active=1"; // only the active types
1498  }
1499 
1500  if (!empty($source) && $source != 'all') {
1501  $sqlWhere[] = " tc.source='".$this->db->escape($source)."'";
1502  }
1503 
1504  if (!empty($code)) {
1505  $sqlWhere[] = " tc.code='".$this->db->escape($code)."'";
1506  }
1507 
1508  if (count($sqlWhere) > 0) {
1509  $sql .= " WHERE ".implode(' AND ', $sqlWhere);
1510  }
1511 
1512  $sql .= $this->db->order('tc.element, tc.position', 'ASC');
1513 
1514  dol_syslog(__METHOD__, LOG_DEBUG);
1515  $resql = $this->db->query($sql);
1516  if ($resql) {
1517  $num = $this->db->num_rows($resql);
1518  if ($num > 0) {
1519  $langs->loadLangs(array("propal", "orders", "bills", "suppliers", "contracts", "supplier_proposal"));
1520 
1521  while ($obj = $this->db->fetch_object($resql)) {
1522  $modulename = $obj->element;
1523  if (strpos($obj->element, 'project') !== false) {
1524  $modulename = 'projet';
1525  } elseif ($obj->element == 'contrat') {
1526  $element = 'contract';
1527  } elseif ($obj->element == 'action') {
1528  $modulename = 'agenda';
1529  } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1530  $modulename = 'fournisseur';
1531  } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1532  $modulename = 'fournisseur';
1533  }
1534  if (!empty($conf->{$modulename}->enabled)) {
1535  $libelle_element = $langs->trans('ContactDefault_'.$obj->element);
1536  $tmpelement = $obj->element;
1537  $transkey = "TypeContact_".$tmpelement."_".$source."_".$obj->code;
1538  $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1539  if (empty($option)) {
1540  $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1541  } else {
1542  $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1543  }
1544  }
1545  }
1546  }
1547  return $tab;
1548  } else {
1549  $this->error = $this->db->lasterror();
1550  return null;
1551  }
1552  }
1553 
1565  public function getIdContact($source, $code, $status = 0)
1566  {
1567  global $conf;
1568 
1569  $result = array();
1570  $i = 0;
1571  //cas particulier pour les expeditions
1572  if ($this->element == 'shipping' && $this->origin_id != 0) {
1573  $id = $this->origin_id;
1574  $element = 'commande';
1575  } elseif ($this->element == 'reception' && $this->origin_id != 0) {
1576  $id = $this->origin_id;
1577  $element = 'order_supplier';
1578  } else {
1579  $id = $this->id;
1580  $element = $this->element;
1581  }
1582 
1583  $sql = "SELECT ec.fk_socpeople";
1584  $sql .= " FROM ".$this->db->prefix()."element_contact as ec,";
1585  if ($source == 'internal') {
1586  $sql .= " ".$this->db->prefix()."user as c,";
1587  }
1588  if ($source == 'external') {
1589  $sql .= " ".$this->db->prefix()."socpeople as c,";
1590  }
1591  $sql .= " ".$this->db->prefix()."c_type_contact as tc";
1592  $sql .= " WHERE ec.element_id = ".((int) $id);
1593  $sql .= " AND ec.fk_socpeople = c.rowid";
1594  if ($source == 'internal') {
1595  $sql .= " AND c.entity IN (".getEntity('user').")";
1596  }
1597  if ($source == 'external') {
1598  $sql .= " AND c.entity IN (".getEntity('societe').")";
1599  }
1600  $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1601  $sql .= " AND tc.element = '".$this->db->escape($element)."'";
1602  $sql .= " AND tc.source = '".$this->db->escape($source)."'";
1603  if ($code) {
1604  $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1605  }
1606  $sql .= " AND tc.active = 1";
1607  if ($status) {
1608  $sql .= " AND ec.statut = ".((int) $status);
1609  }
1610 
1611  dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1612  $resql = $this->db->query($sql);
1613  if ($resql) {
1614  while ($obj = $this->db->fetch_object($resql)) {
1615  $result[$i] = $obj->fk_socpeople;
1616  $i++;
1617  }
1618  } else {
1619  $this->error = $this->db->error();
1620  return null;
1621  }
1622 
1623  return $result;
1624  }
1625 
1626  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1633  public function fetch_contact($contactid = null)
1634  {
1635  // phpcs:enable
1636  if (empty($contactid)) {
1637  $contactid = $this->contact_id;
1638  }
1639 
1640  if (empty($contactid)) {
1641  return 0;
1642  }
1643 
1644  require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1645  $contact = new Contact($this->db);
1646  $result = $contact->fetch($contactid);
1647  $this->contact = $contact;
1648  return $result;
1649  }
1650 
1651  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1658  public function fetch_thirdparty($force_thirdparty_id = 0)
1659  {
1660  // phpcs:enable
1661  global $conf;
1662 
1663  if (empty($this->socid) && empty($this->fk_soc) && empty($force_thirdparty_id)) {
1664  return 0;
1665  }
1666 
1667  require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1668 
1669  $idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : 0);
1670  if ($force_thirdparty_id) {
1671  $idtofetch = $force_thirdparty_id;
1672  }
1673 
1674  if ($idtofetch) {
1675  $thirdparty = new Societe($this->db);
1676  $result = $thirdparty->fetch($idtofetch);
1677  if ($result<0) {
1678  $this->errors=array_merge($this->errors, $thirdparty->errors);
1679  }
1680  $this->thirdparty = $thirdparty;
1681 
1682  // Use first price level if level not defined for third party
1683  if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
1684  $this->thirdparty->price_level = 1;
1685  }
1686 
1687  return $result;
1688  } else {
1689  return -1;
1690  }
1691  }
1692 
1693 
1701  public function fetchOneLike($ref)
1702  {
1703  if (!$this->table_ref_field) {
1704  return 0;
1705  }
1706 
1707  $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element." WHERE ".$this->table_ref_field." LIKE '".$this->db->escape($ref)."' LIMIT 1";
1708 
1709  $query = $this->db->query($sql);
1710 
1711  if (!$this->db->num_rows($query)) {
1712  return 0;
1713  }
1714 
1715  $result = $this->db->fetch_object($query);
1716 
1717  return $this->fetch($result->rowid);
1718  }
1719 
1720  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1728  public function fetch_barcode()
1729  {
1730  // phpcs:enable
1731  global $conf;
1732 
1733  dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1734 
1735  $idtype = $this->barcode_type;
1736  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
1737  if ($this->element == 'product' && !empty($conf->global->PRODUIT_DEFAULT_BARCODE_TYPE)) {
1738  $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1739  } elseif ($this->element == 'societe') {
1740  $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1741  } else {
1742  dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1743  }
1744  }
1745 
1746  if ($idtype > 0) {
1747  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
1748  $sql = "SELECT rowid, code, libelle as label, coder";
1749  $sql .= " FROM ".$this->db->prefix()."c_barcode_type";
1750  $sql .= " WHERE rowid = ".((int) $idtype);
1751  dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1752  $resql = $this->db->query($sql);
1753  if ($resql) {
1754  $obj = $this->db->fetch_object($resql);
1755  $this->barcode_type = $obj->rowid;
1756  $this->barcode_type_code = $obj->code;
1757  $this->barcode_type_label = $obj->label;
1758  $this->barcode_type_coder = $obj->coder;
1759  return 1;
1760  } else {
1761  dol_print_error($this->db);
1762  return -1;
1763  }
1764  }
1765  }
1766  return 0;
1767  }
1768 
1769  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1775  public function fetch_project()
1776  {
1777  // phpcs:enable
1778  return $this->fetch_projet();
1779  }
1780 
1781  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1787  public function fetch_projet()
1788  {
1789  // phpcs:enable
1790  include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1791 
1792  if (empty($this->fk_project) && !empty($this->fk_projet)) {
1793  $this->fk_project = $this->fk_projet; // For backward compatibility
1794  }
1795  if (empty($this->fk_project)) {
1796  return 0;
1797  }
1798 
1799  $project = new Project($this->db);
1800  $result = $project->fetch($this->fk_project);
1801 
1802  $this->projet = $project; // deprecated
1803  $this->project = $project;
1804  return $result;
1805  }
1806 
1807  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1813  public function fetch_product()
1814  {
1815  // phpcs:enable
1816  include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1817 
1818  if (empty($this->fk_product)) {
1819  return 0;
1820  }
1821 
1822  $product = new Product($this->db);
1823  $result = $product->fetch($this->fk_product);
1824 
1825  $this->product = $product;
1826  return $result;
1827  }
1828 
1829  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1836  public function fetch_user($userid)
1837  {
1838  // phpcs:enable
1839  $user = new User($this->db);
1840  $result = $user->fetch($userid);
1841  $this->user = $user;
1842  return $result;
1843  }
1844 
1845  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1851  public function fetch_origin()
1852  {
1853  // phpcs:enable
1854  if ($this->origin == 'shipping') {
1855  $this->origin = 'expedition';
1856  }
1857  if ($this->origin == 'delivery') {
1858  $this->origin = 'livraison';
1859  }
1860  if ($this->origin == 'order_supplier') {
1861  $this->origin = 'commandeFournisseur';
1862  }
1863 
1864  $origin = $this->origin;
1865 
1866  $classname = ucfirst($origin);
1867  $this->$origin = new $classname($this->db);
1868  $this->$origin->fetch($this->origin_id);
1869  }
1870 
1880  public function fetchObjectFrom($table, $field, $key, $element = null)
1881  {
1882  global $conf;
1883 
1884  $result = false;
1885 
1886  $sql = "SELECT rowid FROM ".$this->db->prefix().$table;
1887  $sql .= " WHERE ".$field." = '".$this->db->escape($key)."'";
1888  if (!empty($element)) {
1889  $sql .= " AND entity IN (".getEntity($element).")";
1890  } else {
1891  $sql .= " AND entity = ".((int) $conf->entity);
1892  }
1893 
1894  dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
1895  $resql = $this->db->query($sql);
1896  if ($resql) {
1897  $row = $this->db->fetch_row($resql);
1898  // Test for avoid error -1
1899  if ($row[0] > 0) {
1900  $result = $this->fetch($row[0]);
1901  }
1902  }
1903 
1904  return $result;
1905  }
1906 
1915  public function getValueFrom($table, $id, $field)
1916  {
1917  $result = false;
1918  if (!empty($id) && !empty($field) && !empty($table)) {
1919  $sql = "SELECT ".$field." FROM ".$this->db->prefix().$table;
1920  $sql .= " WHERE rowid = ".((int) $id);
1921 
1922  dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
1923  $resql = $this->db->query($sql);
1924  if ($resql) {
1925  $row = $this->db->fetch_row($resql);
1926  $result = $row[0];
1927  }
1928  }
1929  return $result;
1930  }
1931 
1948  public function setValueFrom($field, $value, $table = '', $id = null, $format = '', $id_field = '', $fuser = null, $trigkey = '', $fk_user_field = 'fk_user_modif')
1949  {
1950  global $user;
1951 
1952  if (empty($table)) {
1953  $table = $this->table_element;
1954  }
1955  if (empty($id)) {
1956  $id = $this->id;
1957  }
1958  if (empty($format)) {
1959  $format = 'text';
1960  }
1961  if (empty($id_field)) {
1962  $id_field = 'rowid';
1963  }
1964 
1965  // Special case
1966  if ($table == 'product' && $field == 'note_private') {
1967  $field = 'note';
1968  }
1969 
1970  if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
1971  $fk_user_field = 'fk_user_mod';
1972  }
1973  if (in_array($table, array('prelevement_bons'))) { // TODO Add a field fk_user_modif into llx_prelevement_bons
1974  $fk_user_field = '';
1975  }
1976 
1977  if ($trigkey) {
1978  $oldvalue = null;
1979 
1980  $sql = "SELECT " . $field;
1981  $sql .= " FROM " . MAIN_DB_PREFIX . $table;
1982  $sql .= " WHERE " . $id_field . " = " . ((int) $id);
1983 
1984  $resql = $this->db->query($sql);
1985  if ($resql) {
1986  if ($obj = $this->db->fetch_object($resql)) {
1987  if ($format == 'date') {
1988  $oldvalue = $this->db->jdate($obj->$field);
1989  } else {
1990  $oldvalue = $obj->$field;
1991  }
1992  }
1993  } else {
1994  $this->error = $this->db->lasterror();
1995  return -1;
1996  }
1997  }
1998 
1999  $error = 0;
2000 
2001  dol_syslog(__METHOD__, LOG_DEBUG);
2002 
2003  $this->db->begin();
2004 
2005  $sql = "UPDATE ".$this->db->prefix().$table." SET ";
2006 
2007  if ($format == 'text') {
2008  $sql .= $field." = '".$this->db->escape($value)."'";
2009  } elseif ($format == 'int') {
2010  $sql .= $field." = ".((int) $value);
2011  } elseif ($format == 'date') {
2012  $sql .= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
2013  } elseif ($format == 'dategmt') {
2014  $sql .= $field." = ".($value ? "'".$this->db->idate($value, 'gmt')."'" : "null");
2015  }
2016 
2017  if ($fk_user_field) {
2018  if (!empty($fuser) && is_object($fuser)) {
2019  $sql .= ", ".$fk_user_field." = ".((int) $fuser->id);
2020  } elseif (empty($fuser) || $fuser != 'none') {
2021  $sql .= ", ".$fk_user_field." = ".((int) $user->id);
2022  }
2023  }
2024 
2025  $sql .= " WHERE ".$id_field." = ".((int) $id);
2026 
2027  $resql = $this->db->query($sql);
2028  if ($resql) {
2029  if ($trigkey) {
2030  // call trigger with updated object values
2031  if (method_exists($this, 'fetch')) {
2032  $result = $this->fetch($id);
2033  } else {
2034  $result = $this->fetchCommon($id);
2035  }
2036  $this->oldcopy = clone $this;
2037  if (property_exists($this->oldcopy, $field)) {
2038  $this->oldcopy->$field = $oldvalue;
2039  }
2040 
2041  if ($result >= 0) {
2042  $result = $this->call_trigger($trigkey, (!empty($fuser) && is_object($fuser)) ? $fuser : $user); // This may set this->errors
2043  }
2044  if ($result < 0) {
2045  $error++;
2046  }
2047  }
2048 
2049  if (!$error) {
2050  if (property_exists($this, $field)) {
2051  $this->$field = $value;
2052  }
2053  $this->db->commit();
2054  return 1;
2055  } else {
2056  $this->db->rollback();
2057  return -2;
2058  }
2059  } else {
2060  if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2061  $this->error = 'DB_ERROR_RECORD_ALREADY_EXISTS';
2062  } else {
2063  $this->error = $this->db->lasterror();
2064  }
2065  $this->db->rollback();
2066  return -1;
2067  }
2068  }
2069 
2070  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2080  public function load_previous_next_ref($filter, $fieldid, $nodbprefix = 0)
2081  {
2082  // phpcs:enable
2083  global $conf, $user;
2084 
2085  if (!$this->table_element) {
2086  dol_print_error('', get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
2087  return -1;
2088  }
2089  if ($fieldid == 'none') {
2090  return 1;
2091  }
2092 
2093  // For backward compatibility
2094  if (in_array($this->table_element, array('facture_rec', 'facture_fourn_rec')) && $fieldid == 'title') {
2095  $fieldid = 'titre';
2096  }
2097 
2098  // Security on socid
2099  $socid = 0;
2100  if ($user->socid > 0) {
2101  $socid = $user->socid;
2102  }
2103 
2104  // this->ismultientitymanaged contains
2105  // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
2106  $aliastablesociete = 's';
2107  if ($this->element == 'societe') {
2108  $aliastablesociete = 'te'; // te as table_element
2109  }
2110  $restrictiononfksoc = empty($this->restrictiononfksoc) ? 0 : $this->restrictiononfksoc;
2111  $sql = "SELECT MAX(te.".$fieldid.")";
2112  $sql .= " FROM ".(empty($nodbprefix) ?$this->db->prefix():'').$this->table_element." as te";
2113  if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2114  $sql .= ",".$this->db->prefix()."usergroup_user as ug";
2115  }
2116  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2117  $tmparray = explode('@', $this->ismultientitymanaged);
2118  $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
2119  } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2120  $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
2121  } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2122  $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
2123  }
2124  if ($restrictiononfksoc && empty($user->rights->societe->client->voir) && !$socid) {
2125  $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
2126  }
2127  $sql .= " WHERE te.".$fieldid." < '".$this->db->escape($fieldid == 'rowid' ? $this->id : $this->ref)."'"; // ->ref must always be defined (set to id if field does not exists)
2128  if ($restrictiononfksoc == 1 && empty($user->rights->societe->client->voir) && !$socid) {
2129  $sql .= " AND sc.fk_user = ".((int) $user->id);
2130  }
2131  if ($restrictiononfksoc == 2 && empty($user->rights->societe->client->voir) && !$socid) {
2132  $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
2133  }
2134  if (!empty($filter)) {
2135  if (!preg_match('/^\s*AND/i', $filter)) {
2136  $sql .= " AND ";
2137  }
2138  $sql .= $filter;
2139  }
2140  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2141  $tmparray = explode('@', $this->ismultientitymanaged);
2142  $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
2143  } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2144  $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
2145  }
2146  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2147  if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2148  if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
2149  $sql .= " AND te.entity IS NOT NULL"; // Show all users
2150  } else {
2151  $sql .= " AND ug.fk_user = te.rowid";
2152  $sql .= " AND ug.entity IN (".getEntity('usergroup').")";
2153  }
2154  } else {
2155  $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
2156  }
2157  }
2158  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
2159  $tmparray = explode('@', $this->ismultientitymanaged);
2160  $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
2161  }
2162  if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
2163  $sql .= ' AND te.fk_soc = '.((int) $socid);
2164  }
2165  if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
2166  $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
2167  }
2168  if ($restrictiononfksoc && $socid && $this->element == 'societe') {
2169  $sql .= ' AND te.rowid = '.((int) $socid);
2170  }
2171  //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
2172 
2173  $result = $this->db->query($sql);
2174  if (!$result) {
2175  $this->error = $this->db->lasterror();
2176  return -1;
2177  }
2178  $row = $this->db->fetch_row($result);
2179  $this->ref_previous = $row[0];
2180 
2181  $sql = "SELECT MIN(te.".$fieldid.")";
2182  $sql .= " FROM ".(empty($nodbprefix) ?$this->db->prefix():'').$this->table_element." as te";
2183  if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2184  $sql .= ",".$this->db->prefix()."usergroup_user as ug";
2185  }
2186  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2187  $tmparray = explode('@', $this->ismultientitymanaged);
2188  $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
2189  } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2190  $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
2191  } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2192  $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
2193  }
2194  if ($restrictiononfksoc && empty($user->rights->societe->client->voir) && !$socid) {
2195  $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
2196  }
2197  $sql .= " WHERE te.".$fieldid." > '".$this->db->escape($fieldid == 'rowid' ? $this->id : $this->ref)."'"; // ->ref must always be defined (set to id if field does not exists)
2198  if ($restrictiononfksoc == 1 && empty($user->rights->societe->client->voir) && !$socid) {
2199  $sql .= " AND sc.fk_user = ".((int) $user->id);
2200  }
2201  if ($restrictiononfksoc == 2 && empty($user->rights->societe->client->voir) && !$socid) {
2202  $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
2203  }
2204  if (!empty($filter)) {
2205  if (!preg_match('/^\s*AND/i', $filter)) {
2206  $sql .= " AND "; // For backward compatibility
2207  }
2208  $sql .= $filter;
2209  }
2210  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2211  $tmparray = explode('@', $this->ismultientitymanaged);
2212  $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
2213  } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2214  $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
2215  }
2216  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2217  if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2218  if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
2219  $sql .= " AND te.entity IS NOT NULL"; // Show all users
2220  } else {
2221  $sql .= " AND ug.fk_user = te.rowid";
2222  $sql .= " AND ug.entity IN (".getEntity('usergroup').")";
2223  }
2224  } else {
2225  $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
2226  }
2227  }
2228  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
2229  $tmparray = explode('@', $this->ismultientitymanaged);
2230  $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
2231  }
2232  if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
2233  $sql .= ' AND te.fk_soc = '.((int) $socid);
2234  }
2235  if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
2236  $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
2237  }
2238  if ($restrictiononfksoc && $socid && $this->element == 'societe') {
2239  $sql .= ' AND te.rowid = '.((int) $socid);
2240  }
2241  //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
2242  // 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
2243 
2244  $result = $this->db->query($sql);
2245  if (!$result) {
2246  $this->error = $this->db->lasterror();
2247  return -2;
2248  }
2249  $row = $this->db->fetch_row($result);
2250  $this->ref_next = $row[0];
2251 
2252  return 1;
2253  }
2254 
2255 
2263  public function getListContactId($source = 'external')
2264  {
2265  $contactAlreadySelected = array();
2266  $tab = $this->liste_contact(-1, $source);
2267  $num = count($tab);
2268  $i = 0;
2269  while ($i < $num) {
2270  if ($source == 'thirdparty') {
2271  $contactAlreadySelected[$i] = $tab[$i]['socid'];
2272  } else {
2273  $contactAlreadySelected[$i] = $tab[$i]['id'];
2274  }
2275  $i++;
2276  }
2277  return $contactAlreadySelected;
2278  }
2279 
2280 
2288  public function setProject($projectid, $notrigger = 0)
2289  {
2290  global $user;
2291  $error = 0;
2292 
2293  if (!$this->table_element) {
2294  dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined", LOG_ERR);
2295  return -1;
2296  }
2297 
2298  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2299  if (!empty($this->fields['fk_project'])) { // Common case
2300  if ($projectid) {
2301  $sql .= " SET fk_project = ".((int) $projectid);
2302  } else {
2303  $sql .= " SET fk_project = NULL";
2304  }
2305  $sql .= ' WHERE rowid = '.((int) $this->id);
2306  } elseif ($this->table_element == 'actioncomm') { // Special case for actioncomm
2307  if ($projectid) {
2308  $sql .= " SET fk_project = ".((int) $projectid);
2309  } else {
2310  $sql .= " SET fk_project = NULL";
2311  }
2312  $sql .= ' WHERE id = '.((int) $this->id);
2313  } else // Special case for old architecture objects
2314  {
2315  if ($projectid) {
2316  $sql .= ' SET fk_projet = '.((int) $projectid);
2317  } else {
2318  $sql .= ' SET fk_projet = NULL';
2319  }
2320  $sql .= " WHERE rowid = ".((int) $this->id);
2321  }
2322 
2323  $this->db->begin();
2324 
2325  dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
2326  if ($this->db->query($sql)) {
2327  $this->fk_project = ((int) $projectid);
2328  } else {
2329  dol_print_error($this->db);
2330  $error++;
2331  }
2332 
2333  // Triggers
2334  if (!$error && !$notrigger) {
2335  // Call triggers
2336  $result = $this->call_trigger(strtoupper($this->element) . '_MODIFY', $user);
2337  if ($result < 0) {
2338  $error++;
2339  } //Do also here what you must do to rollback action if trigger fail
2340  // End call triggers
2341  }
2342 
2343  // Commit or rollback
2344  if ($error) {
2345  $this->db->rollback();
2346  return -1;
2347  } else {
2348  $this->db->commit();
2349  return 1;
2350  }
2351  }
2352 
2359  public function setPaymentMethods($id)
2360  {
2361  global $user;
2362 
2363  $error = 0; $notrigger = 0;
2364 
2365  dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
2366 
2367  if ($this->statut >= 0 || $this->element == 'societe') {
2368  // TODO uniformize field name
2369  $fieldname = 'fk_mode_reglement';
2370  if ($this->element == 'societe') {
2371  $fieldname = 'mode_reglement';
2372  }
2373  if (get_class($this) == 'Fournisseur') {
2374  $fieldname = 'mode_reglement_supplier';
2375  }
2376  if (get_class($this) == 'Tva') {
2377  $fieldname = 'fk_typepayment';
2378  }
2379  if (get_class($this) == 'Salary') {
2380  $fieldname = 'fk_typepayment';
2381  }
2382 
2383  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2384  $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2385  $sql .= ' WHERE rowid='.((int) $this->id);
2386 
2387  if ($this->db->query($sql)) {
2388  $this->mode_reglement_id = $id;
2389  // for supplier
2390  if (get_class($this) == 'Fournisseur') {
2391  $this->mode_reglement_supplier_id = $id;
2392  }
2393  // Triggers
2394  if (!$error && !$notrigger) {
2395  // Call triggers
2396  if (get_class($this) == 'Commande') {
2397  $result = $this->call_trigger('ORDER_MODIFY', $user);
2398  } else {
2399  $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
2400  }
2401  if ($result < 0) {
2402  $error++;
2403  }
2404  // End call triggers
2405  }
2406  return 1;
2407  } else {
2408  dol_syslog(get_class($this).'::setPaymentMethods Error '.$this->db->error());
2409  $this->error = $this->db->error();
2410  return -1;
2411  }
2412  } else {
2413  dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
2414  $this->error = 'Status of the object is incompatible '.$this->statut;
2415  return -2;
2416  }
2417  }
2418 
2425  public function setMulticurrencyCode($code)
2426  {
2427  dol_syslog(get_class($this).'::setMulticurrencyCode('.$code.')');
2428  if ($this->statut >= 0 || $this->element == 'societe') {
2429  $fieldname = 'multicurrency_code';
2430 
2431  $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2432  $sql .= " SET ".$fieldname." = '".$this->db->escape($code)."'";
2433  $sql .= ' WHERE rowid='.((int) $this->id);
2434 
2435  if ($this->db->query($sql)) {
2436  $this->multicurrency_code = $code;
2437 
2438  list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
2439  if ($rate) {
2440  $this->setMulticurrencyRate($rate, 2);
2441  }
2442 
2443  return 1;
2444  } else {
2445  dol_syslog(get_class($this).'::setMulticurrencyCode Error '.$sql.' - '.$this->db->error());
2446  $this->error = $this->db->error();
2447  return -1;
2448  }
2449  } else {
2450  dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
2451  $this->error = 'Status of the object is incompatible '.$this->statut;
2452  return -2;
2453  }
2454  }
2455 
2463  public function setMulticurrencyRate($rate, $mode = 1)
2464  {
2465  dol_syslog(get_class($this).'::setMulticurrencyRate('.$rate.','.$mode.')');
2466  if ($this->statut >= 0 || $this->element == 'societe') {
2467  $fieldname = 'multicurrency_tx';
2468 
2469  $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2470  $sql .= " SET ".$fieldname." = ".((float) $rate);
2471  $sql .= ' WHERE rowid='.((int) $this->id);
2472 
2473  if ($this->db->query($sql)) {
2474  $this->multicurrency_tx = $rate;
2475 
2476  // Update line price
2477  if (!empty($this->lines)) {
2478  foreach ($this->lines as &$line) {
2479  // Amounts in company currency will be recalculated
2480  if ($mode == 1) {
2481  $line->subprice = 0;
2482  }
2483 
2484  // Amounts in foreign currency will be recalculated
2485  if ($mode == 2) {
2486  $line->multicurrency_subprice = 0;
2487  }
2488 
2489  switch ($this->element) {
2490  case 'propal':
2493  $this->updateline(
2494  $line->id,
2495  $line->subprice,
2496  $line->qty,
2497  $line->remise_percent,
2498  $line->tva_tx,
2499  $line->localtax1_tx,
2500  $line->localtax2_tx,
2501  ($line->description ? $line->description : $line->desc),
2502  'HT',
2503  $line->info_bits,
2504  $line->special_code,
2505  $line->fk_parent_line,
2506  $line->skip_update_total,
2507  $line->fk_fournprice,
2508  $line->pa_ht,
2509  $line->label,
2510  $line->product_type,
2511  $line->date_start,
2512  $line->date_end,
2513  $line->array_options,
2514  $line->fk_unit,
2515  $line->multicurrency_subprice
2516  );
2517  break;
2518  case 'commande':
2521  $this->updateline(
2522  $line->id,
2523  ($line->description ? $line->description : $line->desc),
2524  $line->subprice,
2525  $line->qty,
2526  $line->remise_percent,
2527  $line->tva_tx,
2528  $line->localtax1_tx,
2529  $line->localtax2_tx,
2530  'HT',
2531  $line->info_bits,
2532  $line->date_start,
2533  $line->date_end,
2534  $line->product_type,
2535  $line->fk_parent_line,
2536  $line->skip_update_total,
2537  $line->fk_fournprice,
2538  $line->pa_ht,
2539  $line->label,
2540  $line->special_code,
2541  $line->array_options,
2542  $line->fk_unit,
2543  $line->multicurrency_subprice
2544  );
2545  break;
2546  case 'facture':
2549  $this->updateline(
2550  $line->id,
2551  ($line->description ? $line->description : $line->desc),
2552  $line->subprice,
2553  $line->qty,
2554  $line->remise_percent,
2555  $line->date_start,
2556  $line->date_end,
2557  $line->tva_tx,
2558  $line->localtax1_tx,
2559  $line->localtax2_tx,
2560  'HT',
2561  $line->info_bits,
2562  $line->product_type,
2563  $line->fk_parent_line,
2564  $line->skip_update_total,
2565  $line->fk_fournprice,
2566  $line->pa_ht,
2567  $line->label,
2568  $line->special_code,
2569  $line->array_options,
2570  $line->situation_percent,
2571  $line->fk_unit,
2572  $line->multicurrency_subprice
2573  );
2574  break;
2575  case 'supplier_proposal':
2578  $this->updateline(
2579  $line->id,
2580  $line->subprice,
2581  $line->qty,
2582  $line->remise_percent,
2583  $line->tva_tx,
2584  $line->localtax1_tx,
2585  $line->localtax2_tx,
2586  ($line->description ? $line->description : $line->desc),
2587  'HT',
2588  $line->info_bits,
2589  $line->special_code,
2590  $line->fk_parent_line,
2591  $line->skip_update_total,
2592  $line->fk_fournprice,
2593  $line->pa_ht,
2594  $line->label,
2595  $line->product_type,
2596  $line->array_options,
2597  $line->ref_fourn,
2598  $line->multicurrency_subprice
2599  );
2600  break;
2601  case 'order_supplier':
2604  $this->updateline(
2605  $line->id,
2606  ($line->description ? $line->description : $line->desc),
2607  $line->subprice,
2608  $line->qty,
2609  $line->remise_percent,
2610  $line->tva_tx,
2611  $line->localtax1_tx,
2612  $line->localtax2_tx,
2613  'HT',
2614  $line->info_bits,
2615  $line->product_type,
2616  false,
2617  $line->date_start,
2618  $line->date_end,
2619  $line->array_options,
2620  $line->fk_unit,
2621  $line->multicurrency_subprice,
2622  $line->ref_supplier
2623  );
2624  break;
2625  case 'invoice_supplier':
2628  $this->updateline(
2629  $line->id,
2630  ($line->description ? $line->description : $line->desc),
2631  $line->subprice,
2632  $line->tva_tx,
2633  $line->localtax1_tx,
2634  $line->localtax2_tx,
2635  $line->qty,
2636  0,
2637  'HT',
2638  $line->info_bits,
2639  $line->product_type,
2640  $line->remise_percent,
2641  false,
2642  $line->date_start,
2643  $line->date_end,
2644  $line->array_options,
2645  $line->fk_unit,
2646  $line->multicurrency_subprice,
2647  $line->ref_supplier
2648  );
2649  break;
2650  default:
2651  dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
2652  break;
2653  }
2654  }
2655  }
2656 
2657  return 1;
2658  } else {
2659  dol_syslog(get_class($this).'::setMulticurrencyRate Error '.$sql.' - '.$this->db->error());
2660  $this->error = $this->db->error();
2661  return -1;
2662  }
2663  } else {
2664  dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
2665  $this->error = 'Status of the object is incompatible '.$this->statut;
2666  return -2;
2667  }
2668  }
2669 
2677  public function setPaymentTerms($id, $deposit_percent = null)
2678  {
2679  dol_syslog(get_class($this).'::setPaymentTerms('.$id.', '.var_export($deposit_percent, true).')');
2680  if ($this->statut >= 0 || $this->element == 'societe') {
2681  // TODO uniformize field name
2682  $fieldname = 'fk_cond_reglement';
2683  if ($this->element == 'societe') {
2684  $fieldname = 'cond_reglement';
2685  }
2686  if (get_class($this) == 'Fournisseur') {
2687  $fieldname = 'cond_reglement_supplier';
2688  }
2689 
2690  if (empty($deposit_percent) || $deposit_percent < 0) {
2691  $deposit_percent = getDictionaryValue('c_payment_term', 'deposit_percent', $id);
2692  }
2693 
2694  if ($deposit_percent > 100) {
2695  $deposit_percent = 100;
2696  }
2697 
2698  $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2699  $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2700  if (in_array($this->table_element, array('propal', 'commande', 'societe'))) {
2701  $sql .= " , deposit_percent = " . (empty($deposit_percent) ? 'NULL' : "'".$this->db->escape($deposit_percent)."'");
2702  }
2703  $sql .= ' WHERE rowid='.((int) $this->id);
2704 
2705  if ($this->db->query($sql)) {
2706  $this->cond_reglement_id = $id;
2707  // for supplier
2708  if (get_class($this) == 'Fournisseur') {
2709  $this->cond_reglement_supplier_id = $id;
2710  }
2711  $this->cond_reglement = $id; // for compatibility
2712  $this->deposit_percent = $deposit_percent;
2713  return 1;
2714  } else {
2715  dol_syslog(get_class($this).'::setPaymentTerms Error '.$sql.' - '.$this->db->error());
2716  $this->error = $this->db->error();
2717  return -1;
2718  }
2719  } else {
2720  dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
2721  $this->error = 'Status of the object is incompatible '.$this->statut;
2722  return -2;
2723  }
2724  }
2725 
2732  public function setTransportMode($id)
2733  {
2734  dol_syslog(get_class($this).'::setTransportMode('.$id.')');
2735  if ($this->statut >= 0 || $this->element == 'societe') {
2736  $fieldname = 'fk_transport_mode';
2737  if ($this->element == 'societe') {
2738  $fieldname = 'transport_mode';
2739  }
2740  if (get_class($this) == 'Fournisseur') {
2741  $fieldname = 'transport_mode_supplier';
2742  }
2743 
2744  $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2745  $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2746  $sql .= ' WHERE rowid='.((int) $this->id);
2747 
2748  if ($this->db->query($sql)) {
2749  $this->transport_mode_id = $id;
2750  // for supplier
2751  if (get_class($this) == 'Fournisseur') {
2752  $this->transport_mode_supplier_id = $id;
2753  }
2754  return 1;
2755  } else {
2756  dol_syslog(get_class($this).'::setTransportMode Error '.$sql.' - '.$this->db->error());
2757  $this->error = $this->db->error();
2758  return -1;
2759  }
2760  } else {
2761  dol_syslog(get_class($this).'::setTransportMode, status of the object is incompatible');
2762  $this->error = 'Status of the object is incompatible '.$this->statut;
2763  return -2;
2764  }
2765  }
2766 
2773  public function setRetainedWarrantyPaymentTerms($id)
2774  {
2775  dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms('.$id.')');
2776  if ($this->statut >= 0 || $this->element == 'societe') {
2777  $fieldname = 'retained_warranty_fk_cond_reglement';
2778 
2779  $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2780  $sql .= " SET ".$fieldname." = ".((int) $id);
2781  $sql .= ' WHERE rowid='.((int) $this->id);
2782 
2783  if ($this->db->query($sql)) {
2784  $this->retained_warranty_fk_cond_reglement = $id;
2785  return 1;
2786  } else {
2787  dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms Error '.$sql.' - '.$this->db->error());
2788  $this->error = $this->db->error();
2789  return -1;
2790  }
2791  } else {
2792  dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms, status of the object is incompatible');
2793  $this->error = 'Status of the object is incompatible '.$this->statut;
2794  return -2;
2795  }
2796  }
2797 
2805  public function setDeliveryAddress($id)
2806  {
2807  $fieldname = 'fk_delivery_address';
2808  if ($this->element == 'delivery' || $this->element == 'shipping') {
2809  $fieldname = 'fk_address';
2810  }
2811 
2812  $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ".$fieldname." = ".((int) $id);
2813  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
2814 
2815  if ($this->db->query($sql)) {
2816  $this->fk_delivery_address = $id;
2817  return 1;
2818  } else {
2819  $this->error = $this->db->error();
2820  dol_syslog(get_class($this).'::setDeliveryAddress Error '.$this->error);
2821  return -1;
2822  }
2823  }
2824 
2825 
2835  public function setShippingMethod($shipping_method_id, $notrigger = false, $userused = null)
2836  {
2837  global $user;
2838 
2839  if (empty($userused)) {
2840  $userused = $user;
2841  }
2842 
2843  $error = 0;
2844 
2845  if (!$this->table_element) {
2846  dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined", LOG_ERR);
2847  return -1;
2848  }
2849 
2850  $this->db->begin();
2851 
2852  if ($shipping_method_id < 0) {
2853  $shipping_method_id = 'NULL';
2854  }
2855  dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
2856 
2857  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2858  $sql .= " SET fk_shipping_method = ".((int) $shipping_method_id);
2859  $sql .= " WHERE rowid=".((int) $this->id);
2860  $resql = $this->db->query($sql);
2861  if (!$resql) {
2862  dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
2863  $this->error = $this->db->lasterror();
2864  $error++;
2865  } else {
2866  if (!$notrigger) {
2867  // Call trigger
2868  $this->context = array('shippingmethodupdate'=>1);
2869  $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused);
2870  if ($result < 0) {
2871  $error++;
2872  }
2873  // End call trigger
2874  }
2875  }
2876  if ($error) {
2877  $this->db->rollback();
2878  return -1;
2879  } else {
2880  $this->shipping_method_id = ($shipping_method_id == 'NULL') ?null:$shipping_method_id;
2881  $this->db->commit();
2882  return 1;
2883  }
2884  }
2885 
2886 
2893  public function setWarehouse($warehouse_id)
2894  {
2895  if (!$this->table_element) {
2896  dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined", LOG_ERR);
2897  return -1;
2898  }
2899  if ($warehouse_id < 0) {
2900  $warehouse_id = 'NULL';
2901  }
2902  dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
2903 
2904  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2905  $sql .= " SET fk_warehouse = ".((int) $warehouse_id);
2906  $sql .= " WHERE rowid=".((int) $this->id);
2907 
2908  if ($this->db->query($sql)) {
2909  $this->warehouse_id = ($warehouse_id == 'NULL') ?null:$warehouse_id;
2910  return 1;
2911  } else {
2912  dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
2913  $this->error = $this->db->error();
2914  return 0;
2915  }
2916  }
2917 
2918 
2926  public function setDocModel($user, $modelpdf)
2927  {
2928  if (!$this->table_element) {
2929  dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined", LOG_ERR);
2930  return -1;
2931  }
2932 
2933  $newmodelpdf = dol_trunc($modelpdf, 255);
2934 
2935  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2936  $sql .= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
2937  $sql .= " WHERE rowid = ".((int) $this->id);
2938 
2939  dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
2940  $resql = $this->db->query($sql);
2941  if ($resql) {
2942  $this->model_pdf = $modelpdf;
2943  $this->modelpdf = $modelpdf; // For bakward compatibility
2944  return 1;
2945  } else {
2946  dol_print_error($this->db);
2947  return 0;
2948  }
2949  }
2950 
2951 
2960  public function setBankAccount($fk_account, $notrigger = false, $userused = null)
2961  {
2962  global $user;
2963 
2964  if (empty($userused)) {
2965  $userused = $user;
2966  }
2967 
2968  $error = 0;
2969 
2970  if (!$this->table_element) {
2971  dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined", LOG_ERR);
2972  return -1;
2973  }
2974  $this->db->begin();
2975 
2976  if ($fk_account < 0) {
2977  $fk_account = 'NULL';
2978  }
2979  dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
2980 
2981  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2982  $sql .= " SET fk_account = ".((int) $fk_account);
2983  $sql .= " WHERE rowid=".((int) $this->id);
2984 
2985  $resql = $this->db->query($sql);
2986  if (!$resql) {
2987  dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
2988  $this->error = $this->db->lasterror();
2989  $error++;
2990  } else {
2991  if (!$notrigger) {
2992  // Call trigger
2993  $this->context = array('bankaccountupdate'=>1);
2994  $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused);
2995  if ($result < 0) {
2996  $error++;
2997  }
2998  // End call trigger
2999  }
3000  }
3001  if ($error) {
3002  $this->db->rollback();
3003  return -1;
3004  } else {
3005  $this->fk_account = ($fk_account == 'NULL') ?null:$fk_account;
3006  $this->db->commit();
3007  return 1;
3008  }
3009  }
3010 
3011 
3012  // TODO: Move line related operations to CommonObjectLine?
3013 
3014  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3024  public function line_order($renum = false, $rowidorder = 'ASC', $fk_parent_line = true)
3025  {
3026  // phpcs:enable
3027  if (!$this->table_element_line) {
3028  dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined", LOG_ERR);
3029  return -1;
3030  }
3031  if (!$this->fk_element) {
3032  dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined", LOG_ERR);
3033  return -1;
3034  }
3035 
3036  $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3037  if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3038  $fieldposition = 'position';
3039  }
3040 
3041  // Count number of lines to reorder (according to choice $renum)
3042  $nl = 0;
3043  $sql = "SELECT count(rowid) FROM ".$this->db->prefix().$this->table_element_line;
3044  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3045  if (!$renum) {
3046  $sql .= " AND " . $fieldposition . " = 0";
3047  }
3048  if ($renum) {
3049  $sql .= " AND " . $fieldposition . " <> 0";
3050  }
3051 
3052  dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
3053  $resql = $this->db->query($sql);
3054  if ($resql) {
3055  $row = $this->db->fetch_row($resql);
3056  $nl = $row[0];
3057  } else {
3058  dol_print_error($this->db);
3059  }
3060  if ($nl > 0) {
3061  // The goal of this part is to reorder all lines, with all children lines sharing the same counter that parents.
3062  $rows = array();
3063 
3064  // We first search all lines that are parent lines (for multilevel details lines)
3065  $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3066  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3067  if ($fk_parent_line) {
3068  $sql .= ' AND fk_parent_line IS NULL';
3069  }
3070  $sql .= " ORDER BY " . $fieldposition . " ASC, rowid " . $rowidorder;
3071 
3072  dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
3073  $resql = $this->db->query($sql);
3074  if ($resql) {
3075  $i = 0;
3076  $num = $this->db->num_rows($resql);
3077  while ($i < $num) {
3078  $row = $this->db->fetch_row($resql);
3079  $rows[] = $row[0]; // Add parent line into array rows
3080  $childrens = $this->getChildrenOfLine($row[0]);
3081  if (!empty($childrens)) {
3082  foreach ($childrens as $child) {
3083  array_push($rows, $child);
3084  }
3085  }
3086  $i++;
3087  }
3088 
3089  // Now we set a new number for each lines (parent and children with children included into parent tree)
3090  if (!empty($rows)) {
3091  foreach ($rows as $key => $row) {
3092  $this->updateRangOfLine($row, ($key + 1));
3093  }
3094  }
3095  } else {
3096  dol_print_error($this->db);
3097  }
3098  }
3099  return 1;
3100  }
3101 
3109  public function getChildrenOfLine($id, $includealltree = 0)
3110  {
3111  $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3112  if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3113  $fieldposition = 'position';
3114  }
3115 
3116  $rows = array();
3117 
3118  $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3119  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3120  $sql .= ' AND fk_parent_line = '.((int) $id);
3121  $sql .= " ORDER BY " . $fieldposition . " ASC";
3122 
3123  dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id, LOG_DEBUG);
3124  $resql = $this->db->query($sql);
3125  if ($resql) {
3126  if ($this->db->num_rows($resql) > 0) {
3127  while ($row = $this->db->fetch_row($resql)) {
3128  $rows[] = $row[0];
3129  if (!empty($includealltree)) {
3130  $rows = array_merge($rows, $this->getChildrenOfLine($row[0]), $includealltree);
3131  }
3132  }
3133  }
3134  }
3135  return $rows;
3136  }
3137 
3138  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3146  public function line_up($rowid, $fk_parent_line = true)
3147  {
3148  // phpcs:enable
3149  $this->line_order(false, 'ASC', $fk_parent_line);
3150 
3151  // Get rang of line
3152  $rang = $this->getRangOfLine($rowid);
3153 
3154  // Update position of line
3155  $this->updateLineUp($rowid, $rang);
3156  }
3157 
3158  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3166  public function line_down($rowid, $fk_parent_line = true)
3167  {
3168  // phpcs:enable
3169  $this->line_order(false, 'ASC', $fk_parent_line);
3170 
3171  // Get rang of line
3172  $rang = $this->getRangOfLine($rowid);
3173 
3174  // Get max value for rang
3175  $max = $this->line_max();
3176 
3177  // Update position of line
3178  $this->updateLineDown($rowid, $rang, $max);
3179  }
3180 
3188  public function updateRangOfLine($rowid, $rang)
3189  {
3190  global $hookmanager;
3191  $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3192  if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3193  $fieldposition = 'position';
3194  }
3195 
3196  $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3197  $sql .= ' WHERE rowid = '.((int) $rowid);
3198 
3199  dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
3200  if (!$this->db->query($sql)) {
3201  dol_print_error($this->db);
3202  return -1;
3203  } else {
3204  $parameters=array('rowid'=>$rowid, 'rang'=>$rang, 'fieldposition' => $fieldposition);
3205  $action='';
3206  $reshook = $hookmanager->executeHooks('afterRankOfLineUpdate', $parameters, $this, $action);
3207  return 1;
3208  }
3209  }
3210 
3211  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3218  public function line_ajaxorder($rows)
3219  {
3220  // phpcs:enable
3221  $num = count($rows);
3222  for ($i = 0; $i < $num; $i++) {
3223  $this->updateRangOfLine($rows[$i], ($i + 1));
3224  }
3225  }
3226 
3234  public function updateLineUp($rowid, $rang)
3235  {
3236  if ($rang > 1) {
3237  $fieldposition = 'rang';
3238  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3239  $fieldposition = 'position';
3240  }
3241 
3242  $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3243  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3244  $sql .= " AND " . $fieldposition . " = " . ((int) ($rang - 1));
3245  if ($this->db->query($sql)) {
3246  $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang - 1));
3247  $sql .= ' WHERE rowid = '.((int) $rowid);
3248  if (!$this->db->query($sql)) {
3249  dol_print_error($this->db);
3250  }
3251  } else {
3252  dol_print_error($this->db);
3253  }
3254  }
3255  }
3256 
3265  public function updateLineDown($rowid, $rang, $max)
3266  {
3267  if ($rang < $max) {
3268  $fieldposition = 'rang';
3269  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3270  $fieldposition = 'position';
3271  }
3272 
3273  $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3274  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3275  $sql .= " AND " . $fieldposition . " = " . ((int) ($rang + 1));
3276  if ($this->db->query($sql)) {
3277  $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang + 1));
3278  $sql .= ' WHERE rowid = '.((int) $rowid);
3279  if (!$this->db->query($sql)) {
3280  dol_print_error($this->db);
3281  }
3282  } else {
3283  dol_print_error($this->db);
3284  }
3285  }
3286  }
3287 
3294  public function getRangOfLine($rowid)
3295  {
3296  $fieldposition = 'rang';
3297  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3298  $fieldposition = 'position';
3299  }
3300 
3301  $sql = "SELECT " . $fieldposition . " FROM ".$this->db->prefix().$this->table_element_line;
3302  $sql .= " WHERE rowid = ".((int) $rowid);
3303 
3304  dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
3305  $resql = $this->db->query($sql);
3306  if ($resql) {
3307  $row = $this->db->fetch_row($resql);
3308  return $row[0];
3309  }
3310 
3311  return 0;
3312  }
3313 
3320  public function getIdOfLine($rang)
3321  {
3322  $fieldposition = 'rang';
3323  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3324  $fieldposition = 'position';
3325  }
3326 
3327  $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3328  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3329  $sql .= " AND " . $fieldposition . " = ".((int) $rang);
3330  $resql = $this->db->query($sql);
3331  if ($resql) {
3332  $row = $this->db->fetch_row($resql);
3333  return $row[0];
3334  }
3335 
3336  return 0;
3337  }
3338 
3339  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3346  public function line_max($fk_parent_line = 0)
3347  {
3348  // phpcs:enable
3349  $positionfield = 'rang';
3350  if (in_array($this->table_element, array('bom_bom', 'product_attribute'))) {
3351  $positionfield = 'position';
3352  }
3353 
3354  // Search the last rang with fk_parent_line
3355  if ($fk_parent_line) {
3356  $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
3357  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3358  $sql .= " AND fk_parent_line = ".((int) $fk_parent_line);
3359 
3360  dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
3361  $resql = $this->db->query($sql);
3362  if ($resql) {
3363  $row = $this->db->fetch_row($resql);
3364  if (!empty($row[0])) {
3365  return $row[0];
3366  } else {
3367  return $this->getRangOfLine($fk_parent_line);
3368  }
3369  }
3370  } else {
3371  // If not, search the last rang of element
3372  $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
3373  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3374 
3375  dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
3376  $resql = $this->db->query($sql);
3377  if ($resql) {
3378  $row = $this->db->fetch_row($resql);
3379  return $row[0];
3380  }
3381  }
3382 
3383  return 0;
3384  }
3385 
3386  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3393  public function update_ref_ext($ref_ext)
3394  {
3395  // phpcs:enable
3396  if (!$this->table_element) {
3397  dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
3398  return -1;
3399  }
3400 
3401  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3402  $sql .= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
3403  $sql .= " WHERE ".(isset($this->table_rowid) ? $this->table_rowid : 'rowid')." = ".((int) $this->id);
3404 
3405  dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
3406  if ($this->db->query($sql)) {
3407  $this->ref_ext = $ref_ext;
3408  return 1;
3409  } else {
3410  $this->error = $this->db->error();
3411  return -1;
3412  }
3413  }
3414 
3415  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3424  public function update_note($note, $suffix = '', $notrigger = 0)
3425  {
3426  // phpcs:enable
3427  global $user;
3428 
3429  if (!$this->table_element) {
3430  $this->error = 'update_note was called on objet with property table_element not defined';
3431  dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
3432  return -1;
3433  }
3434  if (!in_array($suffix, array('', '_public', '_private'))) {
3435  $this->error = 'update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
3436  dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
3437  return -2;
3438  }
3439 
3440  $newsuffix = $suffix;
3441 
3442  // Special cas
3443  if ($this->table_element == 'product' && $newsuffix == '_private') {
3444  $newsuffix = '';
3445  }
3446  if (in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
3447  $fieldusermod = "fk_user_mod";
3448  } elseif ($this->table_element == 'ecm_files') {
3449  $fieldusermod = "fk_user_m";
3450  } else {
3451  $fieldusermod = "fk_user_modif";
3452  }
3453  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3454  $sql .= " SET note".$newsuffix." = ".(!empty($note) ? ("'".$this->db->escape($note)."'") : "NULL");
3455  $sql .= ", ".$fieldusermod." = ".((int) $user->id);
3456  $sql .= " WHERE rowid = ".((int) $this->id);
3457 
3458  dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
3459  if ($this->db->query($sql)) {
3460  if ($suffix == '_public') {
3461  $this->note_public = $note;
3462  } elseif ($suffix == '_private') {
3463  $this->note_private = $note;
3464  } else {
3465  $this->note = $note; // deprecated
3466  $this->note_private = $note;
3467  }
3468  if (empty($notrigger)) {
3469  switch ($this->element) {
3470  case 'societe':
3471  $trigger_name = 'COMPANY_MODIFY';
3472  break;
3473  case 'commande':
3474  $trigger_name = 'ORDER_MODIFY';
3475  break;
3476  case 'facture':
3477  $trigger_name = 'BILL_MODIFY';
3478  break;
3479  case 'invoice_supplier':
3480  $trigger_name = 'BILL_SUPPLIER_MODIFY';
3481  break;
3482  case 'facturerec':
3483  $trigger_name = 'BILLREC_MODIFIY';
3484  break;
3485  case 'expensereport':
3486  $trigger_name = 'EXPENSE_REPORT_MODIFY';
3487  break;
3488  default:
3489  $trigger_name = strtoupper($this->element) . '_MODIFY';
3490  }
3491  $ret = $this->call_trigger($trigger_name, $user);
3492  if ($ret < 0) {
3493  return -1;
3494  }
3495  }
3496  return 1;
3497  } else {
3498  $this->error = $this->db->lasterror();
3499  return -1;
3500  }
3501  }
3502 
3503  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3512  public function update_note_public($note)
3513  {
3514  // phpcs:enable
3515  return $this->update_note($note, '_public');
3516  }
3517 
3518  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3529  public function update_price($exclspec = 0, $roundingadjust = 'none', $nodatabaseupdate = 0, $seller = null)
3530  {
3531  // phpcs:enable
3532  global $conf, $hookmanager, $action;
3533 
3534  $parameters = array('exclspec' => $exclspec, 'roundingadjust' => $roundingadjust, 'nodatabaseupdate' => $nodatabaseupdate, 'seller' => $seller);
3535  $reshook = $hookmanager->executeHooks('updateTotalPrice', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3536  if ($reshook > 0) {
3537  return 1; // replacement code
3538  } elseif ($reshook < 0) {
3539  return -1; // failure
3540  } // reshook = 0 => execute normal code
3541 
3542  // Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
3543  $MODULE = "";
3544  if ($this->element == 'propal') {
3545  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
3546  } elseif ($this->element == 'commande' || $this->element == 'order') {
3547  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
3548  } elseif ($this->element == 'facture' || $this->element == 'invoice') {
3549  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
3550  } elseif ($this->element == 'facture_fourn' || $this->element == 'supplier_invoice' || $this->element == 'invoice_supplier' || $this->element == 'invoice_supplier_rec') {
3551  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
3552  } elseif ($this->element == 'order_supplier' || $this->element == 'supplier_order') {
3553  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
3554  } elseif ($this->element == 'supplier_proposal') {
3555  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
3556  }
3557 
3558  if (!empty($MODULE)) {
3559  if (!empty($conf->global->$MODULE)) {
3560  $modsactivated = explode(',', $conf->global->$MODULE);
3561  foreach ($modsactivated as $mod) {
3562  if (isModEnabled($mod)) {
3563  return 1; // update was disabled by specific setup
3564  }
3565  }
3566  }
3567  }
3568 
3569  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3570 
3571  $forcedroundingmode = $roundingadjust;
3572  if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) {
3573  $forcedroundingmode = getDolGlobalString('MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND');
3574  } elseif ($forcedroundingmode == 'auto') {
3575  $forcedroundingmode = '0';
3576  }
3577 
3578  $error = 0;
3579 
3580  $multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
3581 
3582  // Define constants to find lines to sum (field name int the table_element_line not into table_element)
3583  $fieldtva = 'total_tva';
3584  $fieldlocaltax1 = 'total_localtax1';
3585  $fieldlocaltax2 = 'total_localtax2';
3586  $fieldup = 'subprice';
3587  if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
3588  $fieldtva = 'tva';
3589  $fieldup = 'pu_ht';
3590  }
3591  if ($this->element == 'invoice_supplier_rec') {
3592  $fieldup = 'pu_ht';
3593  }
3594  if ($this->element == 'expensereport') {
3595  $fieldup = 'value_unit';
3596  }
3597 
3598  $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,";
3599  $sql .= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
3600  if ($this->table_element_line == 'facturedet') {
3601  $sql .= ', situation_percent';
3602  }
3603  $sql .= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
3604  $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
3605  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3606  if ($exclspec) {
3607  $product_field = 'product_type';
3608  if ($this->table_element_line == 'contratdet') {
3609  $product_field = ''; // contratdet table has no product_type field
3610  }
3611  if ($product_field) {
3612  $sql .= " AND ".$product_field." <> 9";
3613  }
3614  }
3615  $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
3616 
3617  dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3618 
3619  $resql = $this->db->query($sql);
3620  if ($resql) {
3621  $this->total_ht = 0;
3622  $this->total_tva = 0;
3623  $this->total_localtax1 = 0;
3624  $this->total_localtax2 = 0;
3625  $this->total_ttc = 0;
3626  $total_ht_by_vats = array();
3627  $total_tva_by_vats = array();
3628  $total_ttc_by_vats = array();
3629  $this->multicurrency_total_ht = 0;
3630  $this->multicurrency_total_tva = 0;
3631  $this->multicurrency_total_ttc = 0;
3632 
3633  $this->db->begin();
3634 
3635  $num = $this->db->num_rows($resql);
3636  $i = 0;
3637  while ($i < $num) {
3638  $obj = $this->db->fetch_object($resql);
3639 
3640  // Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
3641  $parameters = array('fk_element' => $obj->rowid);
3642  $reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3643 
3644  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'
3645  // This part of code is to fix data. We should not call it too often.
3646  $localtax_array = array($obj->localtax1_type, $obj->localtax1_tx, $obj->localtax2_type, $obj->localtax2_tx);
3647  $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);
3648 
3649  $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.
3650  $diff_on_current_total = price2num($obj->total_ttc - $obj->total_ht - $obj->total_tva - $obj->total_localtax1 - $obj->total_localtax2, 'MT', 1);
3651  //var_dump($obj->total_ht.' '.$obj->total_tva.' '.$obj->total_localtax1.' '.$obj->total_localtax2.' => '.$obj->total_ttc);
3652  //var_dump($diff_when_using_price_ht.' '.$diff_on_current_total);
3653 
3654  if ($diff_on_current_total) {
3655  // This should not happen, we should always have in table: total_ttc = total_ht + total_vat + total_localtax1 + total_localtax2
3656  $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);
3657  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);
3658  $resqlfix = $this->db->query($sqlfix);
3659  if (!$resqlfix) {
3660  dol_print_error($this->db, 'Failed to update line');
3661  }
3662  $obj->total_tva = $tmpcal[1];
3663  $obj->total_ttc = $tmpcal[2];
3664  } elseif ($diff_when_using_price_ht && $roundingadjust == '0') {
3665  // After calculation from HT, total is consistent but we have found a difference between VAT part in calculation and into database and
3666  // we ask to force the use of rounding on line (like done on calculation) so we force update of line
3667  $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);
3668  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);
3669  $resqlfix = $this->db->query($sqlfix);
3670  if (!$resqlfix) {
3671  dol_print_error($this->db, 'Failed to update line');
3672  }
3673  $obj->total_tva = $tmpcal[1];
3674  $obj->total_ttc = $tmpcal[2];
3675  }
3676  }
3677 
3678  $this->total_ht += $obj->total_ht; // The field visible at end of line detail
3679  $this->total_tva += $obj->total_tva;
3680  $this->total_localtax1 += $obj->total_localtax1;
3681  $this->total_localtax2 += $obj->total_localtax2;
3682  $this->total_ttc += $obj->total_ttc;
3683  $this->multicurrency_total_ht += $obj->multicurrency_total_ht; // The field visible at end of line detail
3684  $this->multicurrency_total_tva += $obj->multicurrency_total_tva;
3685  $this->multicurrency_total_ttc += $obj->multicurrency_total_ttc;
3686 
3687  if (!isset($total_ht_by_vats[$obj->vatrate])) {
3688  $total_ht_by_vats[$obj->vatrate] = 0;
3689  }
3690  if (!isset($total_tva_by_vats[$obj->vatrate])) {
3691  $total_tva_by_vats[$obj->vatrate] = 0;
3692  }
3693  if (!isset($total_ttc_by_vats[$obj->vatrate])) {
3694  $total_ttc_by_vats[$obj->vatrate] = 0;
3695  }
3696  $total_ht_by_vats[$obj->vatrate] += $obj->total_ht;
3697  $total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
3698  $total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
3699 
3700  if ($forcedroundingmode == '1') { // Check if we need adjustement onto line for vat. TODO This works on the company currency but not on foreign currency
3701  $tmpvat = price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
3702  $diff = price2num($total_tva_by_vats[$obj->vatrate] - $tmpvat, 'MT', 1);
3703  //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";
3704  if ($diff) {
3705  if (abs($diff) > (10 * pow(10, -1 * getDolGlobalInt('MAIN_MAX_DECIMALS_TOT', 0)))) {
3706  // If error is more than 10 times the accurancy of rounding. This should not happen.
3707  $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.';
3708  dol_syslog($errmsg, LOG_WARNING);
3709  $this->error = $errmsg;
3710  $error++;
3711  break;
3712  }
3713  $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);
3714  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);
3715 
3716  $resqlfix = $this->db->query($sqlfix);
3717 
3718  if (!$resqlfix) {
3719  dol_print_error($this->db, 'Failed to update line');
3720  }
3721 
3722  $this->total_tva = (float) price2num($this->total_tva - $diff, '', 1);
3723  $this->total_ttc = (float) price2num($this->total_ttc - $diff, '', 1);
3724  $total_tva_by_vats[$obj->vatrate] = (float) price2num($total_tva_by_vats[$obj->vatrate] - $diff, '', 1);
3725  $total_ttc_by_vats[$obj->vatrate] = (float) price2num($total_ttc_by_vats[$obj->vatrate] - $diff, '', 1);
3726  }
3727  }
3728 
3729  $i++;
3730  }
3731 
3732  // Add revenue stamp to total
3733  $this->total_ttc += isset($this->revenuestamp) ? $this->revenuestamp : 0;
3734  $this->multicurrency_total_ttc += isset($this->revenuestamp) ? ($this->revenuestamp * $multicurrency_tx) : 0;
3735 
3736  // Situations totals
3737  if (!empty($this->situation_cycle_ref) && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits') && $this->type != $this::TYPE_CREDIT_NOTE) {
3738  $prev_sits = $this->get_prev_sits();
3739 
3740  foreach ($prev_sits as $sit) { // $sit is an object Facture loaded with a fetch.
3741  $this->total_ht -= $sit->total_ht;
3742  $this->total_tva -= $sit->total_tva;
3743  $this->total_localtax1 -= $sit->total_localtax1;
3744  $this->total_localtax2 -= $sit->total_localtax2;
3745  $this->total_ttc -= $sit->total_ttc;
3746  $this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
3747  $this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
3748  $this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
3749  }
3750  }
3751 
3752  // Clean total
3753  $this->total_ht = (float) price2num($this->total_ht);
3754  $this->total_tva = (float) price2num($this->total_tva);
3755  $this->total_localtax1 = (float) price2num($this->total_localtax1);
3756  $this->total_localtax2 = (float) price2num($this->total_localtax2);
3757  $this->total_ttc = (float) price2num($this->total_ttc);
3758 
3759  $this->db->free($resql);
3760 
3761  // Now update global fields total_ht, total_ttc, total_tva, total_localtax1, total_localtax2, multicurrency_total_* of main object
3762  $fieldht = 'total_ht';
3763  $fieldtva = 'tva';
3764  $fieldlocaltax1 = 'localtax1';
3765  $fieldlocaltax2 = 'localtax2';
3766  $fieldttc = 'total_ttc';
3767  // Specific code for backward compatibility with old field names
3768  if (in_array($this->element, array('propal', 'commande', 'facture', 'facturerec', 'supplier_proposal', 'order_supplier', 'facture_fourn', 'invoice_supplier', 'invoice_supplier_rec', 'expensereport'))) {
3769  $fieldtva = 'total_tva';
3770  }
3771 
3772  if (!$error && empty($nodatabaseupdate)) {
3773  $sql = "UPDATE ".$this->db->prefix().$this->table_element.' SET';
3774  $sql .= " ".$fieldht." = ".((float) price2num($this->total_ht, 'MT', 1)).",";
3775  $sql .= " ".$fieldtva." = ".((float) price2num($this->total_tva, 'MT', 1)).",";
3776  $sql .= " ".$fieldlocaltax1." = ".((float) price2num($this->total_localtax1, 'MT', 1)).",";
3777  $sql .= " ".$fieldlocaltax2." = ".((float) price2num($this->total_localtax2, 'MT', 1)).",";
3778  $sql .= " ".$fieldttc." = ".((float) price2num($this->total_ttc, 'MT', 1));
3779  $sql .= ", multicurrency_total_ht = ".((float) price2num($this->multicurrency_total_ht, 'MT', 1));
3780  $sql .= ", multicurrency_total_tva = ".((float) price2num($this->multicurrency_total_tva, 'MT', 1));
3781  $sql .= ", multicurrency_total_ttc = ".((float) price2num($this->multicurrency_total_ttc, 'MT', 1));
3782  $sql .= " WHERE rowid = ".((int) $this->id);
3783 
3784  dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3785  $resql = $this->db->query($sql);
3786 
3787  if (!$resql) {
3788  $error++;
3789  $this->error = $this->db->lasterror();
3790  $this->errors[] = $this->db->lasterror();
3791  }
3792  }
3793 
3794  if (!$error) {
3795  $this->db->commit();
3796  return 1;
3797  } else {
3798  $this->db->rollback();
3799  return -1;
3800  }
3801  } else {
3802  dol_print_error($this->db, 'Bad request in update_price');
3803  return -1;
3804  }
3805  }
3806 
3807  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3818  public function add_object_linked($origin = null, $origin_id = null, $f_user = null, $notrigger = 0)
3819  {
3820  // phpcs:enable
3821  global $user, $hookmanager, $action;
3822  $origin = (!empty($origin) ? $origin : $this->origin);
3823  $origin_id = (!empty($origin_id) ? $origin_id : $this->origin_id);
3824  $f_user = isset($f_user) ? $f_user : $user;
3825 
3826  // Special case
3827  if ($origin == 'order') {
3828  $origin = 'commande';
3829  }
3830  if ($origin == 'invoice') {
3831  $origin = 'facture';
3832  }
3833  if ($origin == 'invoice_template') {
3834  $origin = 'facturerec';
3835  }
3836  if ($origin == 'supplierorder') {
3837  $origin = 'order_supplier';
3838  }
3839 
3840  // 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.
3841  // 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.
3842  $coremodule = array('knowledgemanagement', 'partnership', 'workstation', 'ticket', 'recruitment', 'eventorganization', 'asset');
3843  // Add module part to target type if object has $module property and isn't in core modules.
3844  $targettype = ((!empty($this->module) && ! in_array($this->module, $coremodule)) ? $this->module.'_' : '').$this->element;
3845 
3846  $parameters = array('targettype'=>$targettype);
3847  // Hook for explicitly set the targettype if it must be differtent than $this->element
3848  $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3849  if ($reshook > 0) {
3850  if (!empty($hookmanager->resArray['targettype'])) $targettype = $hookmanager->resArray['targettype'];
3851  }
3852 
3853  $this->db->begin();
3854  $error = 0;
3855 
3856  $sql = "INSERT INTO " . $this->db->prefix() . "element_element (";
3857  $sql .= "fk_source";
3858  $sql .= ", sourcetype";
3859  $sql .= ", fk_target";
3860  $sql .= ", targettype";
3861  $sql .= ") VALUES (";
3862  $sql .= ((int) $origin_id);
3863  $sql .= ", '" . $this->db->escape($origin) . "'";
3864  $sql .= ", " . ((int) $this->id);
3865  $sql .= ", '" . $this->db->escape($targettype) . "'";
3866  $sql .= ")";
3867 
3868  dol_syslog(get_class($this) . "::add_object_linked", LOG_DEBUG);
3869  if ($this->db->query($sql)) {
3870  if (!$notrigger) {
3871  // Call trigger
3872  $this->context['link_origin'] = $origin;
3873  $this->context['link_origin_id'] = $origin_id;
3874  $result = $this->call_trigger('OBJECT_LINK_INSERT', $f_user);
3875  if ($result < 0) {
3876  $error++;
3877  }
3878  // End call triggers
3879  }
3880  } else {
3881  $this->error = $this->db->lasterror();
3882  $error++;
3883  }
3884 
3885  if (!$error) {
3886  $this->db->commit();
3887  return 1;
3888  } else {
3889  $this->db->rollback();
3890  return 0;
3891  }
3892  }
3893 
3916  public function fetchObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1)
3917  {
3918  global $conf, $hookmanager, $action;
3919 
3920  // Important for pdf generation time reduction
3921  // This boolean is true if $this->linkedObjects has already been loaded with all objects linked without filter
3922  if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
3923  return 1;
3924  }
3925 
3926  $this->linkedObjectsIds = array();
3927  $this->linkedObjects = array();
3928 
3929  $justsource = false;
3930  $justtarget = false;
3931  $withtargettype = false;
3932  $withsourcetype = false;
3933 
3934  $parameters = array('sourcetype'=>$sourcetype, 'sourceid'=>$sourceid, 'targettype'=>$targettype, 'targetid'=>$targetid);
3935  // Hook for explicitly set the targettype if it must be differtent than $this->element
3936  $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3937  if ($reshook > 0) {
3938  if (!empty($hookmanager->resArray['sourcetype'])) $sourcetype = $hookmanager->resArray['sourcetype'];
3939  if (!empty($hookmanager->resArray['sourceid'])) $sourceid = $hookmanager->resArray['sourceid'];
3940  if (!empty($hookmanager->resArray['targettype'])) $targettype = $hookmanager->resArray['targettype'];
3941  if (!empty($hookmanager->resArray['targetid'])) $targetid = $hookmanager->resArray['targetid'];
3942  }
3943 
3944  if (!empty($sourceid) && !empty($sourcetype) && empty($targetid)) {
3945  $justsource = true; // the source (id and type) is a search criteria
3946  if (!empty($targettype)) {
3947  $withtargettype = true;
3948  }
3949  }
3950  if (!empty($targetid) && !empty($targettype) && empty($sourceid)) {
3951  $justtarget = true; // the target (id and type) is a search criteria
3952  if (!empty($sourcetype)) {
3953  $withsourcetype = true;
3954  }
3955  }
3956 
3957  $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
3958  $targetid = (!empty($targetid) ? $targetid : $this->id);
3959  $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
3960  $targettype = (!empty($targettype) ? $targettype : $this->element);
3961 
3962  /*if (empty($sourceid) && empty($targetid))
3963  {
3964  dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
3965  return -1;
3966  }*/
3967 
3968  // Links between objects are stored in table element_element
3969  $sql = "SELECT rowid, fk_source, sourcetype, fk_target, targettype";
3970  $sql .= " FROM ".$this->db->prefix()."element_element";
3971  $sql .= " WHERE ";
3972  if ($justsource || $justtarget) {
3973  if ($justsource) {
3974  $sql .= "fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."'";
3975  if ($withtargettype) {
3976  $sql .= " AND targettype = '".$this->db->escape($targettype)."'";
3977  }
3978  } elseif ($justtarget) {
3979  $sql .= "fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."'";
3980  if ($withsourcetype) {
3981  $sql .= " AND sourcetype = '".$this->db->escape($sourcetype)."'";
3982  }
3983  }
3984  } else {
3985  $sql .= "(fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."')";
3986  $sql .= " ".$clause." (fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."')";
3987  if ($loadalsoobjects && $this->id > 0 && $sourceid == $this->id && $sourcetype == $this->element && $targetid == $this->id && $targettype == $this->element && $clause == 'OR') {
3988  $this->linkedObjectsFullLoaded[$this->id] = true;
3989  }
3990  }
3991  $sql .= " ORDER BY ".$orderby;
3992 
3993  dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
3994  $resql = $this->db->query($sql);
3995  if ($resql) {
3996  $num = $this->db->num_rows($resql);
3997  $i = 0;
3998  while ($i < $num) {
3999  $obj = $this->db->fetch_object($resql);
4000  if ($justsource || $justtarget) {
4001  if ($justsource) {
4002  $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
4003  } elseif ($justtarget) {
4004  $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
4005  }
4006  } else {
4007  if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype) {
4008  $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
4009  }
4010  if ($obj->fk_target == $targetid && $obj->targettype == $targettype) {
4011  $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
4012  }
4013  }
4014  $i++;
4015  }
4016 
4017  if (!empty($this->linkedObjectsIds)) {
4018  $tmparray = $this->linkedObjectsIds;
4019  foreach ($tmparray as $objecttype => $objectids) { // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
4020  // Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
4021  $module = $element = $subelement = $objecttype;
4022  $regs = array();
4023  if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
4024  && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
4025  $module = $element = $regs[1];
4026  $subelement = $regs[2];
4027  }
4028 
4029  $classpath = $element.'/class';
4030  // To work with non standard classpath or module name
4031  if ($objecttype == 'facture') {
4032  $classpath = 'compta/facture/class';
4033  } elseif ($objecttype == 'facturerec') {
4034  $classpath = 'compta/facture/class';
4035  $module = 'facture';
4036  } elseif ($objecttype == 'propal') {
4037  $classpath = 'comm/propal/class';
4038  } elseif ($objecttype == 'supplier_proposal') {
4039  $classpath = 'supplier_proposal/class';
4040  } elseif ($objecttype == 'shipping') {
4041  $classpath = 'expedition/class';
4042  $subelement = 'expedition';
4043  $module = 'expedition';
4044  } elseif ($objecttype == 'delivery') {
4045  $classpath = 'delivery/class';
4046  $subelement = 'delivery';
4047  $module = 'delivery_note';
4048  } elseif ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier') {
4049  $classpath = 'fourn/class';
4050  $module = 'fournisseur';
4051  } elseif ($objecttype == 'fichinter') {
4052  $classpath = 'fichinter/class';
4053  $subelement = 'fichinter';
4054  $module = 'ficheinter';
4055  } elseif ($objecttype == 'subscription') {
4056  $classpath = 'adherents/class';
4057  $module = 'adherent';
4058  } elseif ($objecttype == 'contact') {
4059  $module = 'societe';
4060  }
4061  // Set classfile
4062  $classfile = strtolower($subelement);
4063  $classname = ucfirst($subelement);
4064 
4065  if ($objecttype == 'order') {
4066  $classfile = 'commande';
4067  $classname = 'Commande';
4068  } elseif ($objecttype == 'invoice_supplier') {
4069  $classfile = 'fournisseur.facture';
4070  $classname = 'FactureFournisseur';
4071  } elseif ($objecttype == 'order_supplier') {
4072  $classfile = 'fournisseur.commande';
4073  $classname = 'CommandeFournisseur';
4074  } elseif ($objecttype == 'supplier_proposal') {
4075  $classfile = 'supplier_proposal';
4076  $classname = 'SupplierProposal';
4077  } elseif ($objecttype == 'facturerec') {
4078  $classfile = 'facture-rec';
4079  $classname = 'FactureRec';
4080  } elseif ($objecttype == 'subscription') {
4081  $classfile = 'subscription';
4082  $classname = 'Subscription';
4083  } elseif ($objecttype == 'project' || $objecttype == 'projet') {
4084  $classpath = 'projet/class';
4085  $classfile = 'project';
4086  $classname = 'Project';
4087  } elseif ($objecttype == 'conferenceorboothattendee') {
4088  $classpath = 'eventorganization/class';
4089  $classfile = 'conferenceorboothattendee';
4090  $classname = 'ConferenceOrBoothAttendee';
4091  $module = 'eventorganization';
4092  } elseif ($objecttype == 'conferenceorbooth') {
4093  $classpath = 'eventorganization/class';
4094  $classfile = 'conferenceorbooth';
4095  $classname = 'ConferenceOrBooth';
4096  $module = 'eventorganization';
4097  } elseif ($objecttype == 'mo') {
4098  $classpath = 'mrp/class';
4099  $classfile = 'mo';
4100  $classname = 'Mo';
4101  $module = 'mrp';
4102  }
4103 
4104  // Here $module, $classfile and $classname are set, we can use them.
4105  if (isModEnabled($module) && (($element != $this->element) || $alsosametype)) {
4106  if ($loadalsoobjects && (is_numeric($loadalsoobjects) || ($loadalsoobjects === $objecttype))) {
4107  dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
4108  //print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
4109  if (class_exists($classname)) {
4110  foreach ($objectids as $i => $objectid) { // $i is rowid into llx_element_element
4111  $object = new $classname($this->db);
4112  $ret = $object->fetch($objectid);
4113  if ($ret >= 0) {
4114  $this->linkedObjects[$objecttype][$i] = $object;
4115  }
4116  }
4117  }
4118  }
4119  } else {
4120  unset($this->linkedObjectsIds[$objecttype]);
4121  }
4122  }
4123  }
4124  return 1;
4125  } else {
4126  dol_print_error($this->db);
4127  return -1;
4128  }
4129  }
4130 
4137  public function clearObjectLinkedCache()
4138  {
4139  if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
4140  unset($this->linkedObjectsFullLoaded[$this->id]);
4141  }
4142 
4143  return 1;
4144  }
4145 
4158  public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $f_user = null, $notrigger = 0)
4159  {
4160  global $user;
4161  $updatesource = false;
4162  $updatetarget = false;
4163  $f_user = isset($f_user) ? $f_user : $user;
4164 
4165  if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4166  $updatesource = true;
4167  } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4168  $updatetarget = true;
4169  }
4170 
4171  $this->db->begin();
4172  $error = 0;
4173 
4174  $sql = "UPDATE " . $this->db->prefix() . "element_element SET ";
4175  if ($updatesource) {
4176  $sql .= "fk_source = " . ((int) $sourceid);
4177  $sql .= ", sourcetype = '" . $this->db->escape($sourcetype) . "'";
4178  $sql .= " WHERE fk_target = " . ((int) $this->id);
4179  $sql .= " AND targettype = '" . $this->db->escape($this->element) . "'";
4180  } elseif ($updatetarget) {
4181  $sql .= "fk_target = " . ((int) $targetid);
4182  $sql .= ", targettype = '" . $this->db->escape($targettype) . "'";
4183  $sql .= " WHERE fk_source = " . ((int) $this->id);
4184  $sql .= " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4185  }
4186 
4187  dol_syslog(get_class($this) . "::updateObjectLinked", LOG_DEBUG);
4188  if ($this->db->query($sql)) {
4189  if (!$notrigger) {
4190  // Call trigger
4191  $this->context['link_source_id'] = $sourceid;
4192  $this->context['link_source_type'] = $sourcetype;
4193  $this->context['link_target_id'] = $targetid;
4194  $this->context['link_target_type'] = $targettype;
4195  $result = $this->call_trigger('OBJECT_LINK_MODIFY', $f_user);
4196  if ($result < 0) {
4197  $error++;
4198  }
4199  // End call triggers
4200  }
4201  } else {
4202  $this->error = $this->db->lasterror();
4203  $error++;
4204  }
4205 
4206  if (!$error) {
4207  $this->db->commit();
4208  return 1;
4209  } else {
4210  $this->db->rollback();
4211  return -1;
4212  }
4213  }
4214 
4228  public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = '', $f_user = null, $notrigger = 0)
4229  {
4230  global $user;
4231  $deletesource = false;
4232  $deletetarget = false;
4233  $f_user = isset($f_user) ? $f_user : $user;
4234 
4235  if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4236  $deletesource = true;
4237  } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4238  $deletetarget = true;
4239  }
4240 
4241  $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
4242  $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
4243  $targetid = (!empty($targetid) ? $targetid : $this->id);
4244  $targettype = (!empty($targettype) ? $targettype : $this->element);
4245  $this->db->begin();
4246  $error = 0;
4247 
4248  if (!$notrigger) {
4249  // Call trigger
4250  $this->context['link_id'] = $rowid;
4251  $this->context['link_source_id'] = $sourceid;
4252  $this->context['link_source_type'] = $sourcetype;
4253  $this->context['link_target_id'] = $targetid;
4254  $this->context['link_target_type'] = $targettype;
4255  $result = $this->call_trigger('OBJECT_LINK_DELETE', $f_user);
4256  if ($result < 0) {
4257  $error++;
4258  }
4259  // End call triggers
4260  }
4261 
4262  if (!$error) {
4263  $sql = "DELETE FROM " . $this->db->prefix() . "element_element";
4264  $sql .= " WHERE";
4265  if ($rowid > 0) {
4266  $sql .= " rowid = " . ((int) $rowid);
4267  } else {
4268  if ($deletesource) {
4269  $sql .= " fk_source = " . ((int) $sourceid) . " AND sourcetype = '" . $this->db->escape($sourcetype) . "'";
4270  $sql .= " AND fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "'";
4271  } elseif ($deletetarget) {
4272  $sql .= " fk_target = " . ((int) $targetid) . " AND targettype = '" . $this->db->escape($targettype) . "'";
4273  $sql .= " AND fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4274  } else {
4275  $sql .= " (fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "')";
4276  $sql .= " OR";
4277  $sql .= " (fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "')";
4278  }
4279  }
4280 
4281  dol_syslog(get_class($this) . "::deleteObjectLinked", LOG_DEBUG);
4282  if (!$this->db->query($sql)) {
4283  $this->error = $this->db->lasterror();
4284  $this->errors[] = $this->error;
4285  $error++;
4286  }
4287  }
4288 
4289  if (!$error) {
4290  $this->db->commit();
4291  return 1;
4292  } else {
4293  $this->db->rollback();
4294  return 0;
4295  }
4296  }
4297 
4307  public static function getAllItemsLinkedByObjectID($fk_object_where, $field_select, $field_where, $table_element)
4308  {
4309  if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4310  return -1;
4311  }
4312 
4313  global $db;
4314 
4315  $sql = "SELECT ".$field_select." FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4316  $resql = $db->query($sql);
4317 
4318  $TRes = array();
4319  if (!empty($resql)) {
4320  while ($res = $db->fetch_object($resql)) {
4321  $TRes[] = $res->{$field_select};
4322  }
4323  }
4324 
4325  return $TRes;
4326  }
4327 
4336  public static function deleteAllItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
4337  {
4338  if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4339  return -1;
4340  }
4341 
4342  global $db;
4343 
4344  $sql = "DELETE FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4345  $resql = $db->query($sql);
4346 
4347  if (empty($resql)) {
4348  return 0;
4349  }
4350 
4351  return 1;
4352  }
4353 
4364  public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '', $fieldstatus = 'fk_statut')
4365  {
4366  global $user, $langs, $conf;
4367 
4368  $savElementId = $elementId; // To be used later to know if we were using the method using the id of this or not.
4369 
4370  $elementId = (!empty($elementId) ? $elementId : $this->id);
4371  $elementTable = (!empty($elementType) ? $elementType : $this->table_element);
4372 
4373  $this->db->begin();
4374 
4375  if ($elementTable == 'facture_rec') {
4376  $fieldstatus = "suspended";
4377  }
4378  if ($elementTable == 'mailing') {
4379  $fieldstatus = "statut";
4380  }
4381  if ($elementTable == 'cronjob') {
4382  $fieldstatus = "status";
4383  }
4384  if ($elementTable == 'user') {
4385  $fieldstatus = "statut";
4386  }
4387  if ($elementTable == 'expensereport') {
4388  $fieldstatus = "fk_statut";
4389  }
4390  if ($elementTable == 'commande_fournisseur_dispatch') {
4391  $fieldstatus = "status";
4392  }
4393  if (isset($this->fields) && is_array($this->fields) && array_key_exists('status', $this->fields)) {
4394  $fieldstatus = 'status';
4395  }
4396 
4397  $sql = "UPDATE ".$this->db->prefix().$elementTable;
4398  $sql .= " SET ".$fieldstatus." = ".((int) $status);
4399  // If status = 1 = validated, update also fk_user_valid
4400  // TODO Replace the test on $elementTable by doing a test on existence of the field in $this->fields
4401  if ($status == 1 && in_array($elementTable, array('expensereport', 'inventory'))) {
4402  $sql .= ", fk_user_valid = ".((int) $user->id);
4403  }
4404  if ($status == 1 && in_array($elementTable, array('expensereport'))) {
4405  $sql .= ", date_valid = '".$this->db->idate(dol_now())."'";
4406  }
4407  if ($status == 1 && in_array($elementTable, array('inventory'))) {
4408  $sql .= ", date_validation = '".$this->db->idate(dol_now())."'";
4409  }
4410  $sql .= " WHERE rowid = ".((int) $elementId);
4411  $sql .= " AND ".$fieldstatus." <> ".((int) $status); // We avoid update if status already correct
4412 
4413  dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
4414  $resql = $this->db->query($sql);
4415  if ($resql) {
4416  $error = 0;
4417 
4418  $nb_rows_affected = $this->db->affected_rows($resql); // should be 1 or 0 if status was already correct
4419 
4420  if ($nb_rows_affected > 0) {
4421  if (empty($trigkey)) {
4422  // Try to guess trigkey (for backward compatibility, now we should have trigkey defined into the call of setStatus)
4423  if ($this->element == 'supplier_proposal' && $status == 2) {
4424  $trigkey = 'SUPPLIER_PROPOSAL_SIGN'; // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
4425  }
4426  if ($this->element == 'supplier_proposal' && $status == 3) {
4427  $trigkey = 'SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
4428  }
4429  if ($this->element == 'supplier_proposal' && $status == 4) {
4430  $trigkey = 'SUPPLIER_PROPOSAL_CLOSE'; // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
4431  }
4432  if ($this->element == 'fichinter' && $status == 3) {
4433  $trigkey = 'FICHINTER_CLASSIFY_DONE';
4434  }
4435  if ($this->element == 'fichinter' && $status == 2) {
4436  $trigkey = 'FICHINTER_CLASSIFY_BILLED';
4437  }
4438  if ($this->element == 'fichinter' && $status == 1) {
4439  $trigkey = 'FICHINTER_CLASSIFY_UNBILLED';
4440  }
4441  }
4442 
4443  if ($trigkey) {
4444  // Call trigger
4445  $result = $this->call_trigger($trigkey, $user);
4446  if ($result < 0) {
4447  $error++;
4448  }
4449  // End call triggers
4450  }
4451  } else {
4452  // The status was probably already good. We do nothing more, no triggers.
4453  }
4454 
4455  if (!$error) {
4456  $this->db->commit();
4457 
4458  if (empty($savElementId)) {
4459  // If the element we update is $this (so $elementId was provided as null)
4460  if ($fieldstatus == 'tosell') {
4461  $this->status = $status;
4462  } elseif ($fieldstatus == 'tobuy') {
4463  $this->status_buy = $status;
4464  } else {
4465  $this->statut = $status;
4466  $this->status = $status;
4467  }
4468  }
4469 
4470  return 1;
4471  } else {
4472  $this->db->rollback();
4473  dol_syslog(get_class($this)."::setStatut ".$this->error, LOG_ERR);
4474  return -1;
4475  }
4476  } else {
4477  $this->error = $this->db->lasterror();
4478  $this->db->rollback();
4479  return -1;
4480  }
4481  }
4482 
4483 
4491  public function getCanvas($id = 0, $ref = '')
4492  {
4493  global $conf;
4494 
4495  if (empty($id) && empty($ref)) {
4496  return 0;
4497  }
4498  if (!empty($conf->global->MAIN_DISABLE_CANVAS)) {
4499  return 0; // To increase speed. Not enabled by default.
4500  }
4501 
4502  // Clean parameters
4503  $ref = trim($ref);
4504 
4505  $sql = "SELECT rowid, canvas";
4506  $sql .= " FROM ".$this->db->prefix().$this->table_element;
4507  $sql .= " WHERE entity IN (".getEntity($this->element).")";
4508  if (!empty($id)) {
4509  $sql .= " AND rowid = ".((int) $id);
4510  }
4511  if (!empty($ref)) {
4512  $sql .= " AND ref = '".$this->db->escape($ref)."'";
4513  }
4514 
4515  $resql = $this->db->query($sql);
4516  if ($resql) {
4517  $obj = $this->db->fetch_object($resql);
4518  if ($obj) {
4519  $this->canvas = $obj->canvas;
4520  return 1;
4521  } else {
4522  return 0;
4523  }
4524  } else {
4525  dol_print_error($this->db);
4526  return -1;
4527  }
4528  }
4529 
4530 
4537  public function getSpecialCode($lineid)
4538  {
4539  $sql = "SELECT special_code FROM ".$this->db->prefix().$this->table_element_line;
4540  $sql .= " WHERE rowid = ".((int) $lineid);
4541  $resql = $this->db->query($sql);
4542  if ($resql) {
4543  $row = $this->db->fetch_row($resql);
4544  return $row[0];
4545  }
4546 
4547  return 0;
4548  }
4549 
4558  public function isObjectUsed($id = 0, $entity = 0)
4559  {
4560  global $langs;
4561 
4562  if (empty($id)) {
4563  $id = $this->id;
4564  }
4565 
4566  // Check parameters
4567  if (!isset($this->childtables) || !is_array($this->childtables) || count($this->childtables) == 0) {
4568  dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
4569  return -1;
4570  }
4571 
4572  $arraytoscan = $this->childtables; // array('tablename'=>array('fk_element'=>'parentfield'), ...) or array('tablename'=>array('parent'=>table_parent, 'parentkey'=>'nameoffieldforparentfkkey'), ...)
4573  // For backward compatibility, we check if array is old format array('tablename1', 'tablename2', ...)
4574  $tmparray = array_keys($this->childtables);
4575  if (is_numeric($tmparray[0])) {
4576  $arraytoscan = array_flip($this->childtables);
4577  }
4578 
4579  // Test if child exists
4580  $haschild = 0;
4581  foreach ($arraytoscan as $table => $element) {
4582  //print $id.'-'.$table.'-'.$elementname.'<br>';
4583  // Check if element can be deleted
4584  $sql = "SELECT COUNT(*) as nb";
4585  $sql.= " FROM ".$this->db->prefix().$table." as c";
4586  if (!empty($element['parent']) && !empty($element['parentkey'])) {
4587  $sql.= ", ".$this->db->prefix().$element['parent']." as p";
4588  }
4589  if (!empty($element['fk_element'])) {
4590  $sql.= " WHERE c.".$element['fk_element']." = ".((int) $id);
4591  } else {
4592  $sql.= " WHERE c.".$this->fk_element." = ".((int) $id);
4593  }
4594  if (!empty($element['parent']) && !empty($element['parentkey'])) {
4595  $sql.= " AND c.".$element['parentkey']." = p.rowid";
4596  }
4597  if (!empty($element['parent']) && !empty($element['parenttypefield']) && !empty($element['parenttypevalue'])) {
4598  $sql.= " AND c.".$element['parenttypefield']." = '".$this->db->escape($element['parenttypevalue'])."'";
4599  }
4600  if (!empty($entity)) {
4601  if (!empty($element['parent']) && !empty($element['parentkey'])) {
4602  $sql.= " AND p.entity = ".((int) $entity);
4603  } else {
4604  $sql.= " AND c.entity = ".((int) $entity);
4605  }
4606  }
4607 
4608  $resql = $this->db->query($sql);
4609  if ($resql) {
4610  $obj = $this->db->fetch_object($resql);
4611  if ($obj->nb > 0) {
4612  $langs->load("errors");
4613  //print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
4614  $haschild += $obj->nb;
4615  if (is_numeric($element)) { // very old usage array('table1', 'table2', ...)
4616  $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $table);
4617  } elseif (is_string($element)) { // old usage array('table1' => 'TranslateKey1', 'table2' => 'TranslateKey2', ...)
4618  $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element));
4619  } else { // new usage: $element['name']=Translation key
4620  $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element['name']));
4621  }
4622  break; // We found at least one, we stop here
4623  }
4624  } else {
4625  $this->errors[] = $this->db->lasterror();
4626  return -1;
4627  }
4628  }
4629  if ($haschild > 0) {
4630  $this->errors[] = "ErrorRecordHasChildren";
4631  return $haschild;
4632  } else {
4633  return 0;
4634  }
4635  }
4636 
4643  public function hasProductsOrServices($predefined = -1)
4644  {
4645  $nb = 0;
4646 
4647  foreach ($this->lines as $key => $val) {
4648  $qualified = 0;
4649  if ($predefined == -1) {
4650  $qualified = 1;
4651  }
4652  if ($predefined == 1 && $val->fk_product > 0) {
4653  $qualified = 1;
4654  }
4655  if ($predefined == 0 && $val->fk_product <= 0) {
4656  $qualified = 1;
4657  }
4658  if ($predefined == 2 && $val->fk_product > 0 && $val->product_type == 0) {
4659  $qualified = 1;
4660  }
4661  if ($predefined == 3 && $val->fk_product > 0 && $val->product_type == 1) {
4662  $qualified = 1;
4663  }
4664  if ($qualified) {
4665  $nb++;
4666  }
4667  }
4668  dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
4669  return $nb;
4670  }
4671 
4677  public function getTotalDiscount()
4678  {
4679  if (!empty($this->table_element_line) ) {
4680  $total_discount = 0.00;
4681 
4682  $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
4683  $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
4684  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
4685 
4686  dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
4687  $resql = $this->db->query($sql);
4688  if ($resql) {
4689  $num = $this->db->num_rows($resql);
4690  $i = 0;
4691  while ($i < $num) {
4692  $obj = $this->db->fetch_object($resql);
4693 
4694  $pu_ht = $obj->pu_ht;
4695  $qty = $obj->qty;
4696  $total_ht = $obj->total_ht;
4697 
4698  $total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
4699  $total_discount += $total_discount_line;
4700 
4701  $i++;
4702  }
4703  }
4704 
4705  //print $total_discount; exit;
4706  return price2num($total_discount);
4707  }
4708 
4709  return null;
4710  }
4711 
4712 
4719  public function getTotalWeightVolume()
4720  {
4721  $totalWeight = 0;
4722  $totalVolume = 0;
4723  // defined for shipment only
4724  $totalOrdered = '';
4725  // defined for shipment only
4726  $totalToShip = '';
4727 
4728  foreach ($this->lines as $line) {
4729  if (isset($line->qty_asked)) {
4730  if (empty($totalOrdered)) {
4731  $totalOrdered = 0; // Avoid warning because $totalOrdered is ''
4732  }
4733  $totalOrdered += $line->qty_asked; // defined for shipment only
4734  }
4735  if (isset($line->qty_shipped)) {
4736  if (empty($totalToShip)) {
4737  $totalToShip = 0; // Avoid warning because $totalToShip is ''
4738  }
4739  $totalToShip += $line->qty_shipped; // defined for shipment only
4740  } elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty)) {
4741  if (empty($totalToShip)) {
4742  $totalToShip = 0;
4743  }
4744  $totalToShip += $line->qty; // defined for reception only
4745  }
4746 
4747  // Define qty, weight, volume, weight_units, volume_units
4748  if ($this->element == 'shipping') {
4749  // for shipments
4750  $qty = $line->qty_shipped ? $line->qty_shipped : 0;
4751  } else {
4752  $qty = $line->qty ? $line->qty : 0;
4753  }
4754 
4755  $weight = !empty($line->weight) ? $line->weight : 0;
4756  ($weight == 0 && !empty($line->product->weight)) ? $weight = $line->product->weight : 0;
4757  $volume = !empty($line->volume) ? $line->volume : 0;
4758  ($volume == 0 && !empty($line->product->volume)) ? $volume = $line->product->volume : 0;
4759 
4760  $weight_units = !empty($line->weight_units) ? $line->weight_units : 0;
4761  ($weight_units == 0 && !empty($line->product->weight_units)) ? $weight_units = $line->product->weight_units : 0;
4762  $volume_units = !empty($line->volume_units) ? $line->volume_units : 0;
4763  ($volume_units == 0 && !empty($line->product->volume_units)) ? $volume_units = $line->product->volume_units : 0;
4764 
4765  $weightUnit = 0;
4766  $volumeUnit = 0;
4767  if (!empty($weight_units)) {
4768  $weightUnit = $weight_units;
4769  }
4770  if (!empty($volume_units)) {
4771  $volumeUnit = $volume_units;
4772  }
4773 
4774  if (empty($totalWeight)) {
4775  $totalWeight = 0; // Avoid warning because $totalWeight is ''
4776  }
4777  if (empty($totalVolume)) {
4778  $totalVolume = 0; // Avoid warning because $totalVolume is ''
4779  }
4780 
4781  //var_dump($line->volume_units);
4782  if ($weight_units < 50) { // < 50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
4783  $trueWeightUnit = pow(10, $weightUnit);
4784  $totalWeight += $weight * $qty * $trueWeightUnit;
4785  } else {
4786  if ($weight_units == 99) {
4787  // conversion 1 Pound = 0.45359237 KG
4788  $trueWeightUnit = 0.45359237;
4789  $totalWeight += $weight * $qty * $trueWeightUnit;
4790  } elseif ($weight_units == 98) {
4791  // conversion 1 Ounce = 0.0283495 KG
4792  $trueWeightUnit = 0.0283495;
4793  $totalWeight += $weight * $qty * $trueWeightUnit;
4794  } else {
4795  $totalWeight += $weight * $qty; // This may be wrong if we mix different units
4796  }
4797  }
4798  if ($volume_units < 50) { // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
4799  //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
4800  $trueVolumeUnit = pow(10, $volumeUnit);
4801  //print $line->volume;
4802  $totalVolume += $volume * $qty * $trueVolumeUnit;
4803  } else {
4804  $totalVolume += $volume * $qty; // This may be wrong if we mix different units
4805  }
4806  }
4807 
4808  return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
4809  }
4810 
4811 
4817  public function setExtraParameters()
4818  {
4819  $this->db->begin();
4820 
4821  $extraparams = (!empty($this->extraparams) ? json_encode($this->extraparams) : null);
4822 
4823  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
4824  $sql .= " SET extraparams = ".(!empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
4825  $sql .= " WHERE rowid = ".((int) $this->id);
4826 
4827  dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
4828  $resql = $this->db->query($sql);
4829  if (!$resql) {
4830  $this->error = $this->db->lasterror();
4831  $this->db->rollback();
4832  return -1;
4833  } else {
4834  $this->db->commit();
4835  return 1;
4836  }
4837  }
4838 
4839 
4840  // --------------------
4841  // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
4842  // --------------------
4843 
4844  /* This is to show add lines */
4845 
4855  public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl')
4856  {
4857  global $conf, $user, $langs, $object, $hookmanager, $extrafields;
4858  global $form;
4859 
4860  // Line extrafield
4861  if (!is_object($extrafields)) {
4862  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4863  $extrafields = new ExtraFields($this->db);
4864  }
4865  $extrafields->fetch_name_optionals_label($this->table_element_line);
4866 
4867  // Output template part (modules that overwrite templates must declare this into descriptor)
4868  // Use global variables + $dateSelector + $seller and $buyer
4869  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'.
4870  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4871  foreach ($dirtpls as $module => $reldir) {
4872  if (!empty($module)) {
4873  $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
4874  } else {
4875  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php';
4876  }
4877 
4878  if (empty($conf->file->strict_mode)) {
4879  $res = @include $tpl;
4880  } else {
4881  $res = include $tpl; // for debug
4882  }
4883  if ($res) {
4884  break;
4885  }
4886  }
4887  }
4888 
4889 
4890 
4891  /* This is to show array of line of details */
4892 
4893 
4908  public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl')
4909  {
4910  global $conf, $hookmanager, $langs, $user, $form, $extrafields, $object;
4911  // TODO We should not use global var for this
4912  global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
4913 
4914  // Define usemargins
4915  $usemargins = 0;
4916  if (isModEnabled('margin') && !empty($this->element) && in_array($this->element, array('facture', 'facturerec', 'propal', 'commande'))) {
4917  $usemargins = 1;
4918  }
4919 
4920  $num = count($this->lines);
4921 
4922  // Line extrafield
4923  if (!is_object($extrafields)) {
4924  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4925  $extrafields = new ExtraFields($this->db);
4926  }
4927  $extrafields->fetch_name_optionals_label($this->table_element_line);
4928 
4929  $parameters = array('num'=>$num, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$this->table_element_line);
4930  $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4931  if (empty($reshook)) {
4932  // Output template part (modules that overwrite templates must declare this into descriptor)
4933  // Use global variables + $dateSelector + $seller and $buyer
4934  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook.
4935  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4936  foreach ($dirtpls as $module => $reldir) {
4937  $res = 0;
4938  if (!empty($module)) {
4939  $tpl = dol_buildpath($reldir.'/objectline_title.tpl.php');
4940  } else {
4941  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php';
4942  }
4943  if (file_exists($tpl)) {
4944  if (empty($conf->file->strict_mode)) {
4945  $res = @include $tpl;
4946  } else {
4947  $res = include $tpl; // for debug
4948  }
4949  }
4950  if ($res) {
4951  break;
4952  }
4953  }
4954  }
4955 
4956  $i = 0;
4957 
4958  print "<!-- begin printObjectLines() --><tbody>\n";
4959  foreach ($this->lines as $line) {
4960  //Line extrafield
4961  $line->fetch_optionals();
4962 
4963  //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line)))
4964  if (is_object($hookmanager)) { // Old code is commented on preceding line.
4965  if (empty($line->fk_parent_line)) {
4966  $parameters = array('line'=>$line, 'num'=>$num, 'i'=>$i, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$line->table_element, 'defaulttpldir'=>$defaulttpldir);
4967  $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4968  } else {
4969  $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, 'defaulttpldir'=>$defaulttpldir);
4970  $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4971  }
4972  }
4973  if (empty($reshook)) {
4974  $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafields, $defaulttpldir);
4975  }
4976 
4977  $i++;
4978  }
4979  print "</tbody><!-- end printObjectLines() -->\n";
4980  }
4981 
4999  public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/core/tpl')
5000  {
5001  global $conf, $langs, $user, $object, $hookmanager;
5002  global $form;
5003  global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
5004 
5005  $object_rights = $this->getRights();
5006 
5007  $text = '';
5008  $description = '';
5009 
5010  // Line in view mode
5011  if ($action != 'editline' || $selected != $line->id) {
5012  // Product
5013  if (!empty($line->fk_product) && $line->fk_product > 0) {
5014  $product_static = new Product($this->db);
5015  $product_static->fetch($line->fk_product);
5016 
5017  $product_static->ref = $line->ref; //can change ref in hook
5018  $product_static->label = !empty($line->label) ? $line->label : ""; //can change label in hook
5019 
5020  $text = $product_static->getNomUrl(1);
5021 
5022  // Define output language and label
5023  if (getDolGlobalInt('MAIN_MULTILANGS')) {
5024  if (property_exists($this, 'socid') && !is_object($this->thirdparty)) {
5025  dol_print_error('', 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
5026  return;
5027  }
5028 
5029  $prod = new Product($this->db);
5030  $prod->fetch($line->fk_product);
5031 
5032  $outputlangs = $langs;
5033  $newlang = '';
5034  if (empty($newlang) && GETPOST('lang_id', 'aZ09')) {
5035  $newlang = GETPOST('lang_id', 'aZ09');
5036  }
5037  if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang) && is_object($this->thirdparty)) {
5038  $newlang = $this->thirdparty->default_lang; // To use language of customer
5039  }
5040  if (!empty($newlang)) {
5041  $outputlangs = new Translate("", $conf);
5042  $outputlangs->setDefaultLang($newlang);
5043  }
5044 
5045  $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
5046  } else {
5047  $label = $line->product_label;
5048  }
5049 
5050  $text .= ' - '.(!empty($line->label) ? $line->label : $label);
5051  $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.
5052  }
5053 
5054  $line->pu_ttc = price2num((!empty($line->subprice) ? $line->subprice : 0) * (1 + ((!empty($line->tva_tx) ? $line->tva_tx : 0) / 100)), 'MU');
5055 
5056  // Output template part (modules that overwrite templates must declare this into descriptor)
5057  // Use global variables + $dateSelector + $seller and $buyer
5058  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5059  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5060  foreach ($dirtpls as $module => $reldir) {
5061  $res = 0;
5062  if (!empty($module)) {
5063  $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
5064  } else {
5065  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php';
5066  }
5067  if (file_exists($tpl)) {
5068  if (empty($conf->file->strict_mode)) {
5069  $res = @include $tpl;
5070  } else {
5071  $res = include $tpl; // for debug
5072  }
5073  }
5074  if ($res) {
5075  break;
5076  }
5077  }
5078  }
5079 
5080  // Line in update mode
5081  if ($this->statut == 0 && $action == 'editline' && $selected == $line->id) {
5082  $label = (!empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
5083 
5084  $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU');
5085 
5086  // Output template part (modules that overwrite templates must declare this into descriptor)
5087  // Use global variables + $dateSelector + $seller and $buyer
5088  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5089  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5090  foreach ($dirtpls as $module => $reldir) {
5091  if (!empty($module)) {
5092  $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
5093  } else {
5094  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php';
5095  }
5096 
5097  if (empty($conf->file->strict_mode)) {
5098  $res = @include $tpl;
5099  } else {
5100  $res = include $tpl; // for debug
5101  }
5102  if ($res) {
5103  break;
5104  }
5105  }
5106  }
5107  }
5108 
5109 
5110  /* This is to show array of line of details of source object */
5111 
5112 
5123  public function printOriginLinesList($restrictlist = '', $selectedLines = array())
5124  {
5125  global $langs, $hookmanager, $conf, $form, $action;
5126 
5127  print '<tr class="liste_titre">';
5128  print '<td class="linecolref">'.$langs->trans('Ref').'</td>';
5129  print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
5130  print '<td class="linecolvat right">'.$langs->trans('VATRate').'</td>';
5131  print '<td class="linecoluht right">'.$langs->trans('PriceUHT').'</td>';
5132  if (isModEnabled("multicurrency")) {
5133  print '<td class="linecoluht_currency right">'.$langs->trans('PriceUHTCurrency').'</td>';
5134  }
5135  print '<td class="linecolqty right">'.$langs->trans('Qty').'</td>';
5136  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5137  print '<td class="linecoluseunit left">'.$langs->trans('Unit').'</td>';
5138  }
5139  print '<td class="linecoldiscount right">'.$langs->trans('ReductionShort').'</td>';
5140  print '<td class="linecolht right">'.$langs->trans('TotalHT').'</td>';
5141  print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
5142  print '</tr>';
5143  $i = 0;
5144 
5145  if (!empty($this->lines)) {
5146  foreach ($this->lines as $line) {
5147  $reshook = 0;
5148  //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line))) {
5149  if (is_object($hookmanager)) { // Old code is commented on preceding line.
5150  $parameters = array('line'=>$line, 'i'=>$i, 'restrictlist'=>$restrictlist, 'selectedLines'=> $selectedLines);
5151  if (!empty($line->fk_parent_line)) { $parameters['fk_parent_line'] = $line->fk_parent_line; }
5152  $reshook = $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5153  }
5154  if (empty($reshook)) {
5155  $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines);
5156  }
5157 
5158  $i++;
5159  }
5160  }
5161  }
5162 
5176  public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
5177  {
5178  global $langs, $conf;
5179 
5180  //var_dump($line);
5181  if (!empty($line->date_start)) {
5182  $date_start = $line->date_start;
5183  } else {
5184  $date_start = $line->date_debut_prevue;
5185  if ($line->date_debut_reel) {
5186  $date_start = $line->date_debut_reel;
5187  }
5188  }
5189  if (!empty($line->date_end)) {
5190  $date_end = $line->date_end;
5191  } else {
5192  $date_end = $line->date_fin_prevue;
5193  if ($line->date_fin_reel) {
5194  $date_end = $line->date_fin_reel;
5195  }
5196  }
5197 
5198  $this->tpl['id'] = $line->id;
5199 
5200  $this->tpl['label'] = '';
5201  if (!empty($line->fk_parent_line)) {
5202  $this->tpl['label'] .= img_picto('', 'rightarrow');
5203  }
5204 
5205  if (($line->info_bits & 2) == 2) { // TODO Not sure this is used for source object
5206  $discount = new DiscountAbsolute($this->db);
5207  $discount->fk_soc = $this->socid;
5208  $this->tpl['label'] .= $discount->getNomUrl(0, 'discount');
5209  } elseif (!empty($line->fk_product)) {
5210  $productstatic = new Product($this->db);
5211  $productstatic->id = $line->fk_product;
5212  $productstatic->ref = $line->ref;
5213  $productstatic->type = $line->fk_product_type;
5214  if (empty($productstatic->ref)) {
5215  $line->fetch_product();
5216  $productstatic = $line->product;
5217  }
5218 
5219  $this->tpl['label'] .= $productstatic->getNomUrl(1);
5220  $this->tpl['label'] .= ' - '.(!empty($line->label) ? $line->label : $line->product_label);
5221  // Dates
5222  if ($line->product_type == 1 && ($date_start || $date_end)) {
5223  $this->tpl['label'] .= get_date_range($date_start, $date_end);
5224  }
5225  } else {
5226  $this->tpl['label'] .= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
5227  if (!empty($line->desc)) {
5228  $this->tpl['label'] .= $line->desc;
5229  } else {
5230  $this->tpl['label'] .= ($line->label ? '&nbsp;'.$line->label : '');
5231  }
5232 
5233  // Dates
5234  if ($line->product_type == 1 && ($date_start || $date_end)) {
5235  $this->tpl['label'] .= get_date_range($date_start, $date_end);
5236  }
5237  }
5238 
5239  if (!empty($line->desc)) {
5240  if ($line->desc == '(CREDIT_NOTE)') { // TODO Not sure this is used for source object
5241  $discount = new DiscountAbsolute($this->db);
5242  $discount->fetch($line->fk_remise_except);
5243  $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
5244  } elseif ($line->desc == '(DEPOSIT)') { // TODO Not sure this is used for source object
5245  $discount = new DiscountAbsolute($this->db);
5246  $discount->fetch($line->fk_remise_except);
5247  $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
5248  } elseif ($line->desc == '(EXCESS RECEIVED)') {
5249  $discount = new DiscountAbsolute($this->db);
5250  $discount->fetch($line->fk_remise_except);
5251  $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
5252  } elseif ($line->desc == '(EXCESS PAID)') {
5253  $discount = new DiscountAbsolute($this->db);
5254  $discount->fetch($line->fk_remise_except);
5255  $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
5256  } else {
5257  $this->tpl['description'] = dol_trunc($line->desc, 60);
5258  }
5259  } else {
5260  $this->tpl['description'] = '&nbsp;';
5261  }
5262 
5263  // VAT Rate
5264  $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
5265  $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
5266  if (!empty($line->vat_src_code) && !preg_match('/\‍(/', $this->tpl['vat_rate'])) {
5267  $this->tpl['vat_rate'] .= ' ('.$line->vat_src_code.')';
5268  }
5269 
5270  $this->tpl['price'] = price($line->subprice);
5271  $this->tpl['total_ht'] = price($line->total_ht);
5272  $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
5273  $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
5274  if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5275  $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
5276  }
5277  $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
5278 
5279  // Is the line strike or not
5280  $this->tpl['strike'] = 0;
5281  if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) {
5282  $this->tpl['strike'] = 1;
5283  }
5284 
5285  // Output template part (modules that overwrite templates must declare this into descriptor)
5286  // Use global variables + $dateSelector + $seller and $buyer
5287  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5288  foreach ($dirtpls as $module => $reldir) {
5289  if (!empty($module)) {
5290  $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
5291  } else {
5292  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php';
5293  }
5294 
5295  if (empty($conf->file->strict_mode)) {
5296  $res = @include $tpl;
5297  } else {
5298  $res = include $tpl; // for debug
5299  }
5300  if ($res) {
5301  break;
5302  }
5303  }
5304  }
5305 
5306 
5307  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5318  public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
5319  {
5320  // phpcs:enable
5321  $this->db->begin();
5322 
5323  $sql = "INSERT INTO ".$this->db->prefix()."element_resources (";
5324  $sql .= "resource_id";
5325  $sql .= ", resource_type";
5326  $sql .= ", element_id";
5327  $sql .= ", element_type";
5328  $sql .= ", busy";
5329  $sql .= ", mandatory";
5330  $sql .= ") VALUES (";
5331  $sql .= ((int) $resource_id);
5332  $sql .= ", '".$this->db->escape($resource_type)."'";
5333  $sql .= ", '".$this->db->escape($this->id)."'";
5334  $sql .= ", '".$this->db->escape($this->element)."'";
5335  $sql .= ", '".$this->db->escape($busy)."'";
5336  $sql .= ", '".$this->db->escape($mandatory)."'";
5337  $sql .= ")";
5338 
5339  dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
5340  if ($this->db->query($sql)) {
5341  $this->db->commit();
5342  return 1;
5343  } else {
5344  $this->error = $this->db->lasterror();
5345  $this->db->rollback();
5346  return 0;
5347  }
5348  }
5349 
5350  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5359  public function delete_resource($rowid, $element, $notrigger = 0)
5360  {
5361  // phpcs:enable
5362  global $user;
5363 
5364  $this->db->begin();
5365 
5366  $sql = "DELETE FROM ".$this->db->prefix()."element_resources";
5367  $sql .= " WHERE rowid = ".((int) $rowid);
5368 
5369  dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
5370 
5371  $resql = $this->db->query($sql);
5372  if (!$resql) {
5373  $this->error = $this->db->lasterror();
5374  $this->db->rollback();
5375  return -1;
5376  } else {
5377  if (!$notrigger) {
5378  $result = $this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
5379  if ($result < 0) {
5380  $this->db->rollback();
5381  return -1;
5382  }
5383  }
5384  $this->db->commit();
5385  return 1;
5386  }
5387  }
5388 
5389 
5395  public function __clone()
5396  {
5397  // Force a copy of this->lines, otherwise it will point to same object.
5398  if (isset($this->lines) && is_array($this->lines)) {
5399  $nboflines = count($this->lines);
5400  for ($i = 0; $i < $nboflines; $i++) {
5401  if (is_object($this->lines[$i])) {
5402  $this->lines[$i] = clone $this->lines[$i];
5403  }
5404  }
5405  }
5406  }
5407 
5421  protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
5422  {
5423  global $conf, $langs, $user, $hookmanager, $action;
5424 
5425  $srctemplatepath = '';
5426 
5427  $parameters = array('modelspath'=>$modelspath, 'modele'=>$modele, 'outputlangs'=>$outputlangs, 'hidedetails'=>$hidedetails, 'hidedesc'=>$hidedesc, 'hideref'=>$hideref, 'moreparams'=>$moreparams);
5428  $reshook = $hookmanager->executeHooks('commonGenerateDocument', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5429 
5430  if (!empty($reshook)) {
5431  return $reshook;
5432  }
5433 
5434  dol_syslog("commonGenerateDocument modele=".$modele." outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
5435 
5436  if (empty($modele)) {
5437  $this->error = 'BadValueForParameterModele';
5438  return -1;
5439  }
5440 
5441  // Increase limit for PDF build
5442  $err = error_reporting();
5443  error_reporting(0);
5444  @set_time_limit(120);
5445  error_reporting($err);
5446 
5447  // If selected model is a filename template (then $modele="modelname" or "modelname:filename")
5448  $tmp = explode(':', $modele, 2);
5449  if (!empty($tmp[1])) {
5450  $modele = $tmp[0];
5451  $srctemplatepath = $tmp[1];
5452  }
5453 
5454  // Search template files
5455  $file = '';
5456  $classname = '';
5457  $filefound = '';
5458  $dirmodels = array('/');
5459  if (is_array($conf->modules_parts['models'])) {
5460  $dirmodels = array_merge($dirmodels, $conf->modules_parts['models']);
5461  }
5462  foreach ($dirmodels as $reldir) {
5463  foreach (array('doc', 'pdf') as $prefix) {
5464  if (in_array(get_class($this), array('Adherent'))) {
5465  // Member module use prefix_modele.class.php
5466  $file = $prefix."_".$modele.".class.php";
5467  } else {
5468  // Other module use prefix_modele.modules.php
5469  $file = $prefix."_".$modele.".modules.php";
5470  }
5471 
5472  // On verifie l'emplacement du modele
5473  $file = dol_buildpath($reldir.$modelspath.$file, 0);
5474  if (file_exists($file)) {
5475  $filefound = $file;
5476  $classname = $prefix.'_'.$modele;
5477  break;
5478  }
5479  }
5480  if ($filefound) {
5481  break;
5482  }
5483  }
5484 
5485  if (!$filefound) {
5486  $this->error = $langs->trans("Error").' Failed to load doc generator with modelpaths='.$modelspath.' - modele='.$modele;
5487  $this->errors[] = $this->error;
5488  dol_syslog($this->error, LOG_ERR);
5489  return -1;
5490  }
5491 
5492  // If generator was found
5493  global $db; // Required to solve a conception default making an include of code using $db instead of $this->db just after.
5494 
5495  require_once $file;
5496 
5497  $obj = new $classname($this->db);
5498 
5499  // If generator is ODT, we must have srctemplatepath defined, if not we set it.
5500  if ($obj->type == 'odt' && empty($srctemplatepath)) {
5501  $varfortemplatedir = $obj->scandir;
5502  if ($varfortemplatedir && !empty($conf->global->$varfortemplatedir)) {
5503  $dirtoscan = $conf->global->$varfortemplatedir;
5504 
5505  $listoffiles = array();
5506 
5507  // Now we add first model found in directories scanned
5508  $listofdir = explode(',', $dirtoscan);
5509  foreach ($listofdir as $key => $tmpdir) {
5510  $tmpdir = trim($tmpdir);
5511  $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
5512  if (!$tmpdir) {
5513  unset($listofdir[$key]);
5514  continue;
5515  }
5516  if (is_dir($tmpdir)) {
5517  $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
5518  if (count($tmpfiles)) {
5519  $listoffiles = array_merge($listoffiles, $tmpfiles);
5520  }
5521  }
5522  }
5523 
5524  if (count($listoffiles)) {
5525  foreach ($listoffiles as $record) {
5526  $srctemplatepath = $record['fullname'];
5527  break;
5528  }
5529  }
5530  }
5531 
5532  if (empty($srctemplatepath)) {
5533  $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
5534  return -1;
5535  }
5536  }
5537 
5538  if ($obj->type == 'odt' && !empty($srctemplatepath)) {
5539  if (!dol_is_file($srctemplatepath)) {
5540  dol_syslog("Failed to locate template file ".$srctemplatepath, LOG_WARNING);
5541  $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
5542  return -1;
5543  }
5544  }
5545 
5546  // We save charset_output to restore it because write_file can change it if needed for
5547  // output format that does not support UTF8.
5548  $sav_charset_output = empty($outputlangs->charset_output) ? '' : $outputlangs->charset_output;
5549 
5550  if (in_array(get_class($this), array('Adherent'))) {
5551  $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, 'tmp_cards', $moreparams);
5552  } else {
5553  $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
5554  }
5555  // After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
5556 
5557  if ($resultwritefile > 0) {
5558  $outputlangs->charset_output = $sav_charset_output;
5559 
5560  // We delete old preview
5561  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5562  dol_delete_preview($this);
5563 
5564  // Index file in database
5565  if (!empty($obj->result['fullpath'])) {
5566  $destfull = $obj->result['fullpath'];
5567 
5568  // Update the last_main_doc field into main object (if document generator has property ->update_main_doc_field set)
5569  $update_main_doc_field = 0;
5570  if (!empty($obj->update_main_doc_field)) {
5571  $update_main_doc_field = 1;
5572  }
5573 
5574  // Check that the file exists, before indexing it.
5575  // Hint: It does not exist, if we create a PDF and auto delete the ODT File
5576  if (dol_is_file($destfull)) {
5577  $this->indexFile($destfull, $update_main_doc_field);
5578  }
5579  } else {
5580  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);
5581  }
5582 
5583  // Success in building document. We build meta file.
5584  dol_meta_create($this);
5585 
5586  return 1;
5587  } else {
5588  $outputlangs->charset_output = $sav_charset_output;
5589  $this->error = $obj->error;
5590  $this->errors = $obj->errors;
5591  dol_syslog("Error generating document for ".__CLASS__.". Error: ".$obj->error, LOG_ERR);
5592  return -1;
5593  }
5594  }
5595 
5605  public function indexFile($destfull, $update_main_doc_field)
5606  {
5607  global $conf, $user;
5608 
5609  $upload_dir = dirname($destfull);
5610  $destfile = basename($destfull);
5611  $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
5612 
5613  if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a tmp dir
5614  $filename = basename($destfile);
5615  $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
5616  $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
5617 
5618  include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
5619  $ecmfile = new EcmFiles($this->db);
5620  $result = $ecmfile->fetch(0, '', ($rel_dir ? $rel_dir.'/' : '').$filename);
5621 
5622  // Set the public "share" key
5623  $setsharekey = false;
5624  if ($this->element == 'propal' || $this->element == 'proposal') {
5625  if (!isset($conf->global->PROPOSAL_ALLOW_ONLINESIGN) || !empty($conf->global->PROPOSAL_ALLOW_ONLINESIGN)) {
5626  $setsharekey = true; // feature to make online signature is not set or set to on (default)
5627  }
5628  if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) {
5629  $setsharekey = true;
5630  }
5631  }
5632  if ($this->element == 'commande' && !empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD)) {
5633  $setsharekey = true;
5634  }
5635  if ($this->element == 'facture' && !empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD)) {
5636  $setsharekey = true;
5637  }
5638  if ($this->element == 'bank_account' && !empty($conf->global->BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD)) {
5639  $setsharekey = true;
5640  }
5641  if ($this->element == 'product' && !empty($conf->global->PRODUCT_ALLOW_EXTERNAL_DOWNLOAD)) {
5642  $setsharekey = true;
5643  }
5644  if ($this->element == 'contrat' && !empty($conf->global->CONTRACT_ALLOW_EXTERNAL_DOWNLOAD)) {
5645  $setsharekey = true;
5646  }
5647  if ($this->element == 'fichinter' && !empty($conf->global->FICHINTER_ALLOW_EXTERNAL_DOWNLOAD)) {
5648  $setsharekey = true;
5649  }
5650  if ($this->element == 'supplier_proposal' && !empty($conf->global->SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) {
5651  $setsharekey = true;
5652  }
5653 
5654  if ($setsharekey) {
5655  if (empty($ecmfile->share)) { // Because object not found or share not set yet
5656  require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
5657  $ecmfile->share = getRandomPassword(true);
5658  }
5659  }
5660 
5661  if ($result > 0) {
5662  $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5663  $ecmfile->fullpath_orig = '';
5664  $ecmfile->gen_or_uploaded = 'generated';
5665  $ecmfile->description = ''; // indexed content
5666  $ecmfile->keywords = ''; // keyword content
5667  $result = $ecmfile->update($user);
5668  if ($result < 0) {
5669  setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5670  return -1;
5671  }
5672  } else {
5673  $ecmfile->entity = $conf->entity;
5674  $ecmfile->filepath = $rel_dir;
5675  $ecmfile->filename = $filename;
5676  $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5677  $ecmfile->fullpath_orig = '';
5678  $ecmfile->gen_or_uploaded = 'generated';
5679  $ecmfile->description = ''; // indexed content
5680  $ecmfile->keywords = ''; // keyword content
5681  $ecmfile->src_object_type = $this->table_element; // $this->table_name is 'myobject' or 'mymodule_myobject'.
5682  $ecmfile->src_object_id = $this->id;
5683 
5684  $result = $ecmfile->create($user);
5685  if ($result < 0) {
5686  setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5687  return -1;
5688  }
5689  }
5690 
5691  /*$this->result['fullname']=$destfull;
5692  $this->result['filepath']=$ecmfile->filepath;
5693  $this->result['filename']=$ecmfile->filename;*/
5694  //var_dump($obj->update_main_doc_field);exit;
5695 
5696  if ($update_main_doc_field && !empty($this->table_element)) {
5697  $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath."/".$ecmfile->filename)."'";
5698  $sql .= " WHERE rowid = ".((int) $this->id);
5699 
5700  $resql = $this->db->query($sql);
5701  if (!$resql) {
5702  dol_print_error($this->db);
5703  return -1;
5704  } else {
5705  $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
5706  }
5707  }
5708  }
5709 
5710  return 1;
5711  }
5712 
5720  public function addThumbs($file)
5721  {
5722  $file_osencoded = dol_osencode($file);
5723 
5724  if (file_exists($file_osencoded)) {
5725  require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
5726 
5727  $tmparraysize = getDefaultImageSizes();
5728  $maxwidthsmall = $tmparraysize['maxwidthsmall'];
5729  $maxheightsmall = $tmparraysize['maxheightsmall'];
5730  $maxwidthmini = $tmparraysize['maxwidthmini'];
5731  $maxheightmini = $tmparraysize['maxheightmini'];
5732  //$quality = $tmparraysize['quality'];
5733  $quality = 50; // For thumbs, we force quality to 50
5734 
5735  // Create small thumbs for company (Ratio is near 16/9)
5736  // Used on logon for example
5737  vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
5738 
5739  // Create mini thumbs for company (Ratio is near 16/9)
5740  // Used on menu or for setup page for example
5741  vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
5742  }
5743  }
5744 
5752  public function delThumbs($file)
5753  {
5754  $imgThumbName = getImageFileNameForSize($file, '_small'); // Full path of thumb file
5755  dol_delete_file($imgThumbName);
5756  $imgThumbName = getImageFileNameForSize($file, '_mini'); // Full path of thumb file
5757  dol_delete_file($imgThumbName);
5758  }
5759 
5760 
5761  /* Functions common to commonobject and commonobjectline */
5762 
5763  /* For default values */
5764 
5778  public function getDefaultCreateValueFor($fieldname, $alternatevalue = null, $type = 'alphanohtml')
5779  {
5780  global $conf, $_POST;
5781 
5782  // If param here has been posted, we use this value first.
5783  if (GETPOSTISSET($fieldname)) {
5784  return GETPOST($fieldname, $type, 3);
5785  }
5786 
5787  if (isset($alternatevalue)) {
5788  return $alternatevalue;
5789  }
5790 
5791  $newelement = $this->element;
5792  if ($newelement == 'facture') {
5793  $newelement = 'invoice';
5794  }
5795  if ($newelement == 'commande') {
5796  $newelement = 'order';
5797  }
5798  if (empty($newelement)) {
5799  dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
5800  return '';
5801  }
5802 
5803  $keyforfieldname = strtoupper($newelement.'_DEFAULT_'.$fieldname);
5804  //var_dump($keyforfieldname);
5805  if (isset($conf->global->$keyforfieldname)) {
5806  return $conf->global->$keyforfieldname;
5807  }
5808 
5809  // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
5810  // store content into $conf->cache['overwrite_default']
5811 
5812  return '';
5813  }
5814 
5815 
5816  /* For triggers */
5817 
5818 
5819  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5830  public function call_trigger($triggerName, $user)
5831  {
5832  // phpcs:enable
5833  global $langs, $conf;
5834  if (!empty(self::TRIGGER_PREFIX) && strpos($triggerName, self::TRIGGER_PREFIX . '_') !== 0) {
5835  dol_print_error('', 'The trigger "' . $triggerName . '" does not start with "' . self::TRIGGER_PREFIX . '_" as required.');
5836  exit;
5837  }
5838  if (!is_object($langs)) { // If lang was not defined, we set it. It is required by run_triggers.
5839  include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5840  $langs = new Translate('', $conf);
5841  }
5842 
5843  include_once DOL_DOCUMENT_ROOT.'/core/class/interfaces.class.php';
5844  $interface = new Interfaces($this->db);
5845  $result = $interface->run_triggers($triggerName, $this, $user, $langs, $conf);
5846 
5847  if ($result < 0) {
5848  if (!empty($this->errors)) {
5849  $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.
5850  } else {
5851  $this->errors = $interface->errors;
5852  }
5853  }
5854  return $result;
5855  }
5856 
5857 
5858  /* Functions for data in other language */
5859 
5860 
5869  {
5870  // To avoid SQL errors. Probably not the better solution though
5871  if (!$this->element) {
5872  return 0;
5873  }
5874  if (!($this->id > 0)) {
5875  return 0;
5876  }
5877  if (is_array($this->array_languages)) {
5878  return 1;
5879  }
5880 
5881  $this->array_languages = array();
5882 
5883  $element = $this->element;
5884  if ($element == 'categorie') {
5885  $element = 'categories'; // For compatibility
5886  }
5887 
5888  // Request to get translation values for object
5889  $sql = "SELECT rowid, property, lang , value";
5890  $sql .= " FROM ".$this->db->prefix()."object_lang";
5891  $sql .= " WHERE type_object = '".$this->db->escape($element)."'";
5892  $sql .= " AND fk_object = ".((int) $this->id);
5893 
5894  //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
5895  $resql = $this->db->query($sql);
5896  if ($resql) {
5897  $numrows = $this->db->num_rows($resql);
5898  if ($numrows) {
5899  $i = 0;
5900  while ($i < $numrows) {
5901  $obj = $this->db->fetch_object($resql);
5902  $key = $obj->property;
5903  $value = $obj->value;
5904  $codelang = $obj->lang;
5905  $type = $this->fields[$key]['type'];
5906 
5907  // we can add this attribute to object
5908  if (preg_match('/date/', $type)) {
5909  $this->array_languages[$key][$codelang] = $this->db->jdate($value);
5910  } else {
5911  $this->array_languages[$key][$codelang] = $value;
5912  }
5913 
5914  $i++;
5915  }
5916  }
5917 
5918  $this->db->free($resql);
5919 
5920  if ($numrows) {
5921  return $numrows;
5922  } else {
5923  return 0;
5924  }
5925  } else {
5926  dol_print_error($this->db);
5927  return -1;
5928  }
5929  }
5930 
5937  public function setValuesForExtraLanguages($onlykey = '')
5938  {
5939  global $_POST, $langs;
5940 
5941  // Get extra fields
5942  foreach ($_POST as $postfieldkey => $postfieldvalue) {
5943  $tmparray = explode('-', $postfieldkey);
5944  if ($tmparray[0] != 'field') {
5945  continue;
5946  }
5947 
5948  $element = $tmparray[1];
5949  $key = $tmparray[2];
5950  $codelang = $tmparray[3];
5951  //var_dump("postfieldkey=".$postfieldkey." element=".$element." key=".$key." codelang=".$codelang);
5952 
5953  if (!empty($onlykey) && $key != $onlykey) {
5954  continue;
5955  }
5956  if ($element != $this->element) {
5957  continue;
5958  }
5959 
5960  $key_type = $this->fields[$key]['type'];
5961 
5962  $enabled = 1;
5963  if (isset($this->fields[$key]['enabled'])) {
5964  $enabled = dol_eval($this->fields[$key]['enabled'], 1, 1, '1');
5965  }
5966  /*$perms = 1;
5967  if (isset($this->fields[$key]['perms']))
5968  {
5969  $perms = dol_eval($this->fields[$key]['perms'], 1, 1, '1');
5970  }*/
5971  if (empty($enabled)) {
5972  continue;
5973  }
5974  //if (empty($perms)) continue;
5975 
5976  if (in_array($key_type, array('date'))) {
5977  // Clean parameters
5978  // TODO GMT date in memory must be GMT so we should add gm=true in parameters
5979  $value_key = dol_mktime(0, 0, 0, GETPOST($postfieldkey."month", 'int'), GETPOST($postfieldkey."day", 'int'), GETPOST($postfieldkey."year", 'int'));
5980  } elseif (in_array($key_type, array('datetime'))) {
5981  // Clean parameters
5982  // TODO GMT date in memory must be GMT so we should add gm=true in parameters
5983  $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'));
5984  } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
5985  $value_arr = GETPOST($postfieldkey, 'array'); // check if an array
5986  if (!empty($value_arr)) {
5987  $value_key = implode(',', $value_arr);
5988  } else {
5989  $value_key = '';
5990  }
5991  } elseif (in_array($key_type, array('price', 'double'))) {
5992  $value_arr = GETPOST($postfieldkey, 'alpha');
5993  $value_key = price2num($value_arr);
5994  } else {
5995  $value_key = GETPOST($postfieldkey);
5996  if (in_array($key_type, array('link')) && $value_key == '-1') {
5997  $value_key = '';
5998  }
5999  }
6000 
6001  $this->array_languages[$key][$codelang] = $value_key;
6002 
6003  /*if ($nofillrequired) {
6004  $langs->load('errors');
6005  setEventMessages($langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required), null, 'errors');
6006  return -1;
6007  }*/
6008  }
6009 
6010  return 1;
6011  }
6012 
6013 
6014  /* Functions for extrafields */
6015 
6022  public function fetchNoCompute($id)
6023  {
6024  global $conf;
6025 
6026  $savDisableCompute = $conf->disable_compute;
6027  $conf->disable_compute = 1;
6028 
6029  $ret = $this->fetch($id);
6030 
6031  $conf->disable_compute = $savDisableCompute;
6032 
6033  return $ret;
6034  }
6035 
6036  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6046  public function fetch_optionals($rowid = null, $optionsArray = null)
6047  {
6048  // phpcs:enable
6049  global $conf, $extrafields;
6050 
6051  if (empty($rowid)) {
6052  $rowid = $this->id;
6053  }
6054  if (empty($rowid) && isset($this->rowid)) {
6055  $rowid = $this->rowid; // deprecated
6056  }
6057 
6058  // To avoid SQL errors. Probably not the better solution though
6059  if (!$this->table_element) {
6060  return 0;
6061  }
6062 
6063  $this->array_options = array();
6064 
6065  if (!is_array($optionsArray)) {
6066  // If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
6067  if (!isset($extrafields) || !is_object($extrafields)) {
6068  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6069  $extrafields = new ExtraFields($this->db);
6070  }
6071 
6072  // Load array of extrafields for elementype = $this->table_element
6073  if (empty($extrafields->attributes[$this->table_element]['loaded'])) {
6074  $extrafields->fetch_name_optionals_label($this->table_element);
6075  }
6076  $optionsArray = (!empty($extrafields->attributes[$this->table_element]['label']) ? $extrafields->attributes[$this->table_element]['label'] : null);
6077  } else {
6078  global $extrafields;
6079  dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
6080  }
6081 
6082  $table_element = $this->table_element;
6083  if ($table_element == 'categorie') {
6084  $table_element = 'categories'; // For compatibility
6085  }
6086 
6087  // Request to get complementary values
6088  if (is_array($optionsArray) && count($optionsArray) > 0) {
6089  $sql = "SELECT rowid";
6090  foreach ($optionsArray as $name => $label) {
6091  if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate') {
6092  $sql .= ", ".$name;
6093  }
6094  }
6095  $sql .= " FROM ".$this->db->prefix().$table_element."_extrafields";
6096  $sql .= " WHERE fk_object = ".((int) $rowid);
6097 
6098  //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6099  $resql = $this->db->query($sql);
6100  if ($resql) {
6101  $numrows = $this->db->num_rows($resql);
6102  if ($numrows) {
6103  $tab = $this->db->fetch_array($resql);
6104 
6105  foreach ($tab as $key => $value) {
6106  // 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)
6107  if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && !is_int($key)) {
6108  // we can add this attribute to object
6109  if (!empty($extrafields->attributes[$this->table_element]) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date', 'datetime'))) {
6110  //var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
6111  $this->array_options["options_".$key] = $this->db->jdate($value);
6112  } else {
6113  $this->array_options["options_".$key] = $value;
6114  }
6115 
6116  //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
6117  }
6118  }
6119  }
6120 
6121  // If field is a computed field, value must become result of compute (regardless of whether a row exists
6122  // in the element's extrafields table)
6123  if (is_array($extrafields->attributes[$this->table_element]['label'])) {
6124  foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
6125  if (!empty($extrafields->attributes[$this->table_element]) && !empty($extrafields->attributes[$this->table_element]['computed'][$key])) {
6126  //var_dump($conf->disable_compute);
6127  if (empty($conf->disable_compute)) {
6128  global $objectoffield; // We set a global variable to $objectoffield so
6129  $objectoffield = $this; // we can use it inside computed formula
6130  $this->array_options['options_' . $key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0, '');
6131  }
6132  }
6133  }
6134  }
6135 
6136  $this->db->free($resql);
6137 
6138  if ($numrows) {
6139  return $numrows;
6140  } else {
6141  return 0;
6142  }
6143  } else {
6144  $this->errors[]=$this->db->lasterror;
6145  return -1;
6146  }
6147  }
6148  return 0;
6149  }
6150 
6157  public function deleteExtraFields()
6158  {
6159  global $conf;
6160 
6161  if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) {
6162  return 0;
6163  }
6164 
6165  $this->db->begin();
6166 
6167  $table_element = $this->table_element;
6168  if ($table_element == 'categorie') {
6169  $table_element = 'categories'; // For compatibility
6170  }
6171 
6172  dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
6173 
6174  $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6175 
6176  $resql = $this->db->query($sql_del);
6177  if (!$resql) {
6178  $this->error = $this->db->lasterror();
6179  $this->db->rollback();
6180  return -1;
6181  } else {
6182  $this->db->commit();
6183  return 1;
6184  }
6185  }
6186 
6197  public function insertExtraFields($trigger = '', $userused = null)
6198  {
6199  global $conf, $langs, $user;
6200 
6201  if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) {
6202  return 0;
6203  }
6204 
6205  if (empty($userused)) {
6206  $userused = $user;
6207  }
6208 
6209  $error = 0;
6210 
6211  if (!empty($this->array_options)) {
6212  // Check parameters
6213  $langs->load('admin');
6214  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6215  $extrafields = new ExtraFields($this->db);
6216  $target_extrafields = $extrafields->fetch_name_optionals_label($this->table_element);
6217 
6218  // Eliminate copied source object extra fields that do not exist in target object
6219  $new_array_options = array();
6220  foreach ($this->array_options as $key => $value) {
6221  if (in_array(substr($key, 8), array_keys($target_extrafields))) { // We remove the 'options_' from $key for test
6222  $new_array_options[$key] = $value;
6223  } elseif (in_array($key, array_keys($target_extrafields))) { // We test on $key that does not contain the 'options_' prefix
6224  $new_array_options['options_'.$key] = $value;
6225  }
6226  }
6227 
6228  foreach ($new_array_options as $key => $value) {
6229  $attributeKey = substr($key, 8); // Remove 'options_' prefix
6230  $attributeType = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
6231  $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$attributeKey];
6232  $attributeParam = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
6233  $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
6234  $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
6235  $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$attributeKey];
6236 
6237  // If we clone, we have to clean unique extrafields to prevent duplicates.
6238  // This behaviour can be prevented by external code by changing $this->context['createfromclone'] value in createFrom hook
6239  if (!empty($this->context['createfromclone']) && $this->context['createfromclone'] == 'createfromclone' && !empty($attributeUnique)) {
6240  $new_array_options[$key] = null;
6241  }
6242 
6243  // Similar code than into insertExtraFields
6244  if ($attributeRequired) {
6245  $mandatorypb = false;
6246  if ($attributeType == 'link' && $this->array_options[$key] == '-1') {
6247  $mandatorypb = true;
6248  }
6249  if ($this->array_options[$key] === '') {
6250  $mandatorypb = true;
6251  }
6252  if ($attributeType == 'sellist' && $this->array_options[$key] == '0') {
6253  $mandatorypb = true;
6254  }
6255  if ($mandatorypb) {
6256  $langs->load("errors");
6257  dol_syslog("Mandatory field '".$key."' is empty during create and set to required into definition of extrafields");
6258  $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6259  return -1;
6260  }
6261  }
6262 
6263  //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6264  //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6265 
6266  if (!empty($attrfieldcomputed)) {
6267  if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS)) {
6268  $value = dol_eval($attrfieldcomputed, 1, 0, '');
6269  dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
6270  $new_array_options[$key] = $value;
6271  } else {
6272  $new_array_options[$key] = null;
6273  }
6274  }
6275 
6276  switch ($attributeType) {
6277  case 'int':
6278  if (!is_numeric($value) && $value != '') {
6279  $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6280  return -1;
6281  } elseif ($value == '') {
6282  $new_array_options[$key] = null;
6283  }
6284  break;
6285  case 'price':
6286  case 'double':
6287  $value = price2num($value);
6288  if (!is_numeric($value) && $value != '') {
6289  dol_syslog($langs->trans("ExtraFieldHasWrongValue")." for ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6290  $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6291  return -1;
6292  } elseif ($value == '') {
6293  $value = null;
6294  }
6295  //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6296  $new_array_options[$key] = $value;
6297  break;
6298  /*case 'select': // Not required, we chosed value='0' for undefined values
6299  if ($value=='-1')
6300  {
6301  $this->array_options[$key] = null;
6302  }
6303  break;*/
6304  case 'password':
6305  $algo = '';
6306  if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
6307  // If there is an encryption choice, we use it to crypt data before insert
6308  $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
6309  $algo = reset($tmparrays);
6310  if ($algo != '') {
6311  //global $action; // $action may be 'create', 'update', 'update_extras'...
6312  //var_dump($action);
6313  //var_dump($this->oldcopy);exit;
6314  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
6315  //var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]);
6316  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.
6317  $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6318  } else {
6319  // var_dump($algo);
6320  $newvalue = dol_hash($this->array_options[$key], $algo);
6321  $new_array_options[$key] = $newvalue;
6322  }
6323  } else {
6324  $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6325  }
6326  }
6327  } else // Common usage
6328  {
6329  $new_array_options[$key] = $this->array_options[$key];
6330  }
6331  break;
6332  case 'date':
6333  case 'datetime':
6334  // If data is a string instead of a timestamp, we convert it
6335  if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6336  $this->array_options[$key] = strtotime($this->array_options[$key]);
6337  }
6338  $new_array_options[$key] = $this->db->idate($this->array_options[$key]);
6339  break;
6340  case 'datetimegmt':
6341  // If data is a string instead of a timestamp, we convert it
6342  if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6343  $this->array_options[$key] = strtotime($this->array_options[$key]);
6344  }
6345  $new_array_options[$key] = $this->db->idate($this->array_options[$key], 'gmt');
6346  break;
6347  case 'link':
6348  $param_list = array_keys($attributeParam['options']);
6349  // 0 : ObjectName
6350  // 1 : classPath
6351  $InfoFieldList = explode(":", $param_list[0]);
6352  dol_include_once($InfoFieldList[1]);
6353  if ($InfoFieldList[0] && class_exists($InfoFieldList[0])) {
6354  if ($value == '-1') { // -1 is key for no defined in combo list of objects
6355  $new_array_options[$key] = '';
6356  } elseif ($value) {
6357  $object = new $InfoFieldList[0]($this->db);
6358  if (is_numeric($value)) {
6359  $res = $object->fetch($value); // Common case
6360  } else {
6361  $res = $object->fetch('', $value); // For compatibility
6362  }
6363 
6364  if ($res > 0) {
6365  $new_array_options[$key] = $object->id;
6366  } else {
6367  $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
6368  return -1;
6369  }
6370  }
6371  } else {
6372  dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6373  }
6374  break;
6375  case 'checkbox':
6376  case 'chkbxlst':
6377  if (is_array($this->array_options[$key])) {
6378  $new_array_options[$key] = implode(',', $this->array_options[$key]);
6379  } else {
6380  $new_array_options[$key] = $this->array_options[$key];
6381  }
6382  break;
6383  }
6384  }
6385 
6386  $this->db->begin();
6387 
6388  $table_element = $this->table_element;
6389  if ($table_element == 'categorie') {
6390  $table_element = 'categories'; // For compatibility
6391  }
6392 
6393  dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG);
6394 
6395  $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6396  $this->db->query($sql_del);
6397 
6398  $sql = "INSERT INTO ".$this->db->prefix().$table_element."_extrafields (fk_object";
6399  foreach ($new_array_options as $key => $value) {
6400  $attributeKey = substr($key, 8); // Remove 'options_' prefix
6401  // Add field of attribut
6402  if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator
6403  $sql .= ",".$attributeKey;
6404  }
6405  }
6406  // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6407  if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6408  foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6409  if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6410  $sql .= ",".$tmpkey;
6411  }
6412  }
6413  }
6414  $sql .= ") VALUES (".$this->id;
6415 
6416  foreach ($new_array_options as $key => $value) {
6417  $attributeKey = substr($key, 8); // Remove 'options_' prefix
6418  // Add field of attribute
6419  if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator)
6420  if ($new_array_options[$key] != '' || $new_array_options[$key] == '0') {
6421  $sql .= ",'".$this->db->escape($new_array_options[$key])."'";
6422  } else {
6423  $sql .= ",null";
6424  }
6425  }
6426  }
6427  // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6428  if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6429  foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6430  if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6431  if (in_array($tmpval, array('int', 'double', 'price'))) {
6432  $sql .= ", 0";
6433  } else {
6434  $sql .= ", ''";
6435  }
6436  }
6437  }
6438  }
6439 
6440  $sql .= ")";
6441 
6442  $resql = $this->db->query($sql);
6443  if (!$resql) {
6444  $this->error = $this->db->lasterror();
6445  $error++;
6446  }
6447 
6448  if (!$error && $trigger) {
6449  // Call trigger
6450  $this->context = array('extrafieldaddupdate'=>1);
6451  $result = $this->call_trigger($trigger, $userused);
6452  if ($result < 0) {
6453  $error++;
6454  }
6455  // End call trigger
6456  }
6457 
6458  if ($error) {
6459  $this->db->rollback();
6460  return -1;
6461  } else {
6462  $this->db->commit();
6463  return 1;
6464  }
6465  } else {
6466  return 0;
6467  }
6468  }
6469 
6480  public function insertExtraLanguages($trigger = '', $userused = null)
6481  {
6482  global $conf, $langs, $user;
6483 
6484  if (empty($userused)) {
6485  $userused = $user;
6486  }
6487 
6488  $error = 0;
6489 
6490  if (!empty($conf->global->MAIN_EXTRALANGUAGES_DISABLED)) {
6491  return 0; // For avoid conflicts if trigger used
6492  }
6493 
6494  if (is_array($this->array_languages)) {
6495  $new_array_languages = $this->array_languages;
6496 
6497  foreach ($new_array_languages as $key => $value) {
6498  $attributeKey = $key;
6499  $attributeType = $this->fields[$attributeKey]['type'];
6500  $attributeLabel = $this->fields[$attributeKey]['label'];
6501 
6502  //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6503  //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6504 
6505  switch ($attributeType) {
6506  case 'int':
6507  if (!is_numeric($value) && $value != '') {
6508  $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6509  return -1;
6510  } elseif ($value == '') {
6511  $new_array_languages[$key] = null;
6512  }
6513  break;
6514  case 'double':
6515  $value = price2num($value);
6516  if (!is_numeric($value) && $value != '') {
6517  dol_syslog($langs->trans("ExtraLanguageHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6518  $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6519  return -1;
6520  } elseif ($value == '') {
6521  $new_array_languages[$key] = null;
6522  } else {
6523  $new_array_languages[$key] = $value;
6524  }
6525  break;
6526  /*case 'select': // Not required, we chosed value='0' for undefined values
6527  if ($value=='-1')
6528  {
6529  $this->array_options[$key] = null;
6530  }
6531  break;*/
6532  }
6533  }
6534 
6535  $this->db->begin();
6536 
6537  $table_element = $this->table_element;
6538  if ($table_element == 'categorie') {
6539  $table_element = 'categories'; // For compatibility
6540  }
6541 
6542  dol_syslog(get_class($this)."::insertExtraLanguages delete then insert", LOG_DEBUG);
6543 
6544  foreach ($new_array_languages as $key => $langcodearray) { // $key = 'name', 'town', ...
6545  foreach ($langcodearray as $langcode => $value) {
6546  $sql_del = "DELETE FROM ".$this->db->prefix()."object_lang";
6547  $sql_del .= " WHERE fk_object = ".((int) $this->id)." AND property = '".$this->db->escape($key)."' AND type_object = '".$this->db->escape($table_element)."'";
6548  $sql_del .= " AND lang = '".$this->db->escape($langcode)."'";
6549  $this->db->query($sql_del);
6550 
6551  if ($value !== '') {
6552  $sql = "INSERT INTO ".$this->db->prefix()."object_lang (fk_object, property, type_object, lang, value";
6553  $sql .= ") VALUES (".$this->id.", '".$this->db->escape($key)."', '".$this->db->escape($table_element)."', '".$this->db->escape($langcode)."', '".$this->db->escape($value)."'";
6554  $sql .= ")";
6555 
6556  $resql = $this->db->query($sql);
6557  if (!$resql) {
6558  $this->error = $this->db->lasterror();
6559  $error++;
6560  break;
6561  }
6562  }
6563  }
6564  }
6565 
6566  if (!$error && $trigger) {
6567  // Call trigger
6568  $this->context = array('extralanguagesaddupdate'=>1);
6569  $result = $this->call_trigger($trigger, $userused);
6570  if ($result < 0) {
6571  $error++;
6572  }
6573  // End call trigger
6574  }
6575 
6576  if ($error) {
6577  $this->db->rollback();
6578  return -1;
6579  } else {
6580  $this->db->commit();
6581  return 1;
6582  }
6583  } else {
6584  return 0;
6585  }
6586  }
6587 
6598  public function updateExtraField($key, $trigger = null, $userused = null)
6599  {
6600  global $conf, $langs, $user;
6601 
6602  if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) {
6603  return 0;
6604  }
6605 
6606  if (empty($userused)) {
6607  $userused = $user;
6608  }
6609 
6610  $error = 0;
6611 
6612  if (!empty($this->array_options) && isset($this->array_options["options_".$key])) {
6613  // Check parameters
6614  $langs->load('admin');
6615  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6616  $extrafields = new ExtraFields($this->db);
6617  $extrafields->fetch_name_optionals_label($this->table_element);
6618 
6619  $value = $this->array_options["options_".$key];
6620 
6621  $attributeType = $extrafields->attributes[$this->table_element]['type'][$key];
6622  $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key];
6623  $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key];
6624  $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
6625  $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$key];
6626 
6627  // Similar code than into insertExtraFields
6628  if ($attributeRequired) {
6629  $mandatorypb = false;
6630  if ($attributeType == 'link' && $this->array_options["options_".$key] == '-1') {
6631  $mandatorypb = true;
6632  }
6633  if ($this->array_options["options_".$key] === '') {
6634  $mandatorypb = true;
6635  }
6636  if ($mandatorypb) {
6637  $langs->load("errors");
6638  dol_syslog("Mandatory field 'options_".$key."' is empty during update and set to required into definition of extrafields");
6639  $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6640  return -1;
6641  }
6642  }
6643 
6644  //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6645  //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6646 
6647  if (!empty($attrfieldcomputed)) {
6648  if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS)) {
6649  $value = dol_eval($attrfieldcomputed, 1, 0, '');
6650  dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
6651  $this->array_options["options_".$key] = $value;
6652  } else {
6653  $this->array_options["options_".$key] = null;
6654  }
6655  }
6656 
6657  switch ($attributeType) {
6658  case 'int':
6659  if (!is_numeric($value) && $value != '') {
6660  $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6661  return -1;
6662  } elseif ($value === '') {
6663  $this->array_options["options_".$key] = null;
6664  }
6665  break;
6666  case 'price':
6667  case 'double':
6668  $value = price2num($value);
6669  if (!is_numeric($value) && $value != '') {
6670  dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6671  $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6672  return -1;
6673  } elseif ($value === '') {
6674  $value = null;
6675  }
6676  //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6677  $this->array_options["options_".$key] = $value;
6678  break;
6679  /*case 'select': // Not required, we chosed value='0' for undefined values
6680  if ($value=='-1')
6681  {
6682  $this->array_options[$key] = null;
6683  }
6684  break;*/
6685  case 'date':
6686  case 'datetime':
6687  if (empty($this->array_options["options_".$key])) {
6688  $this->array_options["options_".$key] = null;
6689  } else {
6690  $this->array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key]);
6691  }
6692  break;
6693  case 'datetimegmt':
6694  if (empty($this->array_options["options_".$key])) {
6695  $this->array_options["options_".$key] = null;
6696  } else {
6697  $this->array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key], 'gmt');
6698  }
6699  break;
6700  case 'boolean':
6701  if (empty($this->array_options["options_".$key])) {
6702  $this->array_options["options_".$key] = null;
6703  }
6704  break;
6705  case 'link':
6706  if ($this->array_options["options_".$key] === '') {
6707  $this->array_options["options_".$key] = null;
6708  }
6709  break;
6710  /*
6711  case 'link':
6712  $param_list = array_keys($attributeParam['options']);
6713  // 0 : ObjectName
6714  // 1 : classPath
6715  $InfoFieldList = explode(":", $param_list[0]);
6716  dol_include_once($InfoFieldList[1]);
6717  if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
6718  {
6719  if ($value == '-1') // -1 is key for no defined in combo list of objects
6720  {
6721  $new_array_options[$key] = '';
6722  } elseif ($value) {
6723  $object = new $InfoFieldList[0]($this->db);
6724  if (is_numeric($value)) $res = $object->fetch($value); // Common case
6725  else $res = $object->fetch('', $value); // For compatibility
6726 
6727  if ($res > 0) $new_array_options[$key] = $object->id;
6728  else {
6729  $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
6730  $this->db->rollback();
6731  return -1;
6732  }
6733  }
6734  } else {
6735  dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6736  }
6737  break;
6738  */
6739  case 'checkbox':
6740  case 'chkbxlst':
6741  if (is_array($this->array_options[$key])) {
6742  $new_array_options[$key] = implode(',', $this->array_options[$key]);
6743  } else {
6744  $new_array_options[$key] = $this->array_options[$key];
6745  }
6746  break;
6747  }
6748 
6749  $this->db->begin();
6750 
6751  $linealreadyfound = 0;
6752 
6753  // 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)
6754  $sql = "SELECT COUNT(rowid) as nb FROM ".$this->db->prefix().$this->table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6755  $resql = $this->db->query($sql);
6756  if ($resql) {
6757  $tmpobj = $this->db->fetch_object($resql);
6758  if ($tmpobj) {
6759  $linealreadyfound = $tmpobj->nb;
6760  }
6761  }
6762 
6763  if ($linealreadyfound) {
6764  if ($this->array_options["options_".$key] === null) {
6765  $sql = "UPDATE ".$this->db->prefix().$this->table_element."_extrafields SET ".$key." = null";
6766  } else {
6767  $sql = "UPDATE ".$this->db->prefix().$this->table_element."_extrafields SET ".$key." = '".$this->db->escape($this->array_options["options_".$key])."'";
6768  }
6769  $sql .= " WHERE fk_object = ".((int) $this->id);
6770  } else {
6771  $result = $this->insertExtraFields('', $user);
6772  if ($result < 0) {
6773  $error++;
6774  }
6775  }
6776 
6777  $resql = $this->db->query($sql);
6778  if (!$resql) {
6779  $error++;
6780  $this->error = $this->db->lasterror();
6781  }
6782  if (!$error && $trigger) {
6783  // Call trigger
6784  $this->context = array('extrafieldupdate'=>1);
6785  $result = $this->call_trigger($trigger, $userused);
6786  if ($result < 0) {
6787  $error++;
6788  }
6789  // End call trigger
6790  }
6791 
6792  if ($error) {
6793  dol_syslog(__METHOD__.$this->error, LOG_ERR);
6794  $this->db->rollback();
6795  return -1;
6796  } else {
6797  $this->db->commit();
6798  return 1;
6799  }
6800  } else {
6801  return 0;
6802  }
6803  }
6804 
6815  public function updateExtraLanguages($key, $trigger = null, $userused = null)
6816  {
6817  global $conf, $langs, $user;
6818 
6819  if (empty($userused)) {
6820  $userused = $user;
6821  }
6822 
6823  $error = 0;
6824 
6825  if (!empty($conf->global->MAIN_EXTRALANGUAGES_DISABLED)) {
6826  return 0; // For avoid conflicts if trigger used
6827  }
6828 
6829  return 0;
6830  }
6831 
6832 
6847  public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0, $nonewbutton = 0)
6848  {
6849  global $conf, $langs, $form;
6850 
6851  if (!is_object($form)) {
6852  require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
6853  $form = new Form($this->db);
6854  }
6855 
6856  if (!empty($this->fields)) {
6857  $val = $this->fields[$key];
6858  }
6859 
6860  // Validation tests and output
6861  $fieldValidationErrorMsg = '';
6862  $validationClass = '';
6863  $fieldValidationErrorMsg = $this->getFieldError($key);
6864  if (!empty($fieldValidationErrorMsg)) {
6865  $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
6866  } else {
6867  $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
6868  }
6869 
6870  $out = '';
6871  $type = '';
6872  $isDependList = 0;
6873  $param = array();
6874  $param['options'] = array();
6875  $reg = array();
6876  $size = !empty($this->fields[$key]['size']) ? $this->fields[$key]['size'] : 0;
6877  // Because we work on extrafields
6878  if (preg_match('/^(integer|link):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6879  $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
6880  $type = 'link';
6881  } elseif (preg_match('/^(integer|link):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6882  $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
6883  $type = 'link';
6884  } elseif (preg_match('/^(integer|link):(.*):(.*)/i', $val['type'], $reg)) {
6885  $param['options'] = array($reg[2].':'.$reg[3] => 'N');
6886  $type = 'link';
6887  } elseif (preg_match('/^(sellist):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6888  $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
6889  $type = 'sellist';
6890  } elseif (preg_match('/^(sellist):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6891  $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
6892  $type = 'sellist';
6893  } elseif (preg_match('/^(sellist):(.*):(.*)/i', $val['type'], $reg)) {
6894  $param['options'] = array($reg[2].':'.$reg[3] => 'N');
6895  $type = 'sellist';
6896  } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
6897  $param['options'] = array($reg[1] => 'N');
6898  $type = 'chkbxlst';
6899  } elseif (preg_match('/varchar\‍((\d+)\‍)/', $val['type'], $reg)) {
6900  $param['options'] = array();
6901  $type = 'varchar';
6902  $size = $reg[1];
6903  } elseif (preg_match('/varchar/', $val['type'])) {
6904  $param['options'] = array();
6905  $type = 'varchar';
6906  } else {
6907  $param['options'] = array();
6908  $type = $this->fields[$key]['type'];
6909  }
6910 
6911  // Special case that force options and type ($type can be integer, varchar, ...)
6912  if (!empty($this->fields[$key]['arrayofkeyval']) && is_array($this->fields[$key]['arrayofkeyval'])) {
6913  $param['options'] = $this->fields[$key]['arrayofkeyval'];
6914  $type = 'select';
6915  }
6916 
6917  $label = $this->fields[$key]['label'];
6918  //$elementtype=$this->fields[$key]['elementtype']; // Seems not used
6919  $default = (!empty($this->fields[$key]['default']) ? $this->fields[$key]['default'] : '');
6920  $computed = (!empty($this->fields[$key]['computed']) ? $this->fields[$key]['computed'] : '');
6921  $unique = (!empty($this->fields[$key]['unique']) ? $this->fields[$key]['unique'] : 0);
6922  $required = (!empty($this->fields[$key]['required']) ? $this->fields[$key]['required'] : 0);
6923  $autofocusoncreate = (!empty($this->fields[$key]['autofocusoncreate']) ? $this->fields[$key]['autofocusoncreate'] : 0);
6924 
6925  $langfile = (!empty($this->fields[$key]['langfile']) ? $this->fields[$key]['langfile'] : '');
6926  $list = (!empty($this->fields[$key]['list']) ? $this->fields[$key]['list'] : 0);
6927  $hidden = (in_array(abs($this->fields[$key]['visible']), array(0, 2)) ? 1 : 0);
6928 
6929  $objectid = $this->id;
6930 
6931  if ($computed) {
6932  if (!preg_match('/^search_/', $keyprefix)) {
6933  return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
6934  } else {
6935  return '';
6936  }
6937  }
6938 
6939  // Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine
6940  if (empty($morecss) && !empty($val['css'])) {
6941  $morecss = $val['css'];
6942  } elseif (empty($morecss)) {
6943  if ($type == 'date') {
6944  $morecss = 'minwidth100imp';
6945  } elseif ($type == 'datetime' || $type == 'link') { // link means an foreign key to another primary id
6946  $morecss = 'minwidth200imp';
6947  } elseif (in_array($type, array('int', 'integer', 'price')) || preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', $type)) {
6948  $morecss = 'maxwidth75';
6949  } elseif ($type == 'url') {
6950  $morecss = 'minwidth400';
6951  } elseif ($type == 'boolean') {
6952  $morecss = '';
6953  } else {
6954  if (round($size) < 12) {
6955  $morecss = 'minwidth100';
6956  } elseif (round($size) <= 48) {
6957  $morecss = 'minwidth200';
6958  } else {
6959  $morecss = 'minwidth400';
6960  }
6961  }
6962  }
6963 
6964  // Add validation state class
6965  if (!empty($validationClass)) {
6966  $morecss.= $validationClass;
6967  }
6968 
6969  if (in_array($type, array('date'))) {
6970  $tmp = explode(',', $size);
6971  $newsize = $tmp[0];
6972  $showtime = 0;
6973 
6974  // Do not show current date when field not required (see selectDate() method)
6975  if (!$required && $value == '') {
6976  $value = '-1';
6977  }
6978 
6979  // TODO Must also support $moreparam
6980  $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
6981  } elseif (in_array($type, array('datetime'))) {
6982  $tmp = explode(',', $size);
6983  $newsize = $tmp[0];
6984  $showtime = 1;
6985 
6986  // Do not show current date when field not required (see selectDate() method)
6987  if (!$required && $value == '') $value = '-1';
6988 
6989  // TODO Must also support $moreparam
6990  $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
6991  } elseif (in_array($type, array('duration'))) {
6992  $out = $form->select_duration($keyprefix.$key.$keysuffix, $value, 0, 'text', 0, 1);
6993  } elseif (in_array($type, array('int', 'integer'))) {
6994  $tmp = explode(',', $size);
6995  $newsize = $tmp[0];
6996  $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' : '').'>';
6997  } elseif (in_array($type, array('real'))) {
6998  $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
6999  } elseif (preg_match('/varchar/', $type)) {
7000  $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' : '').'>';
7001  } elseif (in_array($type, array('email', 'mail', 'phone', 'url', 'ip'))) {
7002  $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7003  } elseif (preg_match('/^text/', $type)) {
7004  if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7005  require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7006  $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
7007  $out = $doleditor->Create(1);
7008  } else {
7009  $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7010  }
7011  } elseif (preg_match('/^html/', $type)) {
7012  if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7013  require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7014  $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_5, '90%');
7015  $out = $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
7016  } else {
7017  $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7018  }
7019  } elseif ($type == 'boolean') {
7020  $checked = '';
7021  if (!empty($value)) {
7022  $checked = ' checked value="1" ';
7023  } else {
7024  $checked = ' value="1" ';
7025  }
7026  $out = '<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
7027  } elseif ($type == 'price') {
7028  if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7029  $value = price($value);
7030  }
7031  $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '.$langs->getCurrencySymbol($conf->currency);
7032  } elseif (preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', $type)) {
7033  if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7034  $value = price($value);
7035  }
7036  $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
7037  } elseif ($type == 'select') { // combo list
7038  $out = '';
7039  if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_EXTRAFIELDS_DISABLE_SELECT2)) {
7040  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7041  $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7042  }
7043 
7044  $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7045  if ((!isset($this->fields[$key]['default'])) || ($this->fields[$key]['notnull'] != 1)) {
7046  $out .= '<option value="0">&nbsp;</option>';
7047  }
7048  foreach ($param['options'] as $keyb => $valb) {
7049  if ((string) $keyb == '') {
7050  continue;
7051  }
7052  if (strpos($valb, "|") !== false) {
7053  list($valb, $parent) = explode('|', $valb);
7054  }
7055  $out .= '<option value="'.$keyb.'"';
7056  $out .= (((string) $value == (string) $keyb) ? ' selected' : '');
7057  $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
7058  $out .= '>'.$valb.'</option>';
7059  }
7060  $out .= '</select>';
7061  } elseif ($type == 'sellist') {
7062  $out = '';
7063  if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_EXTRAFIELDS_DISABLE_SELECT2)) {
7064  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7065  $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7066  }
7067 
7068  $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7069  if (is_array($param['options'])) {
7070  $param_list = array_keys($param['options']);
7071  $InfoFieldList = explode(":", $param_list[0]);
7072  $parentName = '';
7073  $parentField = '';
7074  // 0 : tableName
7075  // 1 : label field name
7076  // 2 : key fields name (if differ of rowid)
7077  // 3 : key field parent (for dependent lists)
7078  // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
7079  // 5 : id category type
7080  // 6 : ids categories list separated by comma for category root
7081  $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7082 
7083  if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7084  if (strpos($InfoFieldList[4], 'extra.') !== false) {
7085  $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7086  } else {
7087  $keyList = $InfoFieldList[2].' as rowid';
7088  }
7089  }
7090  if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7091  list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7092  $keyList .= ', '.$parentField;
7093  }
7094 
7095  $filter_categorie = false;
7096  if (count($InfoFieldList) > 5) {
7097  if ($InfoFieldList[0] == 'categorie') {
7098  $filter_categorie = true;
7099  }
7100  }
7101 
7102  if ($filter_categorie === false) {
7103  $fields_label = explode('|', $InfoFieldList[1]);
7104  if (is_array($fields_label)) {
7105  $keyList .= ', ';
7106  $keyList .= implode(', ', $fields_label);
7107  }
7108 
7109  $sqlwhere = '';
7110  $sql = "SELECT " . $keyList;
7111  $sql .= " FROM " . $this->db->prefix() . $InfoFieldList[0];
7112  if (!empty($InfoFieldList[4])) {
7113  // can use SELECT request
7114  if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7115  $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7116  }
7117 
7118  // current object id can be use into filter
7119  if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7120  $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
7121  } else {
7122  $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7123  }
7124 
7125  //We have to join on extrafield table
7126  if (strpos($InfoFieldList[4], 'extra') !== false) {
7127  $sql .= " as main, " . $this->db->prefix() . $InfoFieldList[0] . "_extrafields as extra";
7128  $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2] . " AND " . $InfoFieldList[4];
7129  } else {
7130  $sqlwhere .= " WHERE " . $InfoFieldList[4];
7131  }
7132  } else {
7133  $sqlwhere .= ' WHERE 1=1';
7134  }
7135  // Some tables may have field, some other not. For the moment we disable it.
7136  if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7137  $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7138  }
7139  $sql .= $sqlwhere;
7140  //print $sql;
7141 
7142  $sql .= ' ORDER BY ' . implode(', ', $fields_label);
7143 
7144  dol_syslog(get_class($this) . '::showInputField type=sellist', LOG_DEBUG);
7145  $resql = $this->db->query($sql);
7146  if ($resql) {
7147  $out .= '<option value="0">&nbsp;</option>';
7148  $num = $this->db->num_rows($resql);
7149  $i = 0;
7150  while ($i < $num) {
7151  $labeltoshow = '';
7152  $obj = $this->db->fetch_object($resql);
7153 
7154  // Several field into label (eq table:code|libelle:rowid)
7155  $notrans = false;
7156  $fields_label = explode('|', $InfoFieldList[1]);
7157  if (count($fields_label) > 1) {
7158  $notrans = true;
7159  foreach ($fields_label as $field_toshow) {
7160  $labeltoshow .= $obj->$field_toshow . ' ';
7161  }
7162  } else {
7163  $labeltoshow = $obj->{$InfoFieldList[1]};
7164  }
7165  $labeltoshow = dol_trunc($labeltoshow, 45);
7166 
7167  if ($value == $obj->rowid) {
7168  foreach ($fields_label as $field_toshow) {
7169  $translabel = $langs->trans($obj->$field_toshow);
7170  if ($translabel != $obj->$field_toshow) {
7171  $labeltoshow = dol_trunc($translabel) . ' ';
7172  } else {
7173  $labeltoshow = dol_trunc($obj->$field_toshow) . ' ';
7174  }
7175  }
7176  $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7177  } else {
7178  if (!$notrans) {
7179  $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7180  if ($translabel != $obj->{$InfoFieldList[1]}) {
7181  $labeltoshow = dol_trunc($translabel, 18);
7182  } else {
7183  $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]});
7184  }
7185  }
7186  if (empty($labeltoshow)) {
7187  $labeltoshow = '(not defined)';
7188  }
7189  if ($value == $obj->rowid) {
7190  $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7191  }
7192 
7193  if (!empty($InfoFieldList[3]) && $parentField) {
7194  $parent = $parentName . ':' . $obj->{$parentField};
7195  $isDependList = 1;
7196  }
7197 
7198  $out .= '<option value="' . $obj->rowid . '"';
7199  $out .= ($value == $obj->rowid ? ' selected' : '');
7200  $out .= (!empty($parent) ? ' parent="' . $parent . '"' : '');
7201  $out .= '>' . $labeltoshow . '</option>';
7202  }
7203 
7204  $i++;
7205  }
7206  $this->db->free($resql);
7207  } else {
7208  print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
7209  }
7210  } else {
7211  require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7212  $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
7213  $out .= '<option value="0">&nbsp;</option>';
7214  foreach ($data as $data_key => $data_value) {
7215  $out .= '<option value="' . $data_key . '"';
7216  $out .= ($value == $data_key ? ' selected' : '');
7217  $out .= '>' . $data_value . '</option>';
7218  }
7219  }
7220  }
7221  $out .= '</select>';
7222  } elseif ($type == 'checkbox') {
7223  $value_arr = explode(',', $value);
7224  $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ?null:$param['options']), $value_arr, '', 0, $morecss, 0, '100%');
7225  } elseif ($type == 'radio') {
7226  $out = '';
7227  foreach ($param['options'] as $keyopt => $valopt) {
7228  $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
7229  $out .= ' value="'.$keyopt.'"';
7230  $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
7231  $out .= ($value == $keyopt ? 'checked' : '');
7232  $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$valopt.'</label><br>';
7233  }
7234  } elseif ($type == 'chkbxlst') {
7235  if (is_array($value)) {
7236  $value_arr = $value;
7237  } else {
7238  $value_arr = explode(',', $value);
7239  }
7240 
7241  if (is_array($param['options'])) {
7242  $param_list = array_keys($param['options']);
7243  $InfoFieldList = explode(":", $param_list[0]);
7244  $parentName = '';
7245  $parentField = '';
7246  // 0 : tableName
7247  // 1 : label field name
7248  // 2 : key fields name (if differ of rowid)
7249  // 3 : key field parent (for dependent lists)
7250  // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
7251  // 5 : id category type
7252  // 6 : ids categories list separated by comma for category root
7253  $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7254 
7255  if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7256  list ($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7257  $keyList .= ', '.$parentField;
7258  }
7259  if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7260  if (strpos($InfoFieldList[4], 'extra.') !== false) {
7261  $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7262  } else {
7263  $keyList = $InfoFieldList[2].' as rowid';
7264  }
7265  }
7266 
7267  $filter_categorie = false;
7268  if (count($InfoFieldList) > 5) {
7269  if ($InfoFieldList[0] == 'categorie') {
7270  $filter_categorie = true;
7271  }
7272  }
7273 
7274  if ($filter_categorie === false) {
7275  $fields_label = explode('|', $InfoFieldList[1]);
7276  if (is_array($fields_label)) {
7277  $keyList .= ', ';
7278  $keyList .= implode(', ', $fields_label);
7279  }
7280 
7281  $sqlwhere = '';
7282  $sql = "SELECT " . $keyList;
7283  $sql .= ' FROM ' . $this->db->prefix() . $InfoFieldList[0];
7284  if (!empty($InfoFieldList[4])) {
7285  // can use SELECT request
7286  if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7287  $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7288  }
7289 
7290  // current object id can be use into filter
7291  if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7292  $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
7293  } else {
7294  $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7295  }
7296 
7297  // We have to join on extrafield table
7298  if (strpos($InfoFieldList[4], 'extra') !== false) {
7299  $sql .= ' as main, ' . $this->db->prefix() . $InfoFieldList[0] . '_extrafields as extra';
7300  $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2] . " AND " . $InfoFieldList[4];
7301  } else {
7302  $sqlwhere .= " WHERE " . $InfoFieldList[4];
7303  }
7304  } else {
7305  $sqlwhere .= ' WHERE 1=1';
7306  }
7307  // Some tables may have field, some other not. For the moment we disable it.
7308  if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7309  $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7310  }
7311  // $sql.=preg_replace('/^ AND /','',$sqlwhere);
7312  // print $sql;
7313 
7314  $sql .= $sqlwhere;
7315  dol_syslog(get_class($this) . '::showInputField type=chkbxlst', LOG_DEBUG);
7316  $resql = $this->db->query($sql);
7317  if ($resql) {
7318  $num = $this->db->num_rows($resql);
7319  $i = 0;
7320 
7321  $data = array();
7322 
7323  while ($i < $num) {
7324  $labeltoshow = '';
7325  $obj = $this->db->fetch_object($resql);
7326 
7327  $notrans = false;
7328  // Several field into label (eq table:code|libelle:rowid)
7329  $fields_label = explode('|', $InfoFieldList[1]);
7330  if (count($fields_label) > 1) {
7331  $notrans = true;
7332  foreach ($fields_label as $field_toshow) {
7333  $labeltoshow .= $obj->$field_toshow . ' ';
7334  }
7335  } else {
7336  $labeltoshow = $obj->{$InfoFieldList[1]};
7337  }
7338  $labeltoshow = dol_trunc($labeltoshow, 45);
7339 
7340  if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7341  foreach ($fields_label as $field_toshow) {
7342  $translabel = $langs->trans($obj->$field_toshow);
7343  if ($translabel != $obj->$field_toshow) {
7344  $labeltoshow = dol_trunc($translabel, 18) . ' ';
7345  } else {
7346  $labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
7347  }
7348  }
7349 
7350  $data[$obj->rowid] = $labeltoshow;
7351  } else {
7352  if (!$notrans) {
7353  $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7354  if ($translabel != $obj->{$InfoFieldList[1]}) {
7355  $labeltoshow = dol_trunc($translabel, 18);
7356  } else {
7357  $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
7358  }
7359  }
7360  if (empty($labeltoshow)) {
7361  $labeltoshow = '(not defined)';
7362  }
7363 
7364  if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7365  $data[$obj->rowid] = $labeltoshow;
7366  }
7367 
7368  if (!empty($InfoFieldList[3]) && $parentField) {
7369  $parent = $parentName . ':' . $obj->{$parentField};
7370  $isDependList = 1;
7371  }
7372 
7373  $data[$obj->rowid] = $labeltoshow;
7374  }
7375 
7376  $i++;
7377  }
7378  $this->db->free($resql);
7379 
7380  $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, '', 0, $morecss, 0, '100%');
7381  } else {
7382  print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
7383  }
7384  } else {
7385  require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7386  $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
7387  $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, '', 0, $morecss, 0, '100%');
7388  }
7389  }
7390  } elseif ($type == 'link') {
7391  $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
7392  $param_list_array = explode(':', $param_list[0]);
7393  $showempty = (($required && $default != '') ? 0 : 1);
7394 
7395  if (!preg_match('/search_/', $keyprefix)) {
7396  if (!empty($param_list_array[2])) { // If the entry into $fields is set to add a create button
7397  if (!empty($this->fields[$key]['picto'])) {
7398  $morecss .= ' widthcentpercentminusxx';
7399  } else {
7400  $morecss .= ' widthcentpercentminusx';
7401  }
7402  } else {
7403  if (!empty($this->fields[$key]['picto'])) {
7404  $morecss .= ' widthcentpercentminusx';
7405  }
7406  }
7407  }
7408 
7409  $out = $form->selectForForms($param_list[0], $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, $moreparam, 0, empty($val['disabled']) ? 0 : 1);
7410 
7411  if (!empty($param_list_array[2])) { // If the entry into $fields is set, we must add a create button
7412  if ((!GETPOSTISSET('backtopage') || strpos(GETPOST('backtopage'), $_SERVER['PHP_SELF']) === 0) // // To avoid to open several times the 'Plus' button (we accept only one level)
7413  && empty($val['disabled']) && empty($nonewbutton)) { // and to avoid to show the button if the field is protected by a "disabled".
7414  list($class, $classfile) = explode(':', $param_list[0]);
7415  if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) {
7416  $url_path = dol_buildpath(dirname(dirname($classfile)).'/card.php', 1);
7417  } else {
7418  $url_path = dol_buildpath(dirname(dirname($classfile)).'/'.strtolower($class).'_card.php', 1);
7419  }
7420  $paramforthenewlink = '';
7421  $paramforthenewlink .= (GETPOSTISSET('action') ? '&action='.GETPOST('action', 'aZ09') : '');
7422  $paramforthenewlink .= (GETPOSTISSET('id') ? '&id='.GETPOST('id', 'int') : '');
7423  $paramforthenewlink .= (GETPOSTISSET('origin') ? '&origin='.GETPOST('origin', 'aZ09') : '');
7424  $paramforthenewlink .= (GETPOSTISSET('originid') ? '&originid='.GETPOST('originid', 'int') : '');
7425  $paramforthenewlink .= '&fk_'.strtolower($class).'=--IDFORBACKTOPAGE--';
7426  // TODO Add Javascript code to add input fields already filled into $paramforthenewlink so we won't loose them when going back to main page
7427  $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>';
7428  }
7429  }
7430  } elseif ($type == 'password') {
7431  // If prefix is 'search_', field is used as a filter, we use a common text field.
7432  $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
7433  } elseif ($type == 'array') {
7434  $newval = $val;
7435  $newval['type'] = 'varchar(256)';
7436 
7437  $out = '';
7438  if (!empty($value)) {
7439  foreach ($value as $option) {
7440  $out .= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
7441  $out .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $morecss).'<br></span>';
7442  }
7443  }
7444  $out .= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
7445 
7446  $newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
7447  $newInput .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $morecss).'<br></span>';
7448 
7449  if (!empty($conf->use_javascript_ajax)) {
7450  $out .= '
7451  <script nonce="'.getNonce().'">
7452  $(document).ready(function() {
7453  $("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
7454  $("'.dol_escape_js($newInput).'").insertBefore(this);
7455  });
7456 
7457  $(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
7458  $(this).parent().remove();
7459  });
7460  });
7461  </script>';
7462  }
7463  }
7464  if (!empty($hidden)) {
7465  $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
7466  }
7467 
7468  if ($isDependList==1) {
7469  $out .= $this->getJSListDependancies('_common');
7470  }
7471  /* Add comments
7472  if ($type == 'date') $out.=' (YYYY-MM-DD)';
7473  elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
7474  */
7475 
7476  // Display error message for field
7477  if (!empty($fieldValidationErrorMsg) && function_exists('getFieldErrorIcon')) {
7478  $out .= ' '.getFieldErrorIcon($fieldValidationErrorMsg);
7479  }
7480 
7481  return $out;
7482  }
7483 
7497  public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '')
7498  {
7499  global $conf, $langs, $form;
7500 
7501  if (!is_object($form)) {
7502  require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
7503  $form = new Form($this->db);
7504  }
7505 
7506  $label = empty($val['label']) ? '' : $val['label'];
7507  $type = empty($val['type']) ? '' : $val['type'];
7508  $size = empty($val['css']) ? '' : $val['css'];
7509  $reg = array();
7510 
7511  // Convert var to be able to share same code than showOutputField of extrafields
7512  if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
7513  $type = 'varchar'; // convert varchar(xx) int varchar
7514  $size = $reg[1];
7515  } elseif (preg_match('/varchar/', $type)) {
7516  $type = 'varchar'; // convert varchar(xx) int varchar
7517  }
7518  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
7519  $type = 'select';
7520  }
7521  if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
7522  $type = 'link';
7523  }
7524 
7525  $default = empty($val['default']) ? '' : $val['default'];
7526  $computed = empty($val['computed']) ? '' : $val['computed'];
7527  $unique = empty($val['unique']) ? '' : $val['unique'];
7528  $required = empty($val['required']) ? '' : $val['required'];
7529  $param = array();
7530  $param['options'] = array();
7531 
7532  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
7533  $param['options'] = $val['arrayofkeyval'];
7534  }
7535  if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
7536  $type = 'link';
7537  $stringforoptions = $reg[1].':'.$reg[2];
7538  if ($reg[1] == 'User') {
7539  $stringforoptions .= ':-1';
7540  }
7541  $param['options'] = array($stringforoptions => $stringforoptions);
7542  } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7543  $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7544  $type = 'sellist';
7545  } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
7546  $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
7547  $type = 'sellist';
7548  } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
7549  $param['options'] = array($reg[1].':'.$reg[2] => 'N');
7550  $type = 'sellist';
7551  } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
7552  $param['options'] = array($reg[1] => 'N');
7553  $type = 'chkbxlst';
7554  }
7555 
7556  $langfile = empty($val['langfile']) ? '' : $val['langfile'];
7557  $list = (empty($val['list']) ? '' : $val['list']);
7558  $help = (empty($val['help']) ? '' : $val['help']);
7559  $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)
7560 
7561  if ($hidden) {
7562  return '';
7563  }
7564 
7565  // If field is a computed field, value must become result of compute
7566  if ($computed) {
7567  // Make the eval of compute string
7568  //var_dump($computed);
7569  $value = dol_eval($computed, 1, 0, '');
7570  }
7571 
7572  if (empty($morecss)) {
7573  if ($type == 'date') {
7574  $morecss = 'minwidth100imp';
7575  } elseif ($type == 'datetime' || $type == 'timestamp') {
7576  $morecss = 'minwidth200imp';
7577  } elseif (in_array($type, array('int', 'double', 'price'))) {
7578  $morecss = 'maxwidth75';
7579  } elseif ($type == 'url') {
7580  $morecss = 'minwidth400';
7581  } elseif ($type == 'boolean') {
7582  $morecss = '';
7583  } else {
7584  if (is_numeric($size) && round($size) < 12) {
7585  $morecss = 'minwidth100';
7586  } elseif (is_numeric($size) && round($size) <= 48) {
7587  $morecss = 'minwidth200';
7588  } else {
7589  $morecss = 'minwidth400';
7590  }
7591  }
7592  }
7593 
7594  // Format output value differently according to properties of field
7595  if (in_array($key, array('rowid', 'ref')) && method_exists($this, 'getNomUrl')) {
7596  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.
7597  $value = $this->getNomUrl(1, '', 0, '', 1);
7598  }
7599  } elseif ($key == 'status' && method_exists($this, 'getLibStatut')) {
7600  $value = $this->getLibStatut(3);
7601  } elseif ($type == 'date') {
7602  if (!empty($value)) {
7603  $value = dol_print_date($value, 'day'); // We suppose dates without time are always gmt (storage of course + output)
7604  } else {
7605  $value = '';
7606  }
7607  } elseif ($type == 'datetime' || $type == 'timestamp') {
7608  if (!empty($value)) {
7609  $value = dol_print_date($value, 'dayhour', 'tzuserrel');
7610  } else {
7611  $value = '';
7612  }
7613  } elseif ($type == 'duration') {
7614  include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
7615  if (!is_null($value) && $value !== '') {
7616  $value = convertSecondToTime($value, 'allhourmin');
7617  }
7618  } elseif ($type == 'double' || $type == 'real') {
7619  if (!is_null($value) && $value !== '') {
7620  $value = price($value);
7621  }
7622  } elseif ($type == 'boolean') {
7623  $checked = '';
7624  if (!empty($value)) {
7625  $checked = ' checked ';
7626  }
7627  $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
7628  } elseif ($type == 'mail' || $type == 'email') {
7629  $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
7630  } elseif ($type == 'url') {
7631  $value = dol_print_url($value, '_blank', 32, 1);
7632  } elseif ($type == 'phone') {
7633  $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
7634  } elseif ($type == 'ip') {
7635  $value = dol_print_ip($value, 0);
7636  } elseif ($type == 'price') {
7637  if (!is_null($value) && $value !== '') {
7638  $value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
7639  }
7640  } elseif ($type == 'select') {
7641  $value = isset($param['options'][$value])?$param['options'][$value]:'';
7642  } elseif ($type == 'sellist') {
7643  $param_list = array_keys($param['options']);
7644  $InfoFieldList = explode(":", $param_list[0]);
7645 
7646  $selectkey = "rowid";
7647  $keyList = 'rowid';
7648 
7649  if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7650  $selectkey = $InfoFieldList[2];
7651  $keyList = $InfoFieldList[2].' as rowid';
7652  }
7653 
7654  $fields_label = explode('|', $InfoFieldList[1]);
7655  if (is_array($fields_label)) {
7656  $keyList .= ', ';
7657  $keyList .= implode(', ', $fields_label);
7658  }
7659 
7660  $filter_categorie = false;
7661  if (count($InfoFieldList) > 5) {
7662  if ($InfoFieldList[0] == 'categorie') {
7663  $filter_categorie = true;
7664  }
7665  }
7666 
7667  $sql = "SELECT ".$keyList;
7668  $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
7669  if (strpos($InfoFieldList[4], 'extra') !== false) {
7670  $sql .= ' as main';
7671  }
7672  if ($selectkey == 'rowid' && empty($value)) {
7673  $sql .= " WHERE ".$selectkey." = 0";
7674  } elseif ($selectkey == 'rowid') {
7675  $sql .= " WHERE ".$selectkey." = ".((int) $value);
7676  } else {
7677  $sql .= " WHERE ".$selectkey." = '".$this->db->escape($value)."'";
7678  }
7679 
7680  //$sql.= ' AND entity = '.$conf->entity;
7681 
7682  dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
7683  $resql = $this->db->query($sql);
7684  if ($resql) {
7685  if ($filter_categorie === false) {
7686  $value = ''; // value was used, so now we reste it to use it to build final output
7687  $numrows = $this->db->num_rows($resql);
7688  if ($numrows) {
7689  $obj = $this->db->fetch_object($resql);
7690 
7691  // Several field into label (eq table:code|libelle:rowid)
7692  $fields_label = explode('|', $InfoFieldList[1]);
7693 
7694  if (is_array($fields_label) && count($fields_label) > 1) {
7695  foreach ($fields_label as $field_toshow) {
7696  $translabel = '';
7697  if (!empty($obj->$field_toshow)) {
7698  $translabel = $langs->trans($obj->$field_toshow);
7699  }
7700  if ($translabel != $field_toshow) {
7701  $value .= dol_trunc($translabel, 18) . ' ';
7702  } else {
7703  $value .= $obj->$field_toshow . ' ';
7704  }
7705  }
7706  } else {
7707  $translabel = '';
7708  if (!empty($obj->{$InfoFieldList[1]})) {
7709  $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7710  }
7711  if ($translabel != $obj->{$InfoFieldList[1]}) {
7712  $value = dol_trunc($translabel, 18);
7713  } else {
7714  $value = $obj->{$InfoFieldList[1]};
7715  }
7716  }
7717  }
7718  } else {
7719  require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
7720 
7721  $toprint = array();
7722  $obj = $this->db->fetch_object($resql);
7723  $c = new Categorie($this->db);
7724  $c->fetch($obj->rowid);
7725  $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
7726  foreach ($ways as $way) {
7727  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
7728  }
7729  $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
7730  }
7731  } else {
7732  dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
7733  }
7734  } elseif ($type == 'radio') {
7735  $value = $param['options'][$value];
7736  } elseif ($type == 'checkbox') {
7737  $value_arr = explode(',', $value);
7738  $value = '';
7739  if (is_array($value_arr) && count($value_arr) > 0) {
7740  $toprint = array();
7741  foreach ($value_arr as $keyval => $valueval) {
7742  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$param['options'][$valueval].'</li>';
7743  }
7744  $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
7745  }
7746  } elseif ($type == 'chkbxlst') {
7747  $value_arr = explode(',', $value);
7748 
7749  $param_list = array_keys($param['options']);
7750  $InfoFieldList = explode(":", $param_list[0]);
7751 
7752  $selectkey = "rowid";
7753  $keyList = 'rowid';
7754 
7755  if (count($InfoFieldList) >= 3) {
7756  $selectkey = $InfoFieldList[2];
7757  $keyList = $InfoFieldList[2].' as rowid';
7758  }
7759 
7760  $fields_label = explode('|', $InfoFieldList[1]);
7761  if (is_array($fields_label)) {
7762  $keyList .= ', ';
7763  $keyList .= implode(', ', $fields_label);
7764  }
7765 
7766  $filter_categorie = false;
7767  if (count($InfoFieldList) > 5) {
7768  if ($InfoFieldList[0] == 'categorie') {
7769  $filter_categorie = true;
7770  }
7771  }
7772 
7773  $sql = "SELECT ".$keyList;
7774  $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
7775  if (strpos($InfoFieldList[4], 'extra') !== false) {
7776  $sql .= ' as main';
7777  }
7778  // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
7779  // $sql.= ' AND entity = '.$conf->entity;
7780 
7781  dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG);
7782  $resql = $this->db->query($sql);
7783  if ($resql) {
7784  if ($filter_categorie === false) {
7785  $value = ''; // value was used, so now we reste it to use it to build final output
7786  $toprint = array();
7787  while ($obj = $this->db->fetch_object($resql)) {
7788  // Several field into label (eq table:code|libelle:rowid)
7789  $fields_label = explode('|', $InfoFieldList[1]);
7790  if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7791  if (is_array($fields_label) && count($fields_label) > 1) {
7792  foreach ($fields_label as $field_toshow) {
7793  $translabel = '';
7794  if (!empty($obj->$field_toshow)) {
7795  $translabel = $langs->trans($obj->$field_toshow);
7796  }
7797  if ($translabel != $field_toshow) {
7798  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
7799  } else {
7800  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->$field_toshow . '</li>';
7801  }
7802  }
7803  } else {
7804  $translabel = '';
7805  if (!empty($obj->{$InfoFieldList[1]})) {
7806  $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7807  }
7808  if ($translabel != $obj->{$InfoFieldList[1]}) {
7809  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
7810  } else {
7811  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->{$InfoFieldList[1]} . '</li>';
7812  }
7813  }
7814  }
7815  }
7816  } else {
7817  require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
7818 
7819  $toprint = array();
7820  while ($obj = $this->db->fetch_object($resql)) {
7821  if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7822  $c = new Categorie($this->db);
7823  $c->fetch($obj->rowid);
7824  $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
7825  foreach ($ways as $way) {
7826  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
7827  }
7828  }
7829  }
7830  }
7831  $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
7832  } else {
7833  dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
7834  }
7835  } elseif ($type == 'link') {
7836  $out = '';
7837 
7838  // only if something to display (perf)
7839  if ($value) {
7840  $param_list = array_keys($param['options']); // Example: $param_list='ObjectName:classPath:-1::customer'
7841 
7842  $InfoFieldList = explode(":", $param_list[0]);
7843  $classname = $InfoFieldList[0];
7844  $classpath = $InfoFieldList[1];
7845  $getnomurlparam = (empty($InfoFieldList[2]) ? 3 : $InfoFieldList[2]);
7846  $getnomurlparam2 = (empty($InfoFieldList[4]) ? '' : $InfoFieldList[4]);
7847  if (!empty($classpath)) {
7848  dol_include_once($InfoFieldList[1]);
7849  if ($classname && class_exists($classname)) {
7850  $object = new $classname($this->db);
7851  if ($object->element === 'product') { // Special cas for product because default valut of fetch are wrong
7852  $result = $object->fetch($value, '', '', '', 0, 1, 1);
7853  } else {
7854  $result = $object->fetch($value);
7855  }
7856  if ($result > 0) {
7857  if ($object->element === 'product') {
7858  $get_name_url_param_arr = array($getnomurlparam, $getnomurlparam2, 0, -1, 0, '', 0);
7859  if (isset($val['get_name_url_params'])) {
7860  $get_name_url_params = explode(':', $val['get_name_url_params']);
7861  if (!empty($get_name_url_params)) {
7862  $param_num_max = count($get_name_url_param_arr) - 1;
7863  foreach ($get_name_url_params as $param_num => $param_value) {
7864  if ($param_num > $param_num_max) {
7865  break;
7866  }
7867  $get_name_url_param_arr[$param_num] = $param_value;
7868  }
7869  }
7870  }
7871 
7875  $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]);
7876  } else {
7877  $value = $object->getNomUrl($getnomurlparam, $getnomurlparam2);
7878  }
7879  } else {
7880  $value = '';
7881  }
7882  }
7883  } else {
7884  dol_syslog('Error bad setup of extrafield', LOG_WARNING);
7885  return 'Error bad setup of extrafield';
7886  }
7887  } else {
7888  $value = '';
7889  }
7890  } elseif ($type == 'password') {
7891  $value = preg_replace('/./i', '*', $value);
7892  } elseif ($type == 'array') {
7893  $value = implode('<br>', $value);
7894  } else { // text|html|varchar
7895  $value = dol_htmlentitiesbr($value);
7896  }
7897 
7898  //print $type.'-'.$size.'-'.$value;
7899  $out = $value;
7900 
7901  return $out;
7902  }
7903 
7910  public function clearFieldError($fieldKey)
7911  {
7912  $this->error = '';
7913  unset($this->validateFieldsErrors[$fieldKey]);
7914  }
7915 
7923  public function setFieldError($fieldKey, $msg = '')
7924  {
7925  global $langs;
7926  if (empty($msg)) {
7927  $msg = $langs->trans("UnknowError");
7928  }
7929 
7930  $this->error = $this->validateFieldsErrors[$fieldKey] = $msg;
7931  }
7932 
7939  public function getFieldError($fieldKey)
7940  {
7941  if (!empty($this->validateFieldsErrors[$fieldKey])) {
7942  return $this->validateFieldsErrors[$fieldKey];
7943  }
7944  return '';
7945  }
7946 
7955  public function validateField($fields, $fieldKey, $fieldValue)
7956  {
7957  global $langs;
7958 
7959  if (!class_exists('Validate')) {
7960  require_once DOL_DOCUMENT_ROOT . '/core/class/validate.class.php';
7961  }
7962 
7963  $this->clearFieldError($fieldKey);
7964 
7965  if (!isset($fields[$fieldKey])) {
7966  $this->setFieldError($fieldKey, $langs->trans('FieldNotFoundInObject'));
7967  return false;
7968  }
7969 
7970  $val = $fields[$fieldKey];
7971 
7972  $param = array();
7973  $param['options'] = array();
7974  $type = $val['type'];
7975 
7976  $required = false;
7977  if (isset($val['notnull']) && $val['notnull'] === 1) {
7978  // 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
7979  $required = true;
7980  }
7981 
7982  $maxSize = 0;
7983  $minSize = 0;
7984 
7985  //
7986  // PREPARE Elements
7987  //
7988  $reg = array();
7989 
7990  // Convert var to be able to share same code than showOutputField of extrafields
7991  if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
7992  $type = 'varchar'; // convert varchar(xx) int varchar
7993  $maxSize = $reg[1];
7994  } elseif (preg_match('/varchar/', $type)) {
7995  $type = 'varchar'; // convert varchar(xx) int varchar
7996  }
7997 
7998  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
7999  $type = 'select';
8000  }
8001 
8002  if (!empty($val['type']) && preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8003  $type = 'link';
8004  }
8005 
8006  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8007  $param['options'] = $val['arrayofkeyval'];
8008  }
8009 
8010  if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8011  $type = 'link';
8012  $param['options'] = array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
8013  } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
8014  $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
8015  $type = 'sellist';
8016  } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
8017  $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
8018  $type = 'sellist';
8019  } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
8020  $param['options'] = array($reg[1].':'.$reg[2] => 'N');
8021  $type = 'sellist';
8022  }
8023 
8024  //
8025  // TEST Value
8026  //
8027 
8028  // Use Validate class to allow external Modules to use data validation part instead of concentrate all test here (factoring) or just for reuse
8029  $validate = new Validate($this->db, $langs);
8030 
8031 
8032  // little trick : to perform tests with good performances sort tests by quick to low
8033 
8034  //
8035  // COMMON TESTS
8036  //
8037 
8038  // Required test and empty value
8039  if ($required && !$validate->isNotEmptyString($fieldValue)) {
8040  $this->setFieldError($fieldKey, $validate->error);
8041  return false;
8042  } elseif (!$required && !$validate->isNotEmptyString($fieldValue)) {
8043  // if no value sent and the field is not mandatory, no need to perform tests
8044  return true;
8045  }
8046 
8047  // MAX Size test
8048  if (!empty($maxSize) && !$validate->isMaxLength($fieldValue, $maxSize)) {
8049  $this->setFieldError($fieldKey, $validate->error);
8050  return false;
8051  }
8052 
8053  // MIN Size test
8054  if (!empty($minSize) && !$validate->isMinLength($fieldValue, $minSize)) {
8055  $this->setFieldError($fieldKey, $validate->error);
8056  return false;
8057  }
8058 
8059  //
8060  // TESTS for TYPE
8061  //
8062 
8063  if (in_array($type, array('date', 'datetime', 'timestamp'))) {
8064  if (!$validate->isTimestamp($fieldValue)) {
8065  $this->setFieldError($fieldKey, $validate->error);
8066  return false;
8067  } else { return true; }
8068  } elseif ($type == 'duration') {
8069  if (!$validate->isDuration($fieldValue)) {
8070  $this->setFieldError($fieldKey, $validate->error);
8071  return false;
8072  } else { return true; }
8073  } elseif (in_array($type, array('double', 'real', 'price'))) {
8074  // is numeric
8075  if (!$validate->isNumeric($fieldValue)) {
8076  $this->setFieldError($fieldKey, $validate->error);
8077  return false;
8078  } else { return true; }
8079  } elseif ($type == 'boolean') {
8080  if (!$validate->isBool($fieldValue)) {
8081  $this->setFieldError($fieldKey, $validate->error);
8082  return false;
8083  } else { return true; }
8084  } elseif ($type == 'mail') {
8085  if (!$validate->isEmail($fieldValue)) {
8086  $this->setFieldError($fieldKey, $validate->error);
8087  return false;
8088  }
8089  } elseif ($type == 'url') {
8090  if (!$validate->isUrl($fieldValue)) {
8091  $this->setFieldError($fieldKey, $validate->error);
8092  return false;
8093  } else { return true; }
8094  } elseif ($type == 'phone') {
8095  if (!$validate->isPhone($fieldValue)) {
8096  $this->setFieldError($fieldKey, $validate->error);
8097  return false;
8098  } else { return true; }
8099  } elseif ($type == 'select' || $type == 'radio') {
8100  if (!isset($param['options'][$fieldValue])) {
8101  $this->error = $langs->trans('RequireValidValue');
8102  return false;
8103  } else { return true; }
8104  } elseif ($type == 'sellist' || $type == 'chkbxlst') {
8105  $param_list = array_keys($param['options']);
8106  $InfoFieldList = explode(":", $param_list[0]);
8107  $value_arr = explode(',', $fieldValue);
8108  $value_arr = array_map(array($this->db, 'escape'), $value_arr);
8109 
8110  $selectkey = "rowid";
8111  if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
8112  $selectkey = $InfoFieldList[2];
8113  }
8114 
8115  if (!$validate->isInDb($value_arr, $InfoFieldList[0], $selectkey)) {
8116  $this->setFieldError($fieldKey, $validate->error);
8117  return false;
8118  } else { return true; }
8119  } elseif ($type == 'link') {
8120  $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
8121  $InfoFieldList = explode(":", $param_list[0]);
8122  $classname = $InfoFieldList[0];
8123  $classpath = $InfoFieldList[1];
8124  if (!$validate->isFetchable($fieldValue, $classname, $classpath)) {
8125  $this->setFieldError($fieldKey, $validate->error);
8126  return false;
8127  } else { return true; }
8128  }
8129 
8130  // if no test failled all is ok
8131  return true;
8132  }
8133 
8147  public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = 0, $display_type = 'card')
8148  {
8149  global $db, $conf, $langs, $action, $form, $hookmanager;
8150 
8151  if (!is_object($form)) {
8152  $form = new Form($db);
8153  }
8154  if (!is_object($extrafields)) {
8155  dol_syslog('Bad parameter extrafields for showOptionals', LOG_ERR);
8156  return 'Bad parameter extrafields for showOptionals';
8157  }
8158  if (!is_array($extrafields->attributes[$this->table_element])) {
8159  dol_syslog("extrafields->attributes was not loaded with extrafields->fetch_name_optionals_label(table_element);", LOG_WARNING);
8160  }
8161 
8162  $out = '';
8163 
8164  $parameters = array('mode'=>$mode, 'params'=>$params, 'keysuffix'=>$keysuffix, 'keyprefix'=>$keyprefix, 'display_type'=>$display_type);
8165  $reshook = $hookmanager->executeHooks('showOptionals', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
8166 
8167  if (empty($reshook)) {
8168  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) {
8169  $out .= "\n";
8170  $out .= '<!-- commonobject:showOptionals --> ';
8171  $out .= "\n";
8172 
8173  $nbofextrafieldsshown = 0;
8174  $e = 0; // var to manage the modulo (odd/even)
8175 
8176  $lastseparatorkeyfound = '';
8177  $extrafields_collapse_num = '';
8178  $extrafields_collapse_num_old = '';
8179  $i = 0;
8180 
8181  foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $label) {
8182  $i++;
8183 
8184  // Show only the key field in params
8185  if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) {
8186  continue;
8187  }
8188 
8189  // Test on 'enabled' ('enabled' is different than 'list' = 'visibility')
8190  $enabled = 1;
8191  if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
8192  $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
8193  }
8194  if (empty($enabled)) {
8195  continue;
8196  }
8197 
8198  $visibility = 1;
8199  if ($visibility && isset($extrafields->attributes[$this->table_element]['list'][$key])) {
8200  $visibility = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
8201  }
8202 
8203  $perms = 1;
8204  if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
8205  $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
8206  }
8207 
8208  if (($mode == 'create') && abs($visibility) != 1 && abs($visibility) != 3) {
8209  continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list
8210  } elseif (($mode == 'edit') && abs($visibility) != 1 && abs($visibility) != 3 && abs($visibility) != 4) {
8211  continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list and <> 4 = not visible at the creation
8212  } elseif ($mode == 'view' && empty($visibility)) {
8213  continue;
8214  }
8215  if (empty($perms)) {
8216  continue;
8217  }
8218 
8219  // Load language if required
8220  if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
8221  $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
8222  }
8223 
8224  $colspan = 0;
8225  if (is_array($params) && count($params) > 0 && $display_type=='card') {
8226  if (array_key_exists('cols', $params)) {
8227  $colspan = $params['cols'];
8228  } elseif (array_key_exists('colspan', $params)) { // For backward compatibility. Use cols instead now.
8229  $reg = array();
8230  if (preg_match('/colspan="(\d+)"/', $params['colspan'], $reg)) {
8231  $colspan = $reg[1];
8232  } else {
8233  $colspan = $params['colspan'];
8234  }
8235  }
8236  }
8237  $colspan = intval($colspan);
8238 
8239  switch ($mode) {
8240  case "view":
8241  $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
8242  break;
8243  case "create":
8244  case "edit":
8245  // We get the value of property found with GETPOST so it takes into account:
8246  // default values overwrite, restore back to list link, ... (but not 'default value in database' of field)
8247  $check = 'alphanohtml';
8248  if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text'))) {
8249  $check = 'restricthtml';
8250  }
8251  $getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, $check, 3); // GETPOST can get value from GET, POST or setup of default values overwrite.
8252  // GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
8253  if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)) {
8254  if (is_array($getposttemp)) {
8255  // $getposttemp is an array but following code expects a comma separated string
8256  $value = implode(",", $getposttemp);
8257  } else {
8258  $value = $getposttemp;
8259  }
8260  } else {
8261  $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.
8262  }
8263  //var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
8264  break;
8265  }
8266 
8267  $nbofextrafieldsshown++;
8268 
8269  // Output value of the current field
8270  if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
8271  $extrafields_collapse_num = $key;
8272  /*
8273  $extrafield_param = $extrafields->attributes[$this->table_element]['param'][$key];
8274  if (!empty($extrafield_param) && is_array($extrafield_param)) {
8275  $extrafield_param_list = array_keys($extrafield_param['options']);
8276 
8277  if (count($extrafield_param_list) > 0) {
8278  $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
8279 
8280  if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
8281  //$extrafields_collapse_num = $extrafields->attributes[$this->table_element]['pos'][$key];
8282  $extrafields_collapse_num = $key;
8283  }
8284  }
8285  }
8286  */
8287 
8288  // if colspan=0 or 1, the second column is not extended, so the separator must be on 2 columns
8289  $out .= $extrafields->showSeparator($key, $this, ($colspan ? $colspan + 1 : 2), $display_type, $mode);
8290 
8291  $lastseparatorkeyfound = $key;
8292  } else {
8293  $collapse_group = $extrafields_collapse_num.(!empty($this->id) ? '_'.$this->id : '');
8294 
8295  $class = (!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
8296  $csstyle = '';
8297  if (is_array($params) && count($params) > 0) {
8298  if (array_key_exists('class', $params)) {
8299  $class .= $params['class'].' ';
8300  }
8301  if (array_key_exists('style', $params)) {
8302  $csstyle = $params['style'];
8303  }
8304  }
8305 
8306  // add html5 elements
8307  $domData = ' data-element="extrafield"';
8308  $domData .= ' data-targetelement="'.$this->element.'"';
8309  $domData .= ' data-targetid="'.$this->id.'"';
8310 
8311  $html_id = (empty($this->id) ? '' : 'extrarow-'.$this->element.'_'.$key.'_'.$this->id);
8312  if ($display_type=='card') {
8313  if (!empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0) {
8314  $colspan = 0;
8315  }
8316 
8317  if ($action == 'selectlines') {
8318  $colspan++;
8319  }
8320  }
8321 
8322  // Convert date into timestamp format (value in memory must be a timestamp)
8323  if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date'))) {
8324  $datenotinstring = null;
8325  if (array_key_exists('options_'.$key, $this->array_options)) {
8326  $datenotinstring = $this->array_options['options_'.$key];
8327  if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
8328  $datenotinstring = $this->db->jdate($datenotinstring);
8329  }
8330  }
8331  $datekey = $keyprefix.'options_'.$key.$keysuffix;
8332  $value = (GETPOSTISSET($datekey)) ? dol_mktime(12, 0, 0, GETPOST($datekey.'month', 'int', 3), GETPOST($datekey.'day', 'int', 3), GETPOST($datekey.'year', 'int', 3)) : $datenotinstring;
8333  }
8334  if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('datetime'))) {
8335  $datenotinstring = null;
8336  if (array_key_exists('options_'.$key, $this->array_options)) {
8337  $datenotinstring = $this->array_options['options_'.$key];
8338  if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
8339  $datenotinstring = $this->db->jdate($datenotinstring);
8340  }
8341  }
8342  $timekey = $keyprefix.'options_'.$key.$keysuffix;
8343  $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;
8344  }
8345  // Convert float submited string into real php numeric (value in memory must be a php numeric)
8346  if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price', 'double'))) {
8347  if (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) {
8348  $value = price2num($value);
8349  } elseif (isset($this->array_options['options_'.$key])) {
8350  $value = $this->array_options['options_'.$key];
8351  }
8352  }
8353 
8354  // HTML, text, select, integer and varchar: take into account default value in database if in create mode
8355  if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text', 'varchar', 'select', 'radio', 'int', 'boolean'))) {
8356  if ($action == 'create') {
8357  $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) ? $value : $extrafields->attributes[$this->table_element]['default'][$key];
8358  }
8359  }
8360 
8361  $labeltoshow = $langs->trans($label);
8362  $helptoshow = $langs->trans($extrafields->attributes[$this->table_element]['help'][$key]);
8363 
8364  if ($display_type == 'card') {
8365  $out .= '<tr '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="field_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
8366  if (!empty($conf->global->MAIN_VIEW_LINE_NUMBER) && ($action == 'view' || $action == 'valid' || $action == 'editline' || $action == 'confirm_valid' || $action == 'confirm_cancel')) {
8367  $out .= '<td></td>';
8368  }
8369  $out .= '<td class="'.(empty($params['tdclass']) ? 'titlefieldcreate' : $params['tdclass']).' wordbreak';
8370  } elseif ($display_type == 'line') {
8371  $out .= '<div '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="fieldline_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
8372  $out .= '<div style="display: inline-block; padding-right:4px" class="wordbreak';
8373  }
8374  //$out .= "titlefield";
8375  //if (GETPOST('action', 'restricthtml') == 'create') $out.='create';
8376  // BUG #11554 : For public page, use red dot for required fields, instead of bold label
8377  $tpl_context = isset($params["tpl_context"]) ? $params["tpl_context"] : "none";
8378  if ($tpl_context != "public") { // Public page : red dot instead of fieldrequired characters
8379  if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
8380  $out .= ' fieldrequired';
8381  }
8382  }
8383  $out .= '">';
8384  if ($tpl_context == "public") { // Public page : red dot instead of fieldrequired characters
8385  if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
8386  $out .= $form->textwithpicto($labeltoshow, $helptoshow);
8387  } else {
8388  $out .= $labeltoshow;
8389  }
8390  if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
8391  $out .= '&nbsp;<span style="color: red">*</span>';
8392  }
8393  } else {
8394  if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
8395  $out .= $form->textwithpicto($labeltoshow, $helptoshow);
8396  } else {
8397  $out .= $labeltoshow;
8398  }
8399  }
8400 
8401  $out .= ($display_type == 'card' ? '</td>' : '</div>');
8402 
8403  $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
8404  if ($display_type == 'card') {
8405  // 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
8406  $out .= '<td '.($html_id ? 'id="'.$html_id.'" ' : '').' class="valuefieldcreate '.$this->element.'_extras_'.$key.'" '.($colspan ? ' colspan="'.$colspan.'"' : '').'>';
8407  } elseif ($display_type == 'line') {
8408  $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].'">';
8409  }
8410 
8411  switch ($mode) {
8412  case "view":
8413  $out .= $extrafields->showOutputField($key, $value, '', $this->table_element);
8414  break;
8415  case "create":
8416  $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
8417  break;
8418  case "edit":
8419  $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
8420  break;
8421  }
8422 
8423  $out .= ($display_type=='card' ? '</td>' : '</div>');
8424 
8425  if (!empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) {
8426  $out .= ($display_type=='card' ? '</tr>' : '</div>');
8427  } else {
8428  $out .= ($display_type=='card' ? '</tr>' : '</div>');
8429  }
8430 
8431  $e++;
8432  }
8433  }
8434  $out .= "\n";
8435  // Add code to manage list depending on others
8436  if (!empty($conf->use_javascript_ajax)) {
8437  $out .= $this->getJSListDependancies();
8438  }
8439 
8440  $out .= '<!-- commonobject:showOptionals end --> '."\n";
8441 
8442  if (empty($nbofextrafieldsshown)) {
8443  $out = '';
8444  }
8445  }
8446  }
8447 
8448  $out .= $hookmanager->resPrint;
8449 
8450  return $out;
8451  }
8452 
8457  public function getJSListDependancies($type = '_extra')
8458  {
8459  $out = '
8460  <script nonce="'.getNonce().'">
8461  jQuery(document).ready(function() {
8462  function showOptions'.$type.'(child_list, parent_list, orig_select)
8463  {
8464  var val = $("select[name=\""+parent_list+"\"]").val();
8465  var parentVal = parent_list + ":" + val;
8466  if(typeof val == "string"){
8467  if(val != "") {
8468  var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
8469  $("select[name=\""+child_list+"\"] option[parent]").remove();
8470  $("select[name=\""+child_list+"\"]").append(options);
8471  } else {
8472  var options = orig_select.find("option[parent]").clone();
8473  $("select[name=\""+child_list+"\"] option[parent]").remove();
8474  $("select[name=\""+child_list+"\"]").append(options);
8475  }
8476  } else if(val > 0) {
8477  var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
8478  $("select[name=\""+child_list+"\"] option[parent]").remove();
8479  $("select[name=\""+child_list+"\"]").append(options);
8480  } else {
8481  var options = orig_select.find("option[parent]").clone();
8482  $("select[name=\""+child_list+"\"] option[parent]").remove();
8483  $("select[name=\""+child_list+"\"]").append(options);
8484  }
8485  }
8486  function setListDependencies'.$type.'() {
8487  jQuery("select option[parent]").parent().each(function() {
8488  var orig_select = {};
8489  var child_list = $(this).attr("name");
8490  orig_select[child_list] = $(this).clone();
8491  var parent = $(this).find("option[parent]:first").attr("parent");
8492  var infos = parent.split(":");
8493  var parent_list = infos[0];
8494 
8495  //Hide daughters lists
8496  if ($("#"+child_list).val() == 0 && $("#"+parent_list).val() == 0){
8497  $("#"+child_list).hide();
8498  //Show mother lists
8499  } else if ($("#"+parent_list).val() != 0){
8500  $("#"+parent_list).show();
8501  }
8502  //Show the child list if the parent list value is selected
8503  $("select[name=\""+parent_list+"\"]").click(function() {
8504  if ($(this).val() != 0){
8505  $("#"+child_list).show()
8506  }
8507  });
8508 
8509  //When we change parent list
8510  $("select[name=\""+parent_list+"\"]").change(function() {
8511  showOptions'.$type.'(child_list, parent_list, orig_select[child_list]);
8512  //Select the value 0 on child list after a change on the parent list
8513  $("#"+child_list).val(0).trigger("change");
8514  //Hide child lists if the parent value is set to 0
8515  if ($(this).val() == 0){
8516  $("#"+child_list).hide();
8517  }
8518  });
8519  });
8520  }
8521 
8522  setListDependencies'.$type.'();
8523  });
8524  </script>'."\n";
8525  return $out;
8526  }
8527 
8533  public function getRights()
8534  {
8535  global $user;
8536 
8537  $module = empty($this->module) ? '' : $this->module;
8538  $element = $this->element;
8539 
8540  if ($element == 'facturerec') {
8541  $element = 'facture';
8542  } elseif ($element == 'invoice_supplier_rec') {
8543  return empty($user->rights->fournisseur->facture) ? null : $user->rights->fournisseur->facture;
8544  } elseif ($module && !empty($user->rights->$module->$element)) {
8545  // for modules built with ModuleBuilder
8546  return $user->rights->$module->$element;
8547  }
8548 
8549  return $user->rights->$element;
8550  }
8551 
8564  public static function commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
8565  {
8566  foreach ($tables as $table) {
8567  $sql = 'UPDATE '.$dbs->prefix().$table.' SET fk_soc = '.((int) $dest_id).' WHERE fk_soc = '.((int) $origin_id);
8568 
8569  if (!$dbs->query($sql)) {
8570  if ($ignoreerrors) {
8571  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.
8572  }
8573  //$this->errors = $db->lasterror();
8574  return false;
8575  }
8576  }
8577 
8578  return true;
8579  }
8580 
8593  public static function commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
8594  {
8595  foreach ($tables as $table) {
8596  $sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_product = '.((int) $dest_id).' WHERE fk_product = '.((int) $origin_id);
8597 
8598  if (!$dbs->query($sql)) {
8599  if ($ignoreerrors) {
8600  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.
8601  }
8602  //$this->errors = $db->lasterror();
8603  return false;
8604  }
8605  }
8606 
8607  return true;
8608  }
8609 
8622  public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
8623  {
8624  global $conf;
8625 
8626  $buyPrice = 0;
8627 
8628  if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull > 0)) {
8629  // When ForceBuyingPriceIfNull is set
8630  $buyPrice = $unitPrice * (1 - $discountPercent / 100);
8631  } else {
8632  // Get cost price for margin calculation
8633  if (!empty($fk_product) && $fk_product > 0) {
8634  if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice') {
8635  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
8636  $product = new Product($this->db);
8637  $result = $product->fetch($fk_product);
8638  if ($result <= 0) {
8639  $this->errors[] = 'ErrorProductIdDoesNotExists';
8640  return -1;
8641  }
8642  if ($product->cost_price > 0) {
8643  $buyPrice = $product->cost_price;
8644  } elseif ($product->pmp > 0) {
8645  $buyPrice = $product->pmp;
8646  }
8647  } elseif (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp') {
8648  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
8649  $product = new Product($this->db);
8650  $result = $product->fetch($fk_product);
8651  if ($result <= 0) {
8652  $this->errors[] = 'ErrorProductIdDoesNotExists';
8653  return -1;
8654  }
8655  if ($product->pmp > 0) {
8656  $buyPrice = $product->pmp;
8657  }
8658  }
8659 
8660  if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1', 'pmp', 'costprice'))) {
8661  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
8662  $productFournisseur = new ProductFournisseur($this->db);
8663  if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0) {
8664  $buyPrice = $productFournisseur->fourn_unitprice;
8665  } elseif ($result < 0) {
8666  $this->errors[] = $productFournisseur->error;
8667  return -2;
8668  }
8669  }
8670  }
8671  }
8672  return $buyPrice;
8673  }
8674 
8675  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8695  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')
8696  {
8697  // phpcs:enable
8698  global $conf, $user, $langs;
8699 
8700  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
8701  include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
8702 
8703  $sortfield = 'position_name';
8704  $sortorder = 'asc';
8705 
8706  $dir = $sdir.'/';
8707  $pdir = '/';
8708 
8709  $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
8710  $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
8711 
8712  // For backward compatibility
8713  if ($modulepart == 'product') {
8714  if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
8715  $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
8716  $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
8717  }
8718  }
8719 
8720  // Defined relative dir to DOL_DATA_ROOT
8721  $relativedir = '';
8722  if ($dir) {
8723  $relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
8724  $relativedir = preg_replace('/^[\\/]/', '', $relativedir);
8725  $relativedir = preg_replace('/[\\/]$/', '', $relativedir);
8726  }
8727 
8728  $dirthumb = $dir.'thumbs/';
8729  $pdirthumb = $pdir.'thumbs/';
8730 
8731  $return = '<!-- Photo -->'."\n";
8732  $nbphoto = 0;
8733 
8734  $filearray = dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ?SORT_DESC:SORT_ASC), 1);
8735 
8736  /*if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) // For backward compatiblity, we scan also old dirs
8737  {
8738  $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
8739  $filearray=array_merge($filearray, $filearrayold);
8740  }*/
8741 
8742  completeFileArrayWithDatabaseInfo($filearray, $relativedir);
8743 
8744  if (count($filearray)) {
8745  if ($sortfield && $sortorder) {
8746  $filearray = dol_sort_array($filearray, $sortfield, $sortorder);
8747  }
8748 
8749  foreach ($filearray as $key => $val) {
8750  $photo = '';
8751  $file = $val['name'];
8752 
8753  //if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
8754  if (image_format_supported($file) >= 0) {
8755  $nbphoto++;
8756  $photo = $file;
8757  $viewfilename = $file;
8758 
8759  if ($size == 1 || $size == 'small') { // Format vignette
8760  // Find name of thumb file
8761  $photo_vignette = basename(getImageFileNameForSize($dir.$file, '_small'));
8762  if (!dol_is_file($dirthumb.$photo_vignette)) {
8763  // The thumb does not exists, so we will use the original file
8764  $dirthumb = $dir;
8765  $pdirthumb = $pdir;
8766  $photo_vignette = basename($file);
8767  }
8768 
8769  // Get filesize of original file
8770  $imgarray = dol_getImageSize($dir.$photo);
8771 
8772  if ($nbbyrow > 0) {
8773  if ($nbphoto == 1) {
8774  $return .= '<table class="valigntop center centpercent" style="border: 0; padding: 2px; border-spacing: 2px; border-collapse: separate;">';
8775  }
8776 
8777  if ($nbphoto % $nbbyrow == 1) {
8778  $return .= '<tr class="center valignmiddle" style="border: 1px">';
8779  }
8780  $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%" class="photo">'."\n";
8781  } elseif ($nbbyrow < 0) {
8782  $return .= '<div class="inline-block">'."\n";
8783  }
8784 
8785  $relativefile = preg_replace('/^\//', '', $pdir.$photo);
8786  if (empty($nolink)) {
8787  $urladvanced = getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
8788  if ($urladvanced) {
8789  $return .= '<a href="'.$urladvanced.'">';
8790  } else {
8791  $return .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank" rel="noopener noreferrer">';
8792  }
8793  }
8794 
8795  // Show image (width height=$maxHeight)
8796  // Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
8797  $alt = $langs->transnoentitiesnoconv('File').': '.$relativefile;
8798  $alt .= ' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
8799  if ($overwritetitle) {
8800  if (is_numeric($overwritetitle)) {
8801  $alt = '';
8802  } else {
8803  $alt = $overwritetitle;
8804  }
8805  }
8806 
8807  if ($usesharelink) {
8808  if ($val['share']) {
8809  if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
8810  $return .= '<!-- Show original file (thumb not yet available with shared links) -->';
8811  $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).'">';
8812  } else {
8813  $return .= '<!-- Show original file -->';
8814  $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).'">';
8815  }
8816  } else {
8817  $return .= '<!-- Show nophoto file (because file is not shared) -->';
8818  $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
8819  }
8820  } else {
8821  if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
8822  $return .= '<!-- Show thumb -->';
8823  $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).'">';
8824  } else {
8825  $return .= '<!-- Show original file -->';
8826  $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).'">';
8827  }
8828  }
8829 
8830  if (empty($nolink)) {
8831  $return .= '</a>';
8832  }
8833 
8834  if ($showfilename) {
8835  $return .= '<br>'.$viewfilename;
8836  }
8837  if ($showaction) {
8838  $return .= '<br>';
8839  // On propose la generation de la vignette si elle n'existe pas et si la taille est superieure aux limites
8840  if ($photo_vignette && (image_format_supported($photo) > 0) && ($this->imgWidth > $maxWidth || $this->imgHeight > $maxHeight)) {
8841  $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>';
8842  }
8843  // Special cas for product
8844  if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
8845  // Link to resize
8846  $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; ';
8847 
8848  // Link to delete
8849  $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
8850  $return .= img_delete().'</a>';
8851  }
8852  }
8853  $return .= "\n";
8854 
8855  if ($nbbyrow > 0) {
8856  $return .= '</td>';
8857  if (($nbphoto % $nbbyrow) == 0) {
8858  $return .= '</tr>';
8859  }
8860  } elseif ($nbbyrow < 0) {
8861  $return .= '</div>'."\n";
8862  }
8863  }
8864 
8865  if (empty($size)) { // Format origine
8866  $return .= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
8867 
8868  if ($showfilename) {
8869  $return .= '<br>'.$viewfilename;
8870  }
8871  if ($showaction) {
8872  // Special case for product
8873  if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
8874  // Link to resize
8875  $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; ';
8876 
8877  // Link to delete
8878  $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
8879  $return .= img_delete().'</a>';
8880  }
8881  }
8882  }
8883 
8884  // On continue ou on arrete de boucler ?
8885  if ($nbmax && $nbphoto >= $nbmax) {
8886  break;
8887  }
8888  }
8889  }
8890 
8891  if ($size == 1 || $size == 'small') {
8892  if ($nbbyrow > 0) {
8893  // Ferme tableau
8894  while ($nbphoto % $nbbyrow) {
8895  $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%">&nbsp;</td>';
8896  $nbphoto++;
8897  }
8898 
8899  if ($nbphoto) {
8900  $return .= '</table>';
8901  }
8902  }
8903  }
8904  }
8905 
8906  $this->nbphoto = $nbphoto;
8907 
8908  return $return;
8909  }
8910 
8911 
8918  protected function isArray($info)
8919  {
8920  if (is_array($info)) {
8921  if (isset($info['type']) && $info['type'] == 'array') {
8922  return true;
8923  } else {
8924  return false;
8925  }
8926  }
8927  return false;
8928  }
8929 
8936  public function isDate($info)
8937  {
8938  if (isset($info['type']) && ($info['type'] == 'date' || $info['type'] == 'datetime' || $info['type'] == 'timestamp')) {
8939  return true;
8940  }
8941  return false;
8942  }
8943 
8950  public function isDuration($info)
8951  {
8952  if (is_array($info)) {
8953  if (isset($info['type']) && ($info['type'] == 'duration')) {
8954  return true;
8955  } else {
8956  return false;
8957  }
8958  } else {
8959  return false;
8960  }
8961  }
8962 
8969  public function isInt($info)
8970  {
8971  if (is_array($info)) {
8972  if (isset($info['type']) && (preg_match('/(^int|int$)/i', $info['type']))) {
8973  return true;
8974  } else {
8975  return false;
8976  }
8977  } else {
8978  return false;
8979  }
8980  }
8981 
8988  public function isFloat($info)
8989  {
8990  if (is_array($info)) {
8991  if (isset($info['type']) && (preg_match('/^(double|real|price)/i', $info['type']))) {
8992  return true;
8993  } else {
8994  return false;
8995  }
8996  }
8997  return false;
8998  }
8999 
9006  public function isText($info)
9007  {
9008  if (is_array($info)) {
9009  if (isset($info['type']) && $info['type'] == 'text') {
9010  return true;
9011  } else {
9012  return false;
9013  }
9014  }
9015  return false;
9016  }
9017 
9024  protected function canBeNull($info)
9025  {
9026  if (is_array($info)) {
9027  if (isset($info['notnull']) && $info['notnull'] != '1') {
9028  return true;
9029  } else {
9030  return false;
9031  }
9032  }
9033  return true;
9034  }
9035 
9042  protected function isForcedToNullIfZero($info)
9043  {
9044  if (is_array($info)) {
9045  if (isset($info['notnull']) && $info['notnull'] == '-1') {
9046  return true;
9047  } else {
9048  return false;
9049  }
9050  }
9051  return false;
9052  }
9053 
9060  protected function isIndex($info)
9061  {
9062  if (is_array($info)) {
9063  if (isset($info['index']) && $info['index'] == true) {
9064  return true;
9065  } else {
9066  return false;
9067  }
9068  }
9069  return false;
9070  }
9071 
9072 
9081  protected function setSaveQuery()
9082  {
9083  global $conf;
9084 
9085  $queryarray = array();
9086  foreach ($this->fields as $field => $info) { // Loop on definition of fields
9087  // Depending on field type ('datetime', ...)
9088  if ($this->isDate($info)) {
9089  if (empty($this->{$field})) {
9090  $queryarray[$field] = null;
9091  } else {
9092  $queryarray[$field] = $this->db->idate($this->{$field});
9093  }
9094  } elseif ($this->isDuration($info)) {
9095  // $this->{$field} may be null, '', 0, '0', 123, '123'
9096  if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
9097  if (!isset($this->{$field})) {
9098  if (!empty($info['default'])) {
9099  $queryarray[$field] = $info['default'];
9100  } else {
9101  $queryarray[$field] = 0;
9102  }
9103  } else {
9104  $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9105  }
9106  } else {
9107  $queryarray[$field] = null;
9108  }
9109  } elseif ($this->isInt($info) || $this->isFloat($info)) {
9110  if ($field == 'entity' && is_null($this->{$field})) {
9111  $queryarray[$field] = ((int) $conf->entity);
9112  } else {
9113  // $this->{$field} may be null, '', 0, '0', 123, '123'
9114  if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
9115  if (!isset($this->{$field})) {
9116  $queryarray[$field] = 0;
9117  } elseif ($this->isInt($info)) {
9118  $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9119  } elseif ($this->isFloat($info)) {
9120  $queryarray[$field] = (double) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9121  }
9122  } else {
9123  $queryarray[$field] = null;
9124  }
9125  }
9126  } else {
9127  // Note: If $this->{$field} is not defined, it means there is a bug into definition of ->fields or a missing declaration of property
9128  // We should keep the warning generated by this because it is a bug somewhere else in code, not here.
9129  $queryarray[$field] = $this->{$field};
9130  }
9131 
9132  if ($info['type'] == 'timestamp' && empty($queryarray[$field])) {
9133  unset($queryarray[$field]);
9134  }
9135  if (!empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) {
9136  $queryarray[$field] = null; // May force 0 to null
9137  }
9138  }
9139 
9140  return $queryarray;
9141  }
9142 
9149  public function setVarsFromFetchObj(&$obj)
9150  {
9151  global $db;
9152 
9153  foreach ($this->fields as $field => $info) {
9154  if ($this->isDate($info)) {
9155  if (!isset($obj->$field) || is_null($obj->$field) || $obj->$field === '' || $obj->$field === '0000-00-00 00:00:00' || $obj->$field === '1000-01-01 00:00:00') {
9156  $this->$field = '';
9157  } else {
9158  $this->$field = $db->jdate($obj->$field);
9159  }
9160  } elseif ($this->isInt($info)) {
9161  if ($field == 'rowid') {
9162  $this->id = (int) $obj->$field;
9163  } else {
9164  if ($this->isForcedToNullIfZero($info)) {
9165  if (empty($obj->$field)) {
9166  $this->$field = null;
9167  } else {
9168  $this->$field = (double) $obj->$field;
9169  }
9170  } else {
9171  if (isset($obj->$field) && (!is_null($obj->$field) || (isset($info['notnull']) && $info['notnull'] == 1))) {
9172  $this->$field = (int) $obj->$field;
9173  } else {
9174  $this->$field = null;
9175  }
9176  }
9177  }
9178  } elseif ($this->isFloat($info)) {
9179  if ($this->isForcedToNullIfZero($info)) {
9180  if (empty($obj->$field)) {
9181  $this->$field = null;
9182  } else {
9183  $this->$field = (double) $obj->$field;
9184  }
9185  } else {
9186  if (isset($obj->$field) && (!is_null($obj->$field) || (isset($info['notnull']) && $info['notnull'] == 1))) {
9187  $this->$field = (double) $obj->$field;
9188  } else {
9189  $this->$field = null;
9190  }
9191  }
9192  } else {
9193  $this->$field = isset($obj->$field) ? $obj->$field : null;
9194  }
9195  }
9196 
9197  // If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
9198  if (!isset($this->fields['ref']) && isset($this->id)) {
9199  $this->ref = $this->id;
9200  }
9201  }
9202 
9210  public function getFieldList($alias = '', $excludefields = array())
9211  {
9212  $keys = array_keys($this->fields);
9213  if (!empty($alias)) {
9214  $keys_with_alias = array();
9215  foreach ($keys as $fieldname) {
9216  if (!empty($excludefields)) {
9217  if (in_array($fieldname, $excludefields)) { // The field is excluded and must not be in output
9218  continue;
9219  }
9220  }
9221  $keys_with_alias[] = $alias . '.' . $fieldname;
9222  }
9223  return implode(',', $keys_with_alias);
9224  } else {
9225  return implode(',', $keys);
9226  }
9227  }
9228 
9236  protected function quote($value, $fieldsentry)
9237  {
9238  if (is_null($value)) {
9239  return 'NULL';
9240  } elseif (preg_match('/^(int|double|real|price)/i', $fieldsentry['type'])) {
9241  return price2num("$value");
9242  } elseif (preg_match('/int$/i', $fieldsentry['type'])) {
9243  return (int) $value;
9244  } elseif ($fieldsentry['type'] == 'boolean') {
9245  if ($value) {
9246  return 'true';
9247  } else {
9248  return 'false';
9249  }
9250  } else {
9251  return "'".$this->db->escape($value)."'";
9252  }
9253  }
9254 
9255 
9263  public function createCommon(User $user, $notrigger = false)
9264  {
9265  global $langs;
9266  dol_syslog(get_class($this)."::createCommon create", LOG_DEBUG);
9267 
9268  $error = 0;
9269 
9270  $now = dol_now();
9271 
9272  $fieldvalues = $this->setSaveQuery();
9273 
9274  if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) {
9275  $fieldvalues['date_creation'] = $this->db->idate($now);
9276  }
9277  if (array_key_exists('fk_user_creat', $fieldvalues) && !($fieldvalues['fk_user_creat'] > 0)) {
9278  $fieldvalues['fk_user_creat'] = $user->id;
9279  $this->fk_user_creat = $user->id;
9280  }
9281  if (array_key_exists('user_modification_id', $fieldvalues) && !($fieldvalues['user_modification_id'] > 0)) {
9282  $fieldvalues['user_modification_id'] = $user->id;
9283  $this->user_modification_id = $user->id;
9284  }
9285  unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
9286  if (array_key_exists('ref', $fieldvalues)) {
9287  $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
9288  }
9289 
9290  $keys = array();
9291  $values = array(); // Array to store string forged for SQL syntax
9292  foreach ($fieldvalues as $k => $v) {
9293  $keys[$k] = $k;
9294  $value = $this->fields[$k];
9295  $values[$k] = $this->quote($v, $value); // May return string 'NULL' if $value is null
9296  }
9297 
9298  // Clean and check mandatory
9299  foreach ($keys as $key) {
9300  // If field is an implicit foreign key field (so type = 'integer:...')
9301  if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
9302  $values[$key] = '';
9303  }
9304  if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
9305  $values[$key] = '';
9306  }
9307 
9308  if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && is_null($this->fields[$key]['default'])) {
9309  $error++;
9310  $langs->load("errors");
9311  dol_syslog("Mandatory field '".$key."' is empty and required into ->fields definition of class");
9312  $this->errors[] = $langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
9313  }
9314 
9315  // If value is null and there is a default value for field
9316  if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && !is_null($this->fields[$key]['default'])) {
9317  $values[$key] = $this->quote($this->fields[$key]['default'], $this->fields[$key]);
9318  }
9319 
9320  // If field is an implicit foreign key field (so type = 'integer:...')
9321  if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) {
9322  if (isset($this->fields[$key]['default'])) {
9323  $values[$key] = ((int) $this->fields[$key]['default']);
9324  } else {
9325  $values[$key] = 'null';
9326  }
9327  }
9328  if (!empty($this->fields[$key]['foreignkey']) && empty($values[$key])) {
9329  $values[$key] = 'null';
9330  }
9331  }
9332 
9333  if ($error) {
9334  return -1;
9335  }
9336 
9337  $this->db->begin();
9338 
9339  if (!$error) {
9340  $sql = "INSERT INTO ".$this->db->prefix().$this->table_element;
9341  $sql .= " (".implode(", ", $keys).')';
9342  $sql .= " VALUES (".implode(", ", $values).")"; // $values can contains 'abc' or 123
9343 
9344  $res = $this->db->query($sql);
9345  if (!$res) {
9346  $error++;
9347  if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
9348  $this->errors[] = "ErrorRefAlreadyExists";
9349  } else {
9350  $this->errors[] = $this->db->lasterror();
9351  }
9352  }
9353  }
9354 
9355  if (!$error) {
9356  $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
9357  }
9358 
9359  // If we have a field ref with a default value of (PROV)
9360  if (!$error) {
9361  if (key_exists('ref', $this->fields) && $this->fields['ref']['notnull'] > 0 && key_exists('default', $this->fields['ref']) && $this->fields['ref']['default'] == '(PROV)') {
9362  $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ref = '(PROV".((int) $this->id).")' WHERE (ref = '(PROV)' OR ref = '') AND rowid = ".((int) $this->id);
9363  $resqlupdate = $this->db->query($sql);
9364 
9365  if ($resqlupdate === false) {
9366  $error++;
9367  $this->errors[] = $this->db->lasterror();
9368  } else {
9369  $this->ref = '(PROV'.$this->id.')';
9370  }
9371  }
9372  }
9373 
9374  // Create extrafields
9375  if (!$error) {
9376  $result = $this->insertExtraFields();
9377  if ($result < 0) {
9378  $error++;
9379  }
9380  }
9381 
9382  // Create lines
9383  if (!empty($this->table_element_line) && !empty($this->fk_element)) {
9384  $num = (is_array($this->lines) ? count($this->lines) : 0);
9385  for ($i = 0; $i < $num; $i++) {
9386  $line = $this->lines[$i];
9387 
9388  $keyforparent = $this->fk_element;
9389  $line->$keyforparent = $this->id;
9390 
9391  // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
9392  //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
9393  if (!is_object($line)) {
9394  $line = (object) $line;
9395  }
9396 
9397  $result = 0;
9398  if (method_exists($line, 'insert')) {
9399  $result = $line->insert($user, 1);
9400  } elseif (method_exists($line, 'create')) {
9401  $result = $line->create($user, 1);
9402  }
9403  if ($result < 0) {
9404  $this->error = $line->error;
9405  $this->db->rollback();
9406  return -1;
9407  }
9408  }
9409  }
9410 
9411  // Triggers
9412  if (!$error && !$notrigger) {
9413  // Call triggers
9414  $result = $this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user);
9415  if ($result < 0) {
9416  $error++;
9417  }
9418  // End call triggers
9419  }
9420 
9421  // Commit or rollback
9422  if ($error) {
9423  $this->db->rollback();
9424  return -1;
9425  } else {
9426  $this->db->commit();
9427  return $this->id;
9428  }
9429  }
9430 
9431 
9440  public function fetchCommon($id, $ref = null, $morewhere = '')
9441  {
9442  if (empty($id) && empty($ref) && empty($morewhere)) {
9443  return -1;
9444  }
9445 
9446  $fieldlist = $this->getFieldList('t');
9447  if (empty($fieldlist)) {
9448  return 0;
9449  }
9450 
9451  $sql = "SELECT ".$fieldlist;
9452  $sql .= " FROM ".$this->db->prefix().$this->table_element.' as t';
9453 
9454  if (!empty($id)) {
9455  $sql .= ' WHERE t.rowid = '.((int) $id);
9456  } elseif (!empty($ref)) {
9457  $sql .= " WHERE t.ref = '".$this->db->escape($ref)."'";
9458  } else {
9459  $sql .= ' WHERE 1 = 1'; // usage with empty id and empty ref is very rare
9460  }
9461  if (empty($id) && isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
9462  $sql .= ' AND t.entity IN ('.getEntity($this->element).')';
9463  }
9464  if ($morewhere) {
9465  $sql .= $morewhere;
9466  }
9467  $sql .= ' LIMIT 1'; // This is a fetch, to be sure to get only one record
9468 
9469  $res = $this->db->query($sql);
9470  if ($res) {
9471  $obj = $this->db->fetch_object($res);
9472  if ($obj) {
9473  $this->setVarsFromFetchObj($obj);
9474 
9475  // Retrieve all extrafield
9476  // fetch optionals attributes and labels
9477  $this->fetch_optionals();
9478 
9479  return $this->id;
9480  } else {
9481  return 0;
9482  }
9483  } else {
9484  $this->error = $this->db->lasterror();
9485  $this->errors[] = $this->error;
9486  return -1;
9487  }
9488  }
9489 
9496  public function fetchLinesCommon($morewhere = '')
9497  {
9498  $objectlineclassname = get_class($this).'Line';
9499  if (!class_exists($objectlineclassname)) {
9500  $this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
9501  return -1;
9502  }
9503 
9504  $objectline = new $objectlineclassname($this->db);
9505 
9506  $sql = "SELECT ".$objectline->getFieldList('l');
9507  $sql .= " FROM ".$this->db->prefix().$objectline->table_element." as l";
9508  $sql .= " WHERE l.fk_".$this->db->escape($this->element)." = ".((int) $this->id);
9509  if ($morewhere) {
9510  $sql .= $morewhere;
9511  }
9512  if (isset($objectline->fields['position'])) {
9513  $sql .= $this->db->order('position', 'ASC');
9514  }
9515 
9516  $resql = $this->db->query($sql);
9517  if ($resql) {
9518  $num_rows = $this->db->num_rows($resql);
9519  $i = 0;
9520  while ($i < $num_rows) {
9521  $obj = $this->db->fetch_object($resql);
9522  if ($obj) {
9523  $newline = new $objectlineclassname($this->db);
9524  $newline->setVarsFromFetchObj($obj);
9525 
9526  $this->lines[] = $newline;
9527  }
9528  $i++;
9529  }
9530 
9531  return 1;
9532  } else {
9533  $this->error = $this->db->lasterror();
9534  $this->errors[] = $this->error;
9535  return -1;
9536  }
9537  }
9538 
9546  public function updateCommon(User $user, $notrigger = false)
9547  {
9548  global $conf, $langs;
9549  dol_syslog(get_class($this)."::updateCommon update", LOG_DEBUG);
9550 
9551  $error = 0;
9552 
9553  $now = dol_now();
9554 
9555  // $this->oldcopy should have been set by the caller of update
9556  //if (empty($this->oldcopy)) {
9557  // $this->oldcopy = dol_clone($this);
9558  //}
9559 
9560  $fieldvalues = $this->setSaveQuery();
9561 
9562  if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) {
9563  $fieldvalues['date_modification'] = $this->db->idate($now);
9564  }
9565  if (array_key_exists('fk_user_modif', $fieldvalues) && !($fieldvalues['fk_user_modif'] > 0)) {
9566  $fieldvalues['fk_user_modif'] = $user->id;
9567  }
9568  unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
9569  if (array_key_exists('ref', $fieldvalues)) {
9570  $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
9571  }
9572 
9573  // Add quotes and escape on fields with type string
9574  $keys = array();
9575  $values = array();
9576  $tmp = array();
9577  foreach ($fieldvalues as $k => $v) {
9578  $keys[$k] = $k;
9579  $value = $this->fields[$k];
9580  $values[$k] = $this->quote($v, $value);
9581  $tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
9582  }
9583 
9584  // Clean and check mandatory fields
9585  foreach ($keys as $key) {
9586  if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
9587  $values[$key] = ''; // This is an implicit foreign key field
9588  }
9589  if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
9590  $values[$key] = ''; // This is an explicit foreign key field
9591  }
9592 
9593  //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
9594  /*
9595  if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
9596  {
9597  $error++;
9598  $this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
9599  }*/
9600  }
9601 
9602  $sql = 'UPDATE '.$this->db->prefix().$this->table_element.' SET '.implode(', ', $tmp).' WHERE rowid='.((int) $this->id);
9603 
9604  $this->db->begin();
9605 
9606  if (!$error) {
9607  $res = $this->db->query($sql);
9608  if (!$res) {
9609  $error++;
9610  $this->errors[] = $this->db->lasterror();
9611  }
9612  }
9613 
9614  // Update extrafield
9615  if (!$error) {
9616  $result = $this->insertExtraFields();
9617  if ($result < 0) {
9618  $error++;
9619  }
9620  }
9621 
9622  // Triggers
9623  if (!$error && !$notrigger) {
9624  // Call triggers
9625  $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
9626  if ($result < 0) {
9627  $error++;
9628  } //Do also here what you must do to rollback action if trigger fail
9629  // End call triggers
9630  }
9631 
9632  // Commit or rollback
9633  if ($error) {
9634  $this->db->rollback();
9635  return -1;
9636  } else {
9637  $this->db->commit();
9638  return $this->id;
9639  }
9640  }
9641 
9650  public function deleteCommon(User $user, $notrigger = false, $forcechilddeletion = 0)
9651  {
9652  dol_syslog(get_class($this)."::deleteCommon delete", LOG_DEBUG);
9653 
9654  $error = 0;
9655 
9656  $this->db->begin();
9657 
9658  if ($forcechilddeletion) { // Force also delete of childtables that should lock deletion in standard case when option force is off
9659  foreach ($this->childtables as $table) {
9660  $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
9661  $resql = $this->db->query($sql);
9662  if (!$resql) {
9663  $this->error = $this->db->lasterror();
9664  $this->errors[] = $this->error;
9665  $this->db->rollback();
9666  return -1;
9667  }
9668  }
9669  } elseif (!empty($this->childtables)) { // If object has childs linked with a foreign key field, we check all child tables.
9670  $objectisused = $this->isObjectUsed($this->id);
9671  if (!empty($objectisused)) {
9672  dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
9673  $this->error = 'ErrorRecordHasChildren';
9674  $this->errors[] = $this->error;
9675  $this->db->rollback();
9676  return 0;
9677  }
9678  }
9679 
9680  // Delete cascade first
9681  if (is_array($this->childtablesoncascade) && !empty($this->childtablesoncascade)) {
9682  foreach ($this->childtablesoncascade as $table) {
9683  $deleteFromObject = explode(':', $table);
9684  if (count($deleteFromObject) >= 2) {
9685  $className = str_replace('@', '', $deleteFromObject[0]);
9686  $filePath = $deleteFromObject[1];
9687  $columnName = $deleteFromObject[2];
9688  $TMoreSQL = array();
9689  $more_sql = $deleteFromObject[3];
9690  if (!empty($more_sql)) {
9691  $TMoreSQL['customsql'] = $more_sql;
9692  }
9693  if (dol_include_once($filePath)) {
9694  $childObject = new $className($this->db);
9695  if (method_exists($childObject, 'deleteByParentField')) {
9696  $result = $childObject->deleteByParentField($this->id, $columnName, $TMoreSQL);
9697  if ($result < 0) {
9698  $error++;
9699  $this->errors[] = $childObject->error;
9700  break;
9701  }
9702  } else {
9703  $error++;
9704  $this->errors[] = "You defined a cascade delete on an object $childObject but there is no method deleteByParentField for it";
9705  break;
9706  }
9707  } else {
9708  $error++;
9709  $this->errors[] = 'Cannot include child class file '.$filePath;
9710  break;
9711  }
9712  } else {
9713  // Delete record in child table
9714  $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
9715 
9716  $resql = $this->db->query($sql);
9717  if (!$resql) {
9718  $error++;
9719  $this->error = $this->db->lasterror();
9720  $this->errors[] = $this->error;
9721  break;
9722  }
9723  }
9724  }
9725  }
9726 
9727  if (!$error) {
9728  if (!$notrigger) {
9729  // Call triggers
9730  $result = $this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
9731  if ($result < 0) {
9732  $error++;
9733  } // Do also here what you must do to rollback action if trigger fail
9734  // End call triggers
9735  }
9736  }
9737 
9738  // Delete llx_ecm_files
9739  if (!$error) {
9740  $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
9741  if (!$res) {
9742  $error++;
9743  }
9744  }
9745 
9746  // Delete linked object
9747  $res = $this->deleteObjectLinked();
9748  if ($res < 0) {
9749  $error++;
9750  }
9751 
9752  if (!$error && !empty($this->isextrafieldmanaged)) {
9753  $result = $this->deleteExtraFields();
9754  if ($result < 0) {
9755  $error++;
9756  }
9757  }
9758 
9759  if (!$error) {
9760  $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.' WHERE rowid='.((int) $this->id);
9761 
9762  $resql = $this->db->query($sql);
9763  if (!$resql) {
9764  $error++;
9765  $this->errors[] = $this->db->lasterror();
9766  }
9767  }
9768 
9769  // Commit or rollback
9770  if ($error) {
9771  $this->db->rollback();
9772  return -1;
9773  } else {
9774  $this->db->commit();
9775  return 1;
9776  }
9777  }
9778 
9789  public function deleteByParentField($parentId = 0, $parentField = '', $filter = array(), $filtermode = "AND")
9790  {
9791  global $user;
9792 
9793  $error = 0;
9794  $deleted = 0;
9795 
9796  if (!empty($parentId) && !empty($parentField)) {
9797  $this->db->begin();
9798 
9799  $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
9800  $sql .= " WHERE ".$parentField." = ".(int) $parentId;
9801 
9802  // Manage filters
9803  $sqlwhere = array();
9804  if (count($filter) > 0) {
9805  foreach ($filter as $key => $value) {
9806  if ($key == 'customsql') {
9807  $sqlwhere[] = $value;
9808  } elseif (strpos($value, '%') === false) {
9809  $sqlwhere[] = $key." IN (".$this->db->sanitize($this->db->escape($value)).")";
9810  } else {
9811  $sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'";
9812  }
9813  }
9814  }
9815  if (count($sqlwhere) > 0) {
9816  $sql .= " AND (".implode(" ".$filtermode." ", $sqlwhere).")";
9817  }
9818 
9819  $resql = $this->db->query($sql);
9820  if (!$resql) {
9821  $this->errors[] = $this->db->lasterror();
9822  $error++;
9823  } else {
9824  while ($obj = $this->db->fetch_object($resql)) {
9825  $result = $this->fetch($obj->rowid);
9826  if ($result < 0) {
9827  $error++;
9828  $this->errors[] = $this->error;
9829  } else {
9830  if (get_class($this) == 'Contact') { // TODO special code because delete() for contact has not been standardized like other delete.
9831  $result = $this->delete();
9832  } else {
9833  $result = $this->delete($user);
9834  }
9835  if ($result < 0) {
9836  $error++;
9837  $this->errors[] = $this->error;
9838  } else {
9839  $deleted++;
9840  }
9841  }
9842  }
9843  }
9844 
9845  if (empty($error)) {
9846  $this->db->commit();
9847  return $deleted;
9848  } else {
9849  $this->error = implode(', ', $this->errors);
9850  $this->db->rollback();
9851  return $error * -1;
9852  }
9853  }
9854 
9855  return $deleted;
9856  }
9857 
9866  public function deleteLineCommon(User $user, $idline, $notrigger = false)
9867  {
9868  global $conf;
9869 
9870  $error = 0;
9871 
9872  $tmpforobjectclass = get_class($this);
9873  $tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line';
9874 
9875  $this->db->begin();
9876 
9877  // Call trigger
9878  $result = $this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user);
9879  if ($result < 0) {
9880  $error++;
9881  }
9882  // End call triggers
9883 
9884  if (empty($error)) {
9885  $sql = "DELETE FROM ".$this->db->prefix().$this->table_element_line;
9886  $sql .= " WHERE rowid = ".((int) $idline);
9887 
9888  $resql = $this->db->query($sql);
9889  if (!$resql) {
9890  $this->error = "Error ".$this->db->lasterror();
9891  $error++;
9892  }
9893  }
9894 
9895  if (empty($error)) {
9896  // Remove extrafields
9897  $tmpobjectline = new $tmpforobjectlineclass($this->db);
9898  if (!isset($tmpobjectline->isextrafieldmanaged) || !empty($tmpobjectline->isextrafieldmanaged)) {
9899  $tmpobjectline->id = $idline;
9900  $result = $tmpobjectline->deleteExtraFields();
9901  if ($result < 0) {
9902  $error++;
9903  $this->error = "Error ".get_class($this)."::deleteLineCommon deleteExtraFields error -4 ".$tmpobjectline->error;
9904  }
9905  }
9906  }
9907 
9908  if (empty($error)) {
9909  $this->db->commit();
9910  return 1;
9911  } else {
9912  dol_syslog(get_class($this)."::deleteLineCommon ERROR:".$this->error, LOG_ERR);
9913  $this->db->rollback();
9914  return -1;
9915  }
9916  }
9917 
9918 
9928  public function setStatusCommon($user, $status, $notrigger = 0, $triggercode = '')
9929  {
9930  $error = 0;
9931 
9932  $this->db->begin();
9933 
9934  $statusfield = 'status';
9935  if (in_array($this->element, array('don', 'donation', 'shipping'))) {
9936  $statusfield = 'fk_statut';
9937  }
9938 
9939  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
9940  $sql .= " SET ".$statusfield." = ".((int) $status);
9941  $sql .= " WHERE rowid = ".((int) $this->id);
9942 
9943  if ($this->db->query($sql)) {
9944  if (!$error) {
9945  $this->oldcopy = clone $this;
9946  }
9947 
9948  if (!$error && !$notrigger) {
9949  // Call trigger
9950  $result = $this->call_trigger($triggercode, $user);
9951  if ($result < 0) {
9952  $error++;
9953  }
9954  }
9955 
9956  if (!$error) {
9957  $this->status = $status;
9958  $this->db->commit();
9959  return 1;
9960  } else {
9961  $this->db->rollback();
9962  return -1;
9963  }
9964  } else {
9965  $this->error = $this->db->error();
9966  $this->db->rollback();
9967  return -1;
9968  }
9969  }
9970 
9971 
9978  public function initAsSpecimenCommon()
9979  {
9980  global $user;
9981 
9982  $this->id = 0;
9983  $this->specimen = 1;
9984  $fields = array(
9985  'label' => 'This is label',
9986  'ref' => 'ABCD1234',
9987  'description' => 'This is a description',
9988  'qty' => 123.12,
9989  'note_public' => 'Public note',
9990  'note_private' => 'Private note',
9991  'date_creation' => (dol_now() - 3600 * 48),
9992  'date_modification' => (dol_now() - 3600 * 24),
9993  'fk_user_creat' => $user->id,
9994  'fk_user_modif' => $user->id,
9995  'date' => dol_now(),
9996  );
9997  foreach ($fields as $key => $value) {
9998  if (array_key_exists($key, $this->fields)) {
9999  $this->{$key} = $value;
10000  }
10001  }
10002 
10003  // Force values to default values when known
10004  if (property_exists($this, 'fields')) {
10005  foreach ($this->fields as $key => $value) {
10006  // If fields are already set, do nothing
10007  if (array_key_exists($key, $fields)) {
10008  continue;
10009  }
10010 
10011  if (!empty($value['default'])) {
10012  $this->$key = $value['default'];
10013  }
10014  }
10015  }
10016 
10017  return 1;
10018  }
10019 
10020 
10021  /* Part for comments */
10022 
10027  public function fetchComments()
10028  {
10029  require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
10030 
10031  $comment = new Comment($this->db);
10032  $result = $comment->fetchAllFor($this->element, $this->id);
10033  if ($result < 0) {
10034  $this->errors = array_merge($this->errors, $comment->errors);
10035  return -1;
10036  } else {
10037  $this->comments = $comment->comments;
10038  }
10039  return count($this->comments);
10040  }
10041 
10047  public function getNbComments()
10048  {
10049  return count($this->comments);
10050  }
10051 
10058  public function trimParameters($parameters)
10059  {
10060  if (!is_array($parameters)) {
10061  return;
10062  }
10063  foreach ($parameters as $parameter) {
10064  if (isset($this->$parameter)) {
10065  $this->$parameter = trim($this->$parameter);
10066  }
10067  }
10068  }
10069 
10070  /* Part for categories/tags */
10071 
10082  public function getCategoriesCommon($type_categ)
10083  {
10084  require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10085 
10086  // Get current categories
10087  $c = new Categorie($this->db);
10088  $existing = $c->containing($this->id, $type_categ, 'id');
10089 
10090  return $existing;
10091  }
10092 
10105  public function setCategoriesCommon($categories, $type_categ = '', $remove_existing = true)
10106  {
10107  // Handle single category
10108  if (!is_array($categories)) {
10109  $categories = array($categories);
10110  }
10111 
10112  dol_syslog(get_class($this)."::setCategoriesCommon Oject Id:".$this->id.' type_categ:'.$type_categ.' nb tag add:'.count($categories), LOG_DEBUG);
10113 
10114  require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10115 
10116  if (empty($type_categ)) {
10117  dol_syslog(__METHOD__.': Type '.$type_categ.'is an unknown category type. Done nothing.', LOG_ERR);
10118  return -1;
10119  }
10120 
10121  // Get current categories
10122  $c = new Categorie($this->db);
10123  $existing = $c->containing($this->id, $type_categ, 'id');
10124  if ($remove_existing) {
10125  // Diff
10126  if (is_array($existing)) {
10127  $to_del = array_diff($existing, $categories);
10128  $to_add = array_diff($categories, $existing);
10129  } else {
10130  $to_del = array(); // Nothing to delete
10131  $to_add = $categories;
10132  }
10133  } else {
10134  $to_del = array(); // Nothing to delete
10135  $to_add = array_diff($categories, $existing);
10136  }
10137 
10138  $error = 0;
10139  $ok = 0;
10140 
10141  // Process
10142  foreach ($to_del as $del) {
10143  if ($c->fetch($del) > 0) {
10144  $result=$c->del_type($this, $type_categ);
10145  if ($result < 0) {
10146  $error++;
10147  $this->error = $c->error;
10148  $this->errors = $c->errors;
10149  break;
10150  } else {
10151  $ok += $result;
10152  }
10153  }
10154  }
10155  foreach ($to_add as $add) {
10156  if ($c->fetch($add) > 0) {
10157  $result = $c->add_type($this, $type_categ);
10158  if ($result < 0) {
10159  $error++;
10160  $this->error = $c->error;
10161  $this->errors = $c->errors;
10162  break;
10163  } else {
10164  $ok += $result;
10165  }
10166  }
10167  }
10168 
10169  return $error ? (-1 * $error) : $ok;
10170  }
10171 
10180  public function cloneCategories($fromId, $toId, $type = '')
10181  {
10182  $this->db->begin();
10183 
10184  if (empty($type)) {
10185  $type = $this->table_element;
10186  }
10187 
10188  require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10189  $categorystatic = new Categorie($this->db);
10190 
10191  $sql = "INSERT INTO ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type])." (fk_categorie, fk_product)";
10192  $sql .= " SELECT fk_categorie, $toId FROM ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type]);
10193  $sql .= " WHERE fk_product = ".((int) $fromId);
10194 
10195  if (!$this->db->query($sql)) {
10196  $this->error = $this->db->lasterror();
10197  $this->db->rollback();
10198  return -1;
10199  }
10200 
10201  $this->db->commit();
10202  return 1;
10203  }
10204 
10211  public function deleteEcmFiles($mode = 0)
10212  {
10213  global $conf;
10214 
10215  $this->db->begin();
10216 
10217  // Delete in database with mode 0
10218  if ($mode == 0) {
10219  switch ($this->element) {
10220  case 'propal':
10221  $element = 'propale';
10222  break;
10223  case 'product':
10224  $element = 'produit';
10225  break;
10226  case 'order_supplier':
10227  $element = 'fournisseur/commande';
10228  break;
10229  case 'invoice_supplier':
10230  $element = 'fournisseur/facture/'.get_exdir($this->id, 2, 0, 1, $this, 'invoice_supplier');
10231  break;
10232  case 'shipping':
10233  $element = 'expedition/sending';
10234  break;
10235  default:
10236  $element = $this->element;
10237  }
10238 
10239  // Delete ecm_files extrafields
10240  $sql = "DELETE FROM ".$this->db->prefix()."ecm_files_extrafields WHERE fk_object IN (";
10241  $sql .= " SELECT rowid FROM ".$this->db->prefix()."ecm_files WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
10242  $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
10243  $sql .= ")";
10244 
10245  if (!$this->db->query($sql)) {
10246  $this->error = $this->db->lasterror();
10247  $this->db->rollback();
10248  return false;
10249  }
10250 
10251  // Delete ecm_files
10252  $sql = "DELETE FROM ".$this->db->prefix()."ecm_files";
10253  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
10254  $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
10255 
10256  if (!$this->db->query($sql)) {
10257  $this->error = $this->db->lasterror();
10258  $this->db->rollback();
10259  return false;
10260  }
10261  }
10262 
10263  // Delete in database with mode 1
10264  if ($mode == 1) {
10265  $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files_extrafields";
10266  $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).")";
10267  $resql = $this->db->query($sql);
10268  if (!$resql) {
10269  $this->error = $this->db->lasterror();
10270  $this->db->rollback();
10271  return false;
10272  }
10273 
10274  $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files";
10275  $sql .= " WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? "" : "@".$this->module))."' AND src_object_id = ".((int) $this->id);
10276  $resql = $this->db->query($sql);
10277  if (!$resql) {
10278  $this->error = $this->db->lasterror();
10279  $this->db->rollback();
10280  return false;
10281  }
10282  }
10283 
10284  $this->db->commit();
10285  return true;
10286  }
10287 }
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
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...
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.
update_note($note, $suffix='', $notrigger=0)
Update note of element.
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).
getFieldList($alias='', $excludefields=array())
Function to concat keys of fields.
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 return the array of data key-value from the ->fields and all the ->properties of an 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.
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:240
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:1636
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:1356
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:1559
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:926
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.