dolibarr 21.0.4
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 <bafbes@gmail.com>
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-2024 Frédéric France <frederic.france@free.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 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
21 * Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
22 *
23 * This program is free software; you can redistribute it and/or modify
24 * it under the terms of the GNU General Public License as published by
25 * the Free Software Foundation; either version 3 of the License, or
26 * (at your option) any later version.
27 *
28 * This program is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU General Public License for more details.
32 *
33 * You should have received a copy of the GNU General Public License
34 * along with this program. If not, see <https://www.gnu.org/licenses/>.
35 */
36
43require_once DOL_DOCUMENT_ROOT.'/core/class/doldeprecationhandler.class.php';
44
50abstract class CommonObject
51{
52 use DolDeprecationHandler;
53
54 const TRIGGER_PREFIX = ''; // to be overridden in child class implementations, i.e. 'BILL', 'TASK', 'PROPAL', etc.
55
59 public $module;
60
64 public $db;
65
69 public $id;
70
74 public $entity;
75
80 public $error;
81
85 public $errorhidden;
86
90 public $errors = array();
91
95 private $validateFieldsErrors = array();
96
100 public $element;
101
106 public $fk_element;
107
113 public $element_for_permission;
114
118 public $table_element;
119
123 public $table_element_line = '';
124
129 public $ismultientitymanaged;
130
134 public $import_key;
135
139 public $array_options = array();
140
141
145 public $fields = array();
146
152 public $array_languages = null; // Value is array() when load already tried
153
157 public $contacts_ids;
158
162 public $linked_objects;
163
167 public $linkedObjectsIds;
168
172 public $linkedObjects;
173
177 private $linkedObjectsFullLoaded = array();
178
182 public $oldcopy;
183
187 public $oldref;
188
192 protected $table_ref_field = '';
193
197 public $restrictiononfksoc = 0;
198
199
200 // The following vars are used by some objects only.
201 // We keep these properties in CommonObject in order to provide common methods using them.
202
206 public $context = array();
207
211 public $actionmsg;
215 public $actionmsg2;
216
220 public $canvas;
221
226 public $project;
227
232 public $fk_project;
233
239 private $projet;
240
246 public $fk_projet;
247
252 public $contact;
253
258 public $contact_id;
259
264 public $thirdparty;
265
270 public $user;
271
276 public $origin_type;
277
282 public $origin_id;
283
288
294 public $origin;
295
304 private $expedition;
305
311 private $livraison;
312
318 private $commandeFournisseur;
319
320
324 public $ref;
325
329 public $ref_ext;
330
334 public $ref_previous;
335
339 public $ref_next;
340
344 public $newref;
345
352 public $statut;
353
362 public $status;
363
364
369 public $country;
370
375 public $country_id;
376
381 public $country_code;
382
387 public $state;
388
393 public $state_id;
394
399 public $fk_departement;
400
405 public $state_code;
406
411 public $region_id;
412
417 public $region_code;
418
423 public $region;
424
425
430 public $barcode_type;
431
436 public $barcode_type_code;
437
442 public $barcode_type_label;
443
448 public $barcode_type_coder;
449
454 public $mode_reglement_id;
455
460 public $cond_reglement_id;
461
465 public $demand_reason_id;
466
471 public $transport_mode_id;
472
480 private $cond_reglement; // Private to call DolDeprecationHandler
484 protected $depr_cond_reglement; // Internal value for deprecation
485
491 public $fk_delivery_address;
492
497 public $shipping_method_id;
498
503 public $shipping_method;
504
505 // Multicurrency
509 public $fk_multicurrency;
510
515 public $multicurrency_code;
516
521 public $multicurrency_tx;
522
526 public $multicurrency_total_ht;
527
531 public $multicurrency_total_tva;
532
536 public $multicurrency_total_ttc;
537
541 public $multicurrency_total_localtax1; // not in database
542
546 public $multicurrency_total_localtax2; // not in database
547
552 public $model_pdf;
553
558 public $last_main_doc;
559
565 public $fk_bank;
566
571 public $fk_account;
572
577 public $note_public;
578
583 public $note_private;
584
590 public $note;
591
596 public $total_ht;
597
602 public $total_tva;
603
608 public $total_localtax1;
609
614 public $total_localtax2;
615
620 public $total_ttc;
621
622
626 public $lines;
627
631 public $actiontypecode;
632
637 public $comments = array();
638
642 public $name;
643
647 public $lastname;
648
652 public $firstname;
653
657 public $civility_id;
658
659 // Dates
663 public $date_creation;
664
668 public $date_validation;
669
673 public $date_modification;
674
680 public $tms;
681
687 private $date_update;
688
692 public $date_cloture;
693
698 public $user_author;
699
704 public $user_creation;
705
709 public $user_creation_id;
710
715 public $user_valid;
716
721 public $user_validation;
722
726 public $user_validation_id;
727
731 public $user_closing_id;
732
737 public $user_modification;
738
742 public $user_modification_id;
743
748 public $fk_user_creat;
749
754 public $fk_user_modif;
755
756
760 public $next_prev_filter;
761
765 public $specimen = 0;
766
770 public $sendtoid;
771
776 public $alreadypaid;
777
782 public $totalpaid;
783
787 public $labelStatus = array();
788
792 public $labelStatusShort = array();
793
797 public $tpl;
798
799
803 public $showphoto_on_popup;
804
808 public $nb = array();
809
813 public $nbphoto;
814
818 public $output;
819
823 public $extraparams = array();
824
828 protected $childtables = array();
829
835 protected $childtablesoncascade = array();
836
840 public $product;
841
845 public $cond_reglement_supplier_id;
846
852 public $deposit_percent;
853
854
858 public $retained_warranty_fk_cond_reglement;
859
863 public $warehouse_id;
864
868 public $isextrafieldmanaged = 0;
869
870
871 // No constructor as it is an abstract class
872
878 protected function deprecatedProperties()
879 {
880 return array(
881 'alreadypaid' => 'totalpaid',
882 'cond_reglement' => 'depr_cond_reglement',
883 //'note' => 'note_private', // Some classes needs ->note and others need ->note_public/private so we can't manage deprecation for this field with dolDeprecationHandler
884 'commandeFournisseur' => 'origin_object',
885 'expedition' => 'origin_object',
886 'fk_project' => 'fk_project',
887 'livraison' => 'origin_object',
888 'projet' => 'project',
889 'statut' => 'status',
890 );
891 }
892
893
904 public static function isExistingObject($element, $id, $ref = '', $ref_ext = '')
905 {
906 global $db, $conf;
907
908 $sql = "SELECT rowid, ref, ref_ext";
909 $sql .= " FROM ".$db->prefix().$element;
910 $sql .= " WHERE entity IN (".getEntity($element).")";
911
912 if ($id > 0) {
913 $sql .= " AND rowid = ".((int) $id);
914 } elseif ($ref) {
915 $sql .= " AND ref = '".$db->escape($ref)."'";
916 } elseif ($ref_ext) {
917 $sql .= " AND ref_ext = '".$db->escape($ref_ext)."'";
918 } else {
919 $error = 'ErrorWrongParameters';
920 dol_syslog(get_class()."::isExistingObject ".$error, LOG_ERR);
921 return -1;
922 }
923 if ($ref || $ref_ext) { // Because the same ref can exists in 2 different entities, we force the current one in priority
924 $sql .= " AND entity = ".((int) $conf->entity);
925 }
926
927 dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
928 $resql = $db->query($sql);
929 if ($resql) {
930 $num = $db->num_rows($resql);
931 if ($num > 0) {
932 return 1;
933 } else {
934 return 0;
935 }
936 }
937 return -1;
938 }
939
945 public function isEmpty()
946 {
947 return (empty($this->id));
948 }
949
957 {
958 if (!empty($object->error)) {
959 $this->error = $object->error;
960 }
961 if (!empty($object->errors)) {
962 $this->errors = array_merge($this->errors, $object->errors);
963 }
964 }
965
973 public function getTooltipContentArray($params)
974 {
975 return [];
976 }
977
985 public function getTooltipContent($params)
986 {
987 global $action, $extrafields, $langs, $hookmanager;
988
989 // If there is too much extrafields, we do not include them into tooltip
990 $MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP = getDolGlobalInt('MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP', 3);
991
992 $data = $this->getTooltipContentArray($params);
993 $count = 0;
994
995 // Add extrafields
996 if (!empty($extrafields->attributes[$this->table_element]['label'])) {
997 $data['opendivextra'] = '<div class="centpercent wordbreak divtooltipextra">';
998 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
999 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
1000 continue;
1001 }
1002 if ($count >= abs($MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP)) {
1003 $data['more_extrafields'] = '<br>...';
1004 break;
1005 }
1006 $enabled = 1;
1007 if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
1008 $enabled = (int) dol_eval((string) $extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
1009 }
1010 if ($enabled && isset($extrafields->attributes[$this->table_element]['list'][$key])) {
1011 $enabled = (int) dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
1012 }
1013 $perms = 1;
1014 if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
1015 $perms = (int) dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
1016 }
1017 if (empty($enabled)) {
1018 continue; // 0 = Never visible field
1019 }
1020 if (abs($enabled) != 1 && abs($enabled) != 3 && abs($enabled) != 5 && abs($enabled) != 4) {
1021 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list <> 4 = not visible at the creation
1022 }
1023 if (empty($perms)) {
1024 continue; // 0 = Not visible
1025 }
1026 if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
1027 $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
1028 }
1029 $labelextra = $langs->trans((string) $extrafields->attributes[$this->table_element]['label'][$key]);
1030 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
1031 $data[$key] = '<br><b><u>'. $labelextra . '</u></b>';
1032 } else {
1033 $value = (empty($this->array_options['options_' . $key]) ? '' : $this->array_options['options_' . $key]);
1034 $data[$key] = '<br><b>'. $labelextra . ':</b> ' . $extrafields->showOutputField($key, $value, '', $this->table_element);
1035 $count++;
1036 }
1037 }
1038 $data['closedivextra'] = '</div>';
1039 }
1040
1041 $hookmanager->initHooks(array($this->element . 'dao'));
1042 $parameters = array(
1043 'tooltipcontentarray' => &$data,
1044 'params' => $params,
1045 );
1046 // Note that $action and $object may have been modified by some hooks
1047 $hookmanager->executeHooks('getTooltipContent', $parameters, $this, $action);
1048
1049 //var_dump($data);
1050 $label = implode($data);
1051
1052 return $label;
1053 }
1054
1055
1061 public function errorsToString()
1062 {
1063 return $this->error.(is_array($this->errors) ? (($this->error != '' ? ', ' : '').implode(', ', $this->errors)) : '');
1064 }
1065
1066
1073 public function getFormatedCustomerRef($objref)
1074 {
1075 global $hookmanager;
1076
1077 $parameters = array('objref' => $objref);
1078 $action = '';
1079 $reshook = $hookmanager->executeHooks('getFormatedCustomerRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1080 if ($reshook > 0) {
1081 return $hookmanager->resArray['objref'];
1082 }
1083 return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
1084 }
1085
1092 public function getFormatedSupplierRef($objref)
1093 {
1094 global $hookmanager;
1095
1096 $parameters = array('objref' => $objref);
1097 $action = '';
1098 $reshook = $hookmanager->executeHooks('getFormatedSupplierRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1099 if ($reshook > 0) {
1100 return $hookmanager->resArray['objref'];
1101 }
1102 return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
1103 }
1104
1114 public function getFullAddress($withcountry = 0, $sep = "\n", $withregion = 0, $extralangcode = '')
1115 {
1116 if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country))) {
1117 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
1118 $tmparray = getCountry($this->country_id, 'all');
1119 $this->country_code = $tmparray['code'];
1120 $this->country = $tmparray['label'];
1121 }
1122
1123 if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_code))) {
1124 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
1125 $tmparray = getState($this->state_id, 'all', null, 1);
1126 $this->state_code = $tmparray['code'];
1127 $this->state = $tmparray['label'];
1128 $this->region_code = $tmparray['region_code'];
1129 $this->region = $tmparray['region'];
1130 }
1131
1132 return dol_format_address($this, $withcountry, $sep, null, 0, $extralangcode);
1133 }
1134
1135
1144 public function getLastMainDocLink($modulepart, $initsharekey = 0, $relativelink = 0)
1145 {
1146 global $user, $dolibarr_main_url_root;
1147
1148 if (empty($this->last_main_doc)) {
1149 return ''; // No way to known which document name to use
1150 }
1151
1152 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
1153 $ecmfile = new EcmFiles($this->db);
1154 $result = $ecmfile->fetch(0, '', $this->last_main_doc);
1155 if ($result < 0) {
1156 $this->error = $ecmfile->error;
1157 $this->errors = $ecmfile->errors;
1158 return -1;
1159 }
1160
1161 if (empty($ecmfile->id)) { // No entry into file index already exists, we should initialize the shared key manually.
1162 // Add entry into index
1163 if ($initsharekey) {
1164 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
1165
1166 // TODO We can't, we don't have full path of file, only last_main_doc and ->element, so we must first rebuild full path $destfull
1167 /*
1168 $ecmfile->filepath = $rel_dir;
1169 $ecmfile->filename = $filename;
1170 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
1171 $ecmfile->fullpath_orig = '';
1172 $ecmfile->gen_or_uploaded = 'generated';
1173 $ecmfile->description = ''; // indexed content
1174 $ecmfile->keywords = ''; // keyword content
1175 $ecmfile->share = getRandomPassword(true);
1176 $result = $ecmfile->create($user);
1177 if ($result < 0)
1178 {
1179 $this->error = $ecmfile->error;
1180 $this->errors = $ecmfile->errors;
1181 }
1182 */
1183 } else {
1184 return '';
1185 }
1186 } elseif (empty($ecmfile->share)) { // Entry into file index already exists but no share key is defined.
1187 // Add entry into index
1188 if ($initsharekey) {
1189 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
1190 $ecmfile->share = getRandomPassword(true);
1191 $ecmfile->update($user);
1192 } else {
1193 return '';
1194 }
1195 }
1196 // Define $urlwithroot
1197 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1198 // This is to use external domain name found into config file
1199 //if (DOL_URL_ROOT && ! preg_match('/\/$/', $urlwithouturlroot) && ! preg_match('/^\//', DOL_URL_ROOT)) $urlwithroot=$urlwithouturlroot.'/'.DOL_URL_ROOT;
1200 //else
1201 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT;
1202 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1203
1204 $forcedownload = 0;
1205
1206 $paramlink = '';
1207 //if (!empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart; // For sharing with hash (so public files), modulepart is not required.
1208 //if (!empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; // For sharing with hash (so public files), entity is not required.
1209 //$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath); // No need of name of file for public link, we will use the hash
1210 if (!empty($ecmfile->share)) {
1211 $paramlink .= ($paramlink ? '&' : '').'hashp='.$ecmfile->share; // Hash for public share
1212 }
1213 if ($forcedownload) {
1214 $paramlink .= ($paramlink ? '&' : '').'attachment=1';
1215 }
1216
1217 if ($relativelink) {
1218 $linktoreturn = 'document.php'.($paramlink ? '?'.$paramlink : '');
1219 } else {
1220 $linktoreturn = $urlwithroot.'/document.php'.($paramlink ? '?'.$paramlink : '');
1221 }
1222
1223 // Here $ecmfile->share is defined
1224 return $linktoreturn;
1225 }
1226
1227
1228 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1238 public function add_contact($fk_socpeople, $type_contact, $source = 'external', $notrigger = 0)
1239 {
1240 // phpcs:enable
1241 global $user, $langs;
1242
1243
1244 dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
1245
1246 // Check parameters
1247 if ($fk_socpeople <= 0) {
1248 $langs->load("errors");
1249 $this->error = $langs->trans("ErrorWrongValueForParameterX", "1");
1250 dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
1251 return -1;
1252 }
1253 if (!$type_contact) {
1254 $langs->load("errors");
1255 $this->error = $langs->trans("ErrorWrongValueForParameterX", "2");
1256 dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
1257 return -2;
1258 }
1259
1260 $id_type_contact = 0;
1261 if (is_numeric($type_contact)) {
1262 $id_type_contact = $type_contact;
1263 } else {
1264 // We look for id type_contact
1265 $sql = "SELECT tc.rowid";
1266 $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1267 $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1268 $sql .= " AND tc.source='".$this->db->escape($source)."'";
1269 $sql .= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
1270 //print $sql;
1271 $resql = $this->db->query($sql);
1272 if ($resql) {
1273 $obj = $this->db->fetch_object($resql);
1274 if ($obj) {
1275 $id_type_contact = $obj->rowid;
1276 }
1277 }
1278 }
1279
1280 if ($id_type_contact == 0) {
1281 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");
1282 return 0;
1283 }
1284
1285 $datecreate = dol_now();
1286
1287 // Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
1288 $TListeContacts = $this->liste_contact(-1, $source);
1289 $already_added = false;
1290 if (is_array($TListeContacts) && !empty($TListeContacts)) {
1291 foreach ($TListeContacts as $array_contact) {
1292 if ($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
1293 $already_added = true;
1294 break;
1295 }
1296 }
1297 }
1298
1299 if (!$already_added) {
1300 $this->db->begin();
1301
1302 // Insert into database
1303 $sql = "INSERT INTO ".$this->db->prefix()."element_contact";
1304 $sql .= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
1305 $sql .= " VALUES (".$this->id.", ".((int) $fk_socpeople)." , ";
1306 $sql .= "'".$this->db->idate($datecreate)."'";
1307 $sql .= ", 4, ".((int) $id_type_contact);
1308 $sql .= ")";
1309
1310 $resql = $this->db->query($sql);
1311 if ($resql) {
1312 if (!$notrigger) {
1313 $result = $this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
1314 if ($result < 0) {
1315 $this->db->rollback();
1316 return -1;
1317 }
1318 }
1319
1320 $this->db->commit();
1321 return 1;
1322 } else {
1323 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1324 $this->error = $this->db->errno();
1325 $this->db->rollback();
1326 return -2;
1327 } else {
1328 $this->error = $this->db->lasterror();
1329 $this->db->rollback();
1330 return -1;
1331 }
1332 }
1333 } else {
1334 return 0;
1335 }
1336 }
1337
1338 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1346 public function copy_linked_contact($objFrom, $source = 'internal')
1347 {
1348 // phpcs:enable
1349 $contacts = $objFrom->liste_contact(-1, $source);
1350 foreach ($contacts as $contact) {
1351 if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0) {
1352 return -1;
1353 }
1354 }
1355 return 1;
1356 }
1357
1358 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1368 public function update_contact($rowid, $statut, $type_contact_id = 0, $fk_socpeople = 0)
1369 {
1370 // phpcs:enable
1371 // Insert into database
1372 $sql = "UPDATE ".$this->db->prefix()."element_contact set";
1373 $sql .= " statut = ".((int) $statut);
1374 if ($type_contact_id) {
1375 $sql .= ", fk_c_type_contact = ".((int) $type_contact_id);
1376 }
1377 if ($fk_socpeople) {
1378 $sql .= ", fk_socpeople = ".((int) $fk_socpeople);
1379 }
1380 $sql .= " where rowid = ".((int) $rowid);
1381
1382 $resql = $this->db->query($sql);
1383 if ($resql) {
1384 return 0;
1385 } else {
1386 $this->error = $this->db->lasterror();
1387 return -1;
1388 }
1389 }
1390
1391 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1399 public function delete_contact($rowid, $notrigger = 0)
1400 {
1401 // phpcs:enable
1402 global $user;
1403
1404 $error = 0;
1405
1406 $this->db->begin();
1407
1408 if (!$error && empty($notrigger)) {
1409 // Call trigger
1410 $this->context['contact_id'] = ((int) $rowid);
1411 $result = $this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
1412 if ($result < 0) {
1413 $error++;
1414 }
1415 // End call triggers
1416 }
1417
1418 if (!$error) {
1419 dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
1420
1421 $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
1422 $sql .= " WHERE rowid = ".((int) $rowid);
1423
1424 $result = $this->db->query($sql);
1425 if (!$result) {
1426 $error++;
1427 $this->errors[] = $this->db->lasterror();
1428 }
1429 }
1430
1431 if (!$error) {
1432 $this->db->commit();
1433 return 1;
1434 } else {
1435 $this->error = $this->db->lasterror();
1436 $this->db->rollback();
1437 return -1;
1438 }
1439 }
1440
1441 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1449 public function delete_linked_contact($source = '', $code = '')
1450 {
1451 // phpcs:enable
1452 $listId = '';
1453 $temp = array();
1454 $typeContact = $this->liste_type_contact($source, '', 0, 0, $code);
1455
1456 if (!empty($typeContact)) {
1457 foreach ($typeContact as $key => $value) {
1458 array_push($temp, $key);
1459 }
1460 $listId = implode(",", $temp);
1461 }
1462
1463 // If $listId is empty, we have not criteria on fk_c_type_contact so we will delete record on element_id for
1464 // any type or record instead of only the ones of the current object. So we do nothing in such a case.
1465 if (empty($listId)) {
1466 return 0;
1467 }
1468
1469 $sql = "DELETE FROM ".$this->db->prefix()."element_contact";
1470 $sql .= " WHERE element_id = ".((int) $this->id);
1471 $sql .= " AND fk_c_type_contact IN (".$this->db->sanitize($listId).")";
1472
1473 dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
1474 if ($this->db->query($sql)) {
1475 return 1;
1476 } else {
1477 $this->error = $this->db->lasterror();
1478 return -1;
1479 }
1480 }
1481
1482 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1494 public function liste_contact($statusoflink = -1, $source = 'external', $list = 0, $code = '', $status = -1, $arrayoftcids = array())
1495 {
1496 // phpcs:enable
1497 global $langs;
1498
1499 $tab = array();
1500
1501 $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
1502 if ($source == 'internal') {
1503 $sql .= ", '-1' as socid, t.statut as statuscontact, t.login, t.photo, t.gender";
1504 }
1505 if ($source == 'external' || $source == 'thirdparty') {
1506 $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
1507 }
1508 $sql .= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
1509 if (empty($arrayoftcids)) {
1510 $sql .= ", tc.source, tc.element, tc.code, tc.libelle as type_label";
1511 }
1512 $sql .= " FROM";
1513 if (empty($arrayoftcids)) {
1514 $sql .= " ".$this->db->prefix()."c_type_contact as tc,";
1515 }
1516 $sql .= " ".$this->db->prefix()."element_contact as ec";
1517 if ($source == 'internal') { // internal contact (user)
1518 $sql .= " LEFT JOIN ".$this->db->prefix()."user as t on ec.fk_socpeople = t.rowid";
1519 }
1520 if ($source == 'external' || $source == 'thirdparty') { // external contact (socpeople)
1521 $sql .= " LEFT JOIN ".$this->db->prefix()."socpeople as t on ec.fk_socpeople = t.rowid";
1522 }
1523 $sql .= " WHERE ec.element_id = ".((int) $this->id);
1524 if (empty($arrayoftcids)) {
1525 $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1526 $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1527 if ($code) {
1528 $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1529 }
1530 if ($source == 'internal') {
1531 $sql .= " AND tc.source = 'internal'";
1532 }
1533 if ($source == 'external' || $source == 'thirdparty') {
1534 $sql .= " AND tc.source = 'external'";
1535 }
1536 $sql .= " AND tc.active = 1";
1537 } else {
1538 $sql .= " AND ec.fk_c_type_contact IN (".$this->db->sanitize(implode(',', $arrayoftcids)).")";
1539 }
1540 if ($status >= 0) {
1541 $sql .= " AND t.statut = ".((int) $status); // t is llx_user or llx_socpeople
1542 }
1543 if ($statusoflink >= 0) {
1544 $sql .= " AND ec.statut = ".((int) $statusoflink);
1545 }
1546 $sql .= " ORDER BY t.lastname ASC";
1547
1548 dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
1549 $resql = $this->db->query($sql);
1550 if ($resql) {
1551 $num = $this->db->num_rows($resql);
1552 $i = 0;
1553 while ($i < $num) {
1554 $obj = $this->db->fetch_object($resql);
1555
1556 if (!$list) {
1557 $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
1558 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_label);
1559 $tab[$i] = array(
1560 'parentId' => $this->id,
1561 'source' => $obj->source,
1562 'socid' => $obj->socid,
1563 'id' => $obj->id,
1564 'nom' => $obj->lastname, // For backward compatibility
1565 'civility' => $obj->civility,
1566 'lastname' => $obj->lastname,
1567 'firstname' => $obj->firstname,
1568 'email' => $obj->email,
1569 'login' => (empty($obj->login) ? '' : $obj->login),
1570 'photo' => (empty($obj->photo) ? '' : $obj->photo),
1571 'gender' => (empty($obj->gender) ? '' : $obj->gender),
1572 'statuscontact' => $obj->statuscontact,
1573 'rowid' => $obj->rowid,
1574 'code' => $obj->code,
1575 'libelle' => $libelle_type,
1576 'status' => $obj->statuslink,
1577 'fk_c_type_contact' => $obj->fk_c_type_contact
1578 );
1579 } else {
1580 $tab[$i] = $obj->id;
1581 }
1582
1583 $i++;
1584 }
1585
1586 return $tab;
1587 } else {
1588 $this->error = $this->db->lasterror();
1589 dol_print_error($this->db);
1590 return -1;
1591 }
1592 }
1593
1594
1601 public function swapContactStatus($rowid)
1602 {
1603 $sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1604 $sql .= " tc.code, tc.libelle as type_label";
1605 $sql .= " FROM (".$this->db->prefix()."element_contact as ec, ".$this->db->prefix()."c_type_contact as tc)";
1606 $sql .= " WHERE ec.rowid =".((int) $rowid);
1607 $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1608 $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1609
1610 dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1611 $resql = $this->db->query($sql);
1612 if ($resql) {
1613 $obj = $this->db->fetch_object($resql);
1614 $newstatut = ($obj->statut == 4) ? 5 : 4;
1615 $result = $this->update_contact($rowid, $newstatut);
1616 $this->db->free($resql);
1617 return $result;
1618 } else {
1619 $this->error = $this->db->error();
1620 dol_print_error($this->db);
1621 return -1;
1622 }
1623 }
1624
1625 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1636 public function liste_type_contact($source = 'internal', $order = 'position', $option = 0, $activeonly = 0, $code = '')
1637 {
1638 // phpcs:enable
1639 global $langs;
1640
1641 if (empty($order)) {
1642 $order = 'position';
1643 }
1644 if ($order == 'position') {
1645 $order .= ',code';
1646 }
1647
1648 $tab = array();
1649
1650 $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle as type_label, tc.position";
1651 $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1652 $sql .= " WHERE tc.element = '".$this->db->escape($this->element)."'";
1653 if ($activeonly == 1) {
1654 $sql .= " AND tc.active = 1"; // only the active types
1655 }
1656 if (!empty($source) && $source != 'all') {
1657 $sql .= " AND tc.source = '".$this->db->escape($source)."'";
1658 }
1659 if (!empty($code)) {
1660 $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1661 }
1662 $sql .= $this->db->order($order, 'ASC');
1663
1664 //print "sql=".$sql;
1665 $resql = $this->db->query($sql);
1666 if (!$resql) {
1667 $this->error = $this->db->lasterror();
1668 //dol_print_error($this->db);
1669 return null;
1670 }
1671
1672 $num = $this->db->num_rows($resql);
1673 $i = 0;
1674 while ($i < $num) {
1675 $obj = $this->db->fetch_object($resql);
1676
1677 $transkey = "TypeContact_".$this->element."_".$source."_".$obj->code;
1678 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_label);
1679 if (empty($option)) {
1680 $tab[$obj->rowid] = $libelle_type;
1681 } elseif ($option == 1) {
1682 $tab[$obj->code] = $libelle_type;
1683 } else {
1684 $tab[$obj->rowid] = array('id' => $obj->rowid, 'code' => $obj->code, 'label' => $libelle_type);
1685 }
1686 $i++;
1687 }
1688
1689 return $tab;
1690 }
1691
1703 public function listeTypeContacts($source = 'internal', $option = 0, $activeonly = 0, $code = '', $element = '', $excludeelement = '')
1704 {
1705 global $langs;
1706
1707 $langs->loadLangs(array('bills', 'contracts', 'interventions', 'orders', 'projects', 'propal', 'ticket', 'agenda'));
1708
1709 $tab = array();
1710
1711 $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle as type_label, tc.position, tc.element, tc.module";
1712 $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1713
1714 $sqlWhere = array();
1715 if (!empty($element)) {
1716 $sqlWhere[] = " tc.element='".$this->db->escape($element)."'";
1717 }
1718 if (!empty($excludeelement)) {
1719 $sqlWhere[] = " tc.element <> '".$this->db->escape($excludeelement)."'";
1720 }
1721
1722 if ($activeonly == 1) {
1723 $sqlWhere[] = " tc.active=1"; // only the active types
1724 }
1725
1726 if (!empty($source) && $source != 'all') {
1727 $sqlWhere[] = " tc.source='".$this->db->escape($source)."'";
1728 }
1729
1730 if (!empty($code)) {
1731 $sqlWhere[] = " tc.code='".$this->db->escape($code)."'";
1732 }
1733
1734 if (count($sqlWhere) > 0) {
1735 $sql .= " WHERE ".implode(' AND ', $sqlWhere);
1736 }
1737
1738 $sql .= $this->db->order('tc.element, tc.position', 'ASC');
1739
1740 dol_syslog(__METHOD__, LOG_DEBUG);
1741 $resql = $this->db->query($sql);
1742 if ($resql) {
1743 $num = $this->db->num_rows($resql);
1744 if ($num > 0) {
1745 $langs->loadLangs(array("propal", "orders", "bills", "suppliers", "contracts", "supplier_proposal"));
1746
1747 while ($obj = $this->db->fetch_object($resql)) {
1748 $modulename = $obj->module ?? $obj->element;
1749 if (strpos($obj->element, 'project') !== false) {
1750 $modulename = 'projet';
1751 } elseif ($obj->element == 'contrat') {
1752 $element = 'contract';
1753 } elseif ($obj->element == 'action') {
1754 $modulename = 'agenda';
1755 } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1756 $modulename = 'fournisseur';
1757 }
1758 if (isModEnabled($modulename)) {
1759 $libelle_element = $langs->trans('ContactDefault_'.$obj->element);
1760 $tmpelement = $obj->element;
1761 $transkey = "TypeContact_".$tmpelement."_".$source."_".$obj->code;
1762 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_label);
1763 $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1764 }
1765 }
1766 }
1767 return $tab;
1768 } else {
1769 $this->error = $this->db->lasterror();
1770 return null;
1771 }
1772 }
1773
1785 public function getIdContact($source, $code, $status = 0)
1786 {
1787 $result = array();
1788 $i = 0;
1789 // Particular case for shipping
1790 if ($this->element == 'shipping' && $this->origin_id != 0) {
1791 $id = $this->origin_id;
1792 $element = 'commande';
1793 } elseif ($this->element == 'reception' && $this->origin_id != 0) {
1794 $id = $this->origin_id;
1795 $element = 'order_supplier';
1796 } else {
1797 $id = $this->id;
1798 $element = $this->element;
1799 }
1800
1801 $sql = "SELECT ec.fk_socpeople";
1802 $sql .= " FROM ".$this->db->prefix()."element_contact as ec,";
1803 if ($source == 'internal') {
1804 $sql .= " ".$this->db->prefix()."user as c,";
1805 }
1806 if ($source == 'external') {
1807 $sql .= " ".$this->db->prefix()."socpeople as c,";
1808 }
1809 $sql .= " ".$this->db->prefix()."c_type_contact as tc";
1810 $sql .= " WHERE ec.element_id = ".((int) $id);
1811 $sql .= " AND ec.fk_socpeople = c.rowid";
1812 if ($source == 'internal') {
1813 $sql .= " AND c.entity IN (".getEntity('user').")";
1814 }
1815 if ($source == 'external') {
1816 $sql .= " AND c.entity IN (".getEntity('societe').")";
1817 }
1818 $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1819 $sql .= " AND tc.element = '".$this->db->escape($element)."'";
1820 $sql .= " AND tc.source = '".$this->db->escape($source)."'";
1821 if ($code) {
1822 $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1823 }
1824 $sql .= " AND tc.active = 1";
1825 if ($status) {
1826 $sql .= " AND ec.statut = ".((int) $status);
1827 }
1828
1829 dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1830 $resql = $this->db->query($sql);
1831 if ($resql) {
1832 while ($obj = $this->db->fetch_object($resql)) {
1833 $result[$i] = $obj->fk_socpeople;
1834 $i++;
1835 }
1836 } else {
1837 $this->error = $this->db->error();
1838 return null;
1839 }
1840
1841 return $result;
1842 }
1843
1844 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1851 public function fetch_contact($contactid = null)
1852 {
1853 // phpcs:enable
1854 if (empty($contactid)) {
1855 $contactid = $this->contact_id;
1856 }
1857
1858 if (empty($contactid)) {
1859 return 0;
1860 }
1861
1862 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1863 $contact = new Contact($this->db);
1864 $result = $contact->fetch($contactid);
1865 $this->contact = $contact;
1866 return $result;
1867 }
1868
1869 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1877 public function fetch_thirdparty($force_thirdparty_id = 0)
1878 {
1879 // phpcs:enable
1880 if (empty($this->socid) && empty($this->fk_soc) && empty($force_thirdparty_id)) {
1881 return 0;
1882 }
1883
1884 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1885
1886 $idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : 0);
1887 if ($force_thirdparty_id) {
1888 $idtofetch = $force_thirdparty_id;
1889 }
1890
1891 if ($idtofetch) {
1892 $thirdparty = new Societe($this->db);
1893 $result = $thirdparty->fetch($idtofetch);
1894 if ($result < 0) {
1895 $this->errors = array_merge($this->errors, $thirdparty->errors);
1896 }
1897 $this->thirdparty = $thirdparty;
1898
1899 // Use first price level if level not defined for third party
1900 if ((getDolGlobalString('PRODUIT_MULTIPRICES') || getDolGlobalString('PRODUIT_CUSTOMER_PRICES_AND_MULTIPRICES')) && empty($this->thirdparty->price_level)) {
1901 $this->thirdparty->price_level = 1;
1902 }
1903
1904 return $result;
1905 } else {
1906 return -1;
1907 }
1908 }
1909
1910
1918 public function fetchOneLike($ref)
1919 {
1920 if (!$this->table_ref_field) {
1921 return 0;
1922 }
1923
1924 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
1925 $sql .= " WHERE ".$this->table_ref_field." LIKE '".$this->db->escape($ref)."'"; // no escapeforlike here
1926 $sql .= " LIMIT 1";
1927
1928 $query = $this->db->query($sql);
1929
1930 if (!$this->db->num_rows($query)) {
1931 return 0;
1932 }
1933
1934 $result = $this->db->fetch_object($query);
1935
1936 if (method_exists($this, 'fetch')) {
1937 return $this->fetch($result->rowid);
1938 } else {
1939 $this->error = 'Fetch method not implemented on '.get_class($this);
1940 dol_syslog(get_class($this).'::fetchOneLike Error='.$this->error, LOG_ERR);
1941 array_push($this->errors, $this->error);
1942 return -1;
1943 }
1944 }
1945
1946 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1955 public function fetch_barcode()
1956 {
1957 // phpcs:enable
1958 return $this->fetchBarCode();
1959 }
1960
1968 public function fetchBarCode()
1969 {
1970 dol_syslog(get_class($this).'::fetchBarCode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1971
1972 $idtype = $this->barcode_type;
1973 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
1974 if ($this->element == 'product' && getDolGlobalString('PRODUIT_DEFAULT_BARCODE_TYPE')) {
1975 $idtype = getDolGlobalString('PRODUIT_DEFAULT_BARCODE_TYPE');
1976 } elseif ($this->element == 'societe') {
1977 $idtype = getDolGlobalString('GENBARCODE_BARCODETYPE_THIRDPARTY');
1978 } else {
1979 dol_syslog('Call fetchBarCode with barcode_type not defined and cannot be guessed', LOG_WARNING);
1980 }
1981 }
1982
1983 if ($idtype > 0) {
1984 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
1985 $sql = "SELECT rowid, code, libelle as label, coder";
1986 $sql .= " FROM ".$this->db->prefix()."c_barcode_type";
1987 $sql .= " WHERE rowid = ".((int) $idtype);
1988
1989 $resql = $this->db->query($sql);
1990 if ($resql) {
1991 $obj = $this->db->fetch_object($resql);
1992
1993 $this->barcode_type = $obj->rowid;
1994 $this->barcode_type_code = $obj->code;
1995 $this->barcode_type_label = $obj->label;
1996 $this->barcode_type_coder = $obj->coder;
1997 return 1;
1998 } else {
1999 dol_print_error($this->db);
2000 return -1;
2001 }
2002 }
2003 }
2004 return 0;
2005 }
2006
2012 public function fetchProject()
2013 {
2014 include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
2015
2016 if (empty($this->fk_project) && !empty($this->fk_projet)) {
2017 $this->fk_project = $this->fk_projet; // For backward compatibility
2018 }
2019 if (empty($this->fk_project)) {
2020 return 0;
2021 }
2022
2023 $project = new Project($this->db);
2024 $result = $project->fetch($this->fk_project);
2025
2026 $this->projet = $project; // deprecated
2027 $this->project = $project;
2028
2029 return $result;
2030 }
2031
2032 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2040 public function fetch_project()
2041 {
2042 // phpcs:enable
2043 return $this->fetchProject();
2044 }
2045
2046 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2054 public function fetch_projet()
2055 {
2056 // phpcs:enable
2057 return $this->fetchProject();
2058 }
2059
2060 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2066 public function fetch_product()
2067 {
2068 // phpcs:enable
2069 include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2070
2071 // @phan-suppress-next-line PhanUndeclaredProperty
2072 if (empty($this->fk_product)) {
2073 return 0;
2074 }
2075
2076 $product = new Product($this->db);
2077 // @phan-suppress-next-line PhanUndeclaredProperty
2078 $result = $product->fetch($this->fk_product);
2079
2080 $this->product = $product;
2081 return $result;
2082 }
2083
2084 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2091 public function fetch_user($userid)
2092 {
2093 // phpcs:enable
2094 $user = new User($this->db);
2095 $result = $user->fetch($userid);
2096 $this->user = $user;
2097 return $result;
2098 }
2099
2100 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2107 public function fetch_origin()
2108 {
2109 // phpcs:enable
2110 $origin = $this->origin ? $this->origin : $this->origin_type;
2111
2112 // Manage classes with non standard name
2113 if ($origin == 'shipping') {
2114 $origin = 'expedition';
2115 }
2116 if ($origin == 'delivery') {
2117 $origin = 'livraison';
2118 }
2119 if ($origin == 'order_supplier' || $origin == 'supplier_order') {
2120 $origin = 'commandeFournisseur';
2121 }
2122
2123 $classname = ucfirst($origin);
2124 $this->origin_object = new $classname($this->db);
2125 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
2126 $this->origin_object->fetch($this->origin_id);
2127 }
2128
2138 public function fetchObjectFrom($table, $field, $key, $element = null)
2139 {
2140 global $conf;
2141
2142 $result = false;
2143
2144 $sql = "SELECT rowid FROM ".$this->db->prefix().$table;
2145 $sql .= " WHERE ".$field." = '".$this->db->escape($key)."'";
2146 if (!empty($element)) {
2147 $sql .= " AND entity IN (".getEntity($element).")";
2148 } else {
2149 $sql .= " AND entity = ".((int) $conf->entity);
2150 }
2151
2152 dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
2153 $resql = $this->db->query($sql);
2154 if ($resql) {
2155 $obj = $this->db->fetch_object($resql);
2156 // Test for avoid error -1
2157 if ($obj) {
2158 if (method_exists($this, 'fetch')) {
2159 $result = $this->fetch($obj->rowid);
2160 } else {
2161 $this->error = 'fetch() method not implemented on '.get_class($this);
2162 dol_syslog(get_class($this).'::fetchOneLike Error='.$this->error, LOG_ERR);
2163 array_push($this->errors, $this->error);
2164 $result = -1;
2165 }
2166 }
2167 }
2168
2169 return $result;
2170 }
2171
2180 public function getValueFrom($table, $id, $field)
2181 {
2182 $result = false;
2183 if (!empty($id) && !empty($field) && !empty($table)) {
2184 $sql = "SELECT ".$field." FROM ".$this->db->prefix().$table;
2185 $sql .= " WHERE rowid = ".((int) $id);
2186
2187 dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
2188 $resql = $this->db->query($sql);
2189 if ($resql) {
2190 $row = $this->db->fetch_row($resql);
2191 $result = $row[0];
2192 }
2193 }
2194 return $result;
2195 }
2196
2213 public function setValueFrom($field, $value, $table = '', $id = null, $format = '', $id_field = '', $fuser = null, $trigkey = '', $fk_user_field = 'fk_user_modif')
2214 {
2215 global $user;
2216
2217 if (empty($table)) {
2218 $table = $this->table_element;
2219 }
2220 if (empty($id)) {
2221 $id = $this->id;
2222 }
2223 if (empty($format)) {
2224 $format = 'text';
2225 }
2226 if (empty($id_field)) {
2227 $id_field = 'rowid';
2228 }
2229
2230 // Special case
2231 if ($table == 'product' && $field == 'note_private') {
2232 $field = 'note';
2233 }
2234
2235 if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
2236 $fk_user_field = 'fk_user_mod';
2237 }
2238 if (in_array($table, array('prelevement_bons'))) { // TODO Add a field fk_user_modif into llx_prelevement_bons
2239 $fk_user_field = '';
2240 }
2241
2242 $oldvalue = null;
2243 if ($trigkey) {
2244 $sql = "SELECT " . $field;
2245 $sql .= " FROM " . MAIN_DB_PREFIX . $table;
2246 $sql .= " WHERE " . $id_field . " = " . ((int) $id);
2247
2248 $resql = $this->db->query($sql);
2249 if ($resql) {
2250 if ($obj = $this->db->fetch_object($resql)) {
2251 if ($format == 'date') {
2252 $oldvalue = $this->db->jdate($obj->$field);
2253 } else {
2254 $oldvalue = $obj->$field;
2255 }
2256 }
2257 } else {
2258 $this->error = $this->db->lasterror();
2259 return -1;
2260 }
2261 }
2262
2263 $error = 0;
2264
2265 dol_syslog(__METHOD__, LOG_DEBUG);
2266
2267 $this->db->begin();
2268
2269 $sql = "UPDATE ".$this->db->prefix().$table." SET ";
2270
2271 if ($format == 'text') {
2272 $sql .= $field." = '".$this->db->escape($value)."'";
2273 } elseif ($format == 'int') {
2274 $sql .= $field." = ".((int) $value);
2275 } elseif ($format == 'date') {
2276 $sql .= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
2277 } elseif ($format == 'dategmt') {
2278 $sql .= $field." = ".($value ? "'".$this->db->idate($value, 'gmt')."'" : "null");
2279 }
2280
2281 if ($fk_user_field) {
2282 if (!empty($fuser) && is_object($fuser)) {
2283 $sql .= ", ".$fk_user_field." = ".((int) $fuser->id);
2284 } elseif (empty($fuser) || $fuser != 'none') {
2285 $sql .= ", ".$fk_user_field." = ".((int) $user->id);
2286 }
2287 }
2288
2289 $sql .= " WHERE ".$id_field." = ".((int) $id);
2290
2291 $resql = $this->db->query($sql);
2292 if ($resql) {
2293 if ($trigkey) {
2294 // call trigger with updated object values
2295 if (method_exists($this, 'fetch')) {
2296 $result = $this->fetch($id);
2297 } else {
2298 $result = $this->fetchCommon($id);
2299 }
2300 $this->oldcopy = clone $this;
2301 if (property_exists($this->oldcopy, $field)) {
2302 $this->oldcopy->$field = $oldvalue;
2303 }
2304
2305 if ($result >= 0) {
2306 $result = $this->call_trigger($trigkey, (!empty($fuser) && is_object($fuser)) ? $fuser : $user); // This may set this->errors
2307 }
2308 if ($result < 0) {
2309 $error++;
2310 }
2311 }
2312
2313 if (!$error) {
2314 if (property_exists($this, $field)) {
2315 $this->$field = $value;
2316 }
2317 $this->db->commit();
2318 return 1;
2319 } else {
2320 $this->db->rollback();
2321 return -2;
2322 }
2323 } else {
2324 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2325 $this->error = 'DB_ERROR_RECORD_ALREADY_EXISTS';
2326 } else {
2327 $this->error = $this->db->lasterror();
2328 }
2329 $this->db->rollback();
2330 return -1;
2331 }
2332 }
2333
2334 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2345 public function load_previous_next_ref($filter, $fieldid, $nodbprefix = 0)
2346 {
2347 // phpcs:enable
2348 global $conf, $user;
2349
2350 if (!$this->table_element) {
2351 dol_print_error(null, get_class($this)."::load_previous_next_ref was called on object with property table_element not defined");
2352 return -1;
2353 }
2354 if ($fieldid == 'none') {
2355 return 1;
2356 }
2357
2358 // For backward compatibility
2359 if (in_array($this->table_element, array('facture_rec', 'facture_fourn_rec')) && $fieldid == 'title') {
2360 $fieldid = 'titre';
2361 }
2362
2363 // Security on socid
2364 $socid = 0;
2365 if ($user->socid > 0) {
2366 $socid = $user->socid;
2367 }
2368
2369 // this->ismultientitymanaged contains
2370 // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
2371 $aliastablesociete = 's';
2372 if ($this->element == 'societe') {
2373 $aliastablesociete = 'te'; // te as table_element
2374 }
2375 $restrictiononfksoc = empty($this->restrictiononfksoc) ? 0 : $this->restrictiononfksoc;
2376 $sql = "SELECT MAX(te.".$fieldid.")";
2377 $sql .= " FROM ".(empty($nodbprefix) ? $this->db->prefix() : '').$this->table_element." as te";
2378 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2379 $tmparray = explode('@', $this->ismultientitymanaged);
2380 $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
2381 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2382 $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
2383 } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2384 $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
2385 }
2386 if ($restrictiononfksoc && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2387 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
2388 }
2389 if ($fieldid == 'rowid') {
2390 $sql .= " WHERE te.".$fieldid." < ".((int) $this->id);
2391 } else {
2392 $sql .= " WHERE te.".$fieldid." < '".$this->db->escape($this->ref)."'"; // ->ref must always be defined (set to id if field does not exists)
2393 }
2394 if ($restrictiononfksoc == 1 && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2395 $sql .= " AND sc.fk_user = ".((int) $user->id);
2396 }
2397 if ($restrictiononfksoc == 2 && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2398 $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
2399 }
2400
2401 $filtermax = $filter;
2402
2403 // Manage filter
2404 $errormessage = '';
2405 $tmpsql = forgeSQLFromUniversalSearchCriteria($filtermax, $errormessage);
2406 if ($errormessage) {
2407 if (!preg_match('/^\s*AND/i', $filtermax)) {
2408 $sql .= " AND ";
2409 }
2410 $sql .= $filtermax;
2411 } else {
2412 $sql .= $tmpsql;
2413 }
2414
2415 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2416 $tmparray = explode('@', $this->ismultientitymanaged);
2417 $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
2418 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2419 $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
2420 }
2421 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2422 if ($this->element == 'user' && getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE')) {
2423 if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
2424 $sql .= " AND te.entity IS NOT NULL"; // Show all users
2425 } else {
2426 $sql .= " AND te.rowid IN (SELECT ug.fk_user FROM ".$this->db->prefix()."usergroup_user as ug WHERE ug.entity IN (".getEntity('usergroup')."))";
2427 }
2428 } else {
2429 $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
2430 }
2431 }
2432 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
2433 $tmparray = explode('@', $this->ismultientitymanaged);
2434 $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
2435 }
2436 if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
2437 $sql .= ' AND te.fk_soc = '.((int) $socid);
2438 }
2439 if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
2440 $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
2441 }
2442 if ($restrictiononfksoc && $socid && $this->element == 'societe') {
2443 $sql .= ' AND te.rowid = '.((int) $socid);
2444 }
2445 //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
2446
2447 $result = $this->db->query($sql);
2448 if (!$result) {
2449 $this->error = $this->db->lasterror();
2450 return -1;
2451 }
2452 $row = $this->db->fetch_row($result);
2453 $this->ref_previous = $row[0];
2454
2455 $sql = "SELECT MIN(te.".$fieldid.")";
2456 $sql .= " FROM ".(empty($nodbprefix) ? $this->db->prefix() : '').$this->table_element." as te";
2457 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2458 $tmparray = explode('@', $this->ismultientitymanaged);
2459 $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
2460 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2461 $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
2462 } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2463 $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
2464 }
2465 if ($restrictiononfksoc && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2466 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
2467 }
2468 if ($fieldid == 'rowid') {
2469 $sql .= " WHERE te.".$fieldid." > ".((int) $this->id);
2470 } else {
2471 $sql .= " WHERE te.".$fieldid." > '".$this->db->escape($this->ref)."'"; // ->ref must always be defined (set to id if field does not exists)
2472 }
2473 if ($restrictiononfksoc == 1 && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2474 $sql .= " AND (sc.fk_user = ".((int) $user->id);
2475 if (getDolGlobalInt('MAIN_SEE_SUBORDINATES')) {
2476 $userschilds = $user->getAllChildIds();
2477 $sql .= " OR sc.fk_user IN (".$this->db->sanitize(implode(',', $userschilds)).")";
2478 }
2479 $sql .= ')';
2480 }
2481 if ($restrictiononfksoc == 2 && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2482 $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
2483 }
2484
2485 $filtermin = $filter;
2486
2487 // Manage filter
2488 $errormessage = '';
2489 $tmpsql = forgeSQLFromUniversalSearchCriteria($filtermin, $errormessage);
2490 if ($errormessage) {
2491 if (!preg_match('/^\s*AND/i', $filtermin)) {
2492 $sql .= " AND ";
2493 }
2494 $sql .= $filtermin;
2495
2496 $filtermin = '';
2497 } else {
2498 $sql .= $tmpsql;
2499 }
2500
2501 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2502 $tmparray = explode('@', $this->ismultientitymanaged);
2503 $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
2504 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2505 $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
2506 }
2507 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2508 if ($this->element == 'user' && getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE')) {
2509 if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
2510 $sql .= " AND te.entity IS NOT NULL"; // Show all users
2511 } else {
2512 $sql .= " AND te.rowid IN (SELECT ug.fk_user FROM ".$this->db->prefix()."usergroup_user as ug WHERE ug.entity IN (".getEntity('usergroup')."))";
2513 }
2514 } else {
2515 $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
2516 }
2517 }
2518 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
2519 $tmparray = explode('@', $this->ismultientitymanaged);
2520 $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
2521 }
2522 if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
2523 $sql .= ' AND te.fk_soc = '.((int) $socid);
2524 }
2525 if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
2526 $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
2527 }
2528 if ($restrictiononfksoc && $socid && $this->element == 'societe') {
2529 $sql .= ' AND te.rowid = '.((int) $socid);
2530 }
2531 //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
2532 // 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
2533
2534 $result = $this->db->query($sql);
2535 if (!$result) {
2536 $this->error = $this->db->lasterror();
2537 return -2;
2538 }
2539 $row = $this->db->fetch_row($result);
2540 $this->ref_next = $row[0];
2541
2542 return 1;
2543 }
2544
2545
2553 public function getListContactId($source = 'external')
2554 {
2555 $contactAlreadySelected = array();
2556 $tab = $this->liste_contact(-1, $source);
2557 $num = count($tab);
2558 $i = 0;
2559 while ($i < $num) {
2560 if ($source == 'thirdparty') {
2561 $contactAlreadySelected[$i] = $tab[$i]['socid'];
2562 } else {
2563 $contactAlreadySelected[$i] = $tab[$i]['id'];
2564 }
2565 $i++;
2566 }
2567 return $contactAlreadySelected;
2568 }
2569
2570
2578 public function setProject($projectid, $notrigger = 0)
2579 {
2580 global $user;
2581 $error = 0;
2582
2583 if (!$this->table_element) {
2584 dol_syslog(get_class($this)."::setProject was called on object with property table_element not defined", LOG_ERR);
2585 return -1;
2586 }
2587
2588 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2589 // @phan-suppress-next-line PhanTypeMismatchProperty
2590 if (!empty($this->fields['fk_project'])) { // Common case
2591 if ($projectid) {
2592 $sql .= " SET fk_project = ".((int) $projectid);
2593 } else {
2594 $sql .= " SET fk_project = NULL";
2595 }
2596 $sql .= ' WHERE rowid = '.((int) $this->id);
2597 } elseif ($this->table_element == 'actioncomm') { // Special case for actioncomm
2598 if ($projectid) {
2599 $sql .= " SET fk_project = ".((int) $projectid);
2600 } else {
2601 $sql .= " SET fk_project = NULL";
2602 }
2603 $sql .= ' WHERE id = '.((int) $this->id);
2604 } else { // Special case for old architecture objects
2605 if ($projectid) {
2606 $sql .= ' SET fk_projet = '.((int) $projectid);
2607 } else {
2608 $sql .= ' SET fk_projet = NULL';
2609 }
2610 $sql .= " WHERE rowid = ".((int) $this->id);
2611 }
2612
2613 $this->db->begin();
2614
2615 dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
2616 if ($this->db->query($sql)) {
2617 $this->fk_project = ((int) $projectid);
2618 } else {
2619 dol_print_error($this->db);
2620 $error++;
2621 }
2622
2623 // Triggers
2624 if (!$error && !$notrigger) {
2625 // Call triggers
2626 $result = $this->call_trigger(strtoupper($this->element) . '_MODIFY', $user);
2627 if ($result < 0) {
2628 $error++;
2629 } //Do also here what you must do to rollback action if trigger fail
2630 // End call triggers
2631 }
2632
2633 // Commit or rollback
2634 if ($error) {
2635 $this->db->rollback();
2636 return -1;
2637 } else {
2638 $this->db->commit();
2639 return 1;
2640 }
2641 }
2642
2649 public function setPaymentMethods($id)
2650 {
2651 global $user;
2652
2653 $error = 0;
2654 $notrigger = 0;
2655
2656 dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
2657
2658 if ($this->status >= 0 || $this->element == 'societe') {
2659 // TODO uniformize field name
2660 $fieldname = 'fk_mode_reglement';
2661 if ($this->element == 'societe') {
2662 $fieldname = 'mode_reglement';
2663 }
2664 if (get_class($this) == 'Fournisseur') {
2665 $fieldname = 'mode_reglement_supplier';
2666 }
2667 if (get_class($this) == 'Tva') {
2668 $fieldname = 'fk_typepayment';
2669 }
2670 if (get_class($this) == 'Salary') {
2671 $fieldname = 'fk_typepayment';
2672 }
2673
2674 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2675 $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2676 $sql .= ' WHERE rowid='.((int) $this->id);
2677
2678 if ($this->db->query($sql)) {
2679 $this->mode_reglement_id = $id;
2680 // for supplier
2681 if (get_class($this) == 'Fournisseur') {
2682 $this->mode_reglement_supplier_id = $id;
2683 }
2684 // Triggers
2685 if (!$error && !$notrigger) {
2686 // Call triggers
2687 if (get_class($this) == 'Commande') {
2688 $result = $this->call_trigger('ORDER_MODIFY', $user);
2689 } else {
2690 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
2691 }
2692 if ($result < 0) {
2693 $error++;
2694 }
2695 // End call triggers
2696 }
2697 return 1;
2698 } else {
2699 dol_syslog(get_class($this).'::setPaymentMethods Error '.$this->db->error());
2700 $this->error = $this->db->error();
2701 return -1;
2702 }
2703 } else {
2704 dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
2705 $this->error = 'Status of the object is incompatible '.$this->status;
2706 return -2;
2707 }
2708 }
2709
2716 public function setMulticurrencyCode($code)
2717 {
2718 dol_syslog(get_class($this).'::setMulticurrencyCode('.$code.')');
2719 if ($this->status >= 0 || $this->element == 'societe') {
2720 $fieldname = 'multicurrency_code';
2721
2722 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2723 $sql .= " SET ".$fieldname." = '".$this->db->escape($code)."'";
2724 $sql .= ' WHERE rowid='.((int) $this->id);
2725
2726 if ($this->db->query($sql)) {
2727 $this->multicurrency_code = $code;
2728
2729 list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
2730 if ($rate) {
2731 $this->setMulticurrencyRate($rate, 2);
2732 }
2733
2734 return 1;
2735 } else {
2736 dol_syslog(get_class($this).'::setMulticurrencyCode Error '.$sql.' - '.$this->db->error());
2737 $this->error = $this->db->error();
2738 return -1;
2739 }
2740 } else {
2741 dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
2742 $this->error = 'Status of the object is incompatible '.$this->status;
2743 return -2;
2744 }
2745 }
2746
2754 public function setMulticurrencyRate($rate, $mode = 1)
2755 {
2756 dol_syslog(get_class($this).'::setMulticurrencyRate('.$rate.', '.$mode.')');
2757 if ($this->status >= 0 || $this->element == 'societe') {
2758 $fieldname = 'multicurrency_tx';
2759
2760 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2761 $sql .= " SET ".$fieldname." = ".((float) $rate);
2762 $sql .= ' WHERE rowid='.((int) $this->id);
2763
2764 if ($this->db->query($sql)) {
2765 $this->multicurrency_tx = $rate;
2766
2767 // Update line price
2768 if (!empty($this->lines)) {
2769 foreach ($this->lines as &$line) {
2770 // Amounts in company currency will be recalculated
2771 if ($mode == 1) {
2772 $line->subprice = 0;
2773 }
2774
2775 // Amounts in foreign currency will be recalculated
2776 if ($mode == 2) {
2777 $line->multicurrency_subprice = 0;
2778 }
2779
2780 switch ($this->element) {
2781 case 'propal':
2784 '@phan-var-force Propal $this';
2785 '@phan-var-force PropaleLigne $line';
2786 $this->updateline(
2787 $line->id,
2788 $line->subprice,
2789 $line->qty,
2790 $line->remise_percent,
2791 $line->tva_tx,
2792 $line->localtax1_tx,
2793 $line->localtax2_tx,
2794 ($line->description ? $line->description : $line->desc),
2795 'HT',
2796 $line->info_bits,
2797 $line->special_code,
2798 $line->fk_parent_line,
2799 $line->skip_update_total,
2800 $line->fk_fournprice,
2801 $line->pa_ht,
2802 $line->label,
2803 $line->product_type,
2804 $line->date_start,
2805 $line->date_end,
2806 $line->array_options,
2807 $line->fk_unit,
2808 $line->multicurrency_subprice
2809 );
2810 break;
2811 case 'commande':
2814 '@phan-var-force Commande $this';
2815 '@phan-var-force OrderLine $line';
2816 $this->updateline(
2817 $line->id,
2818 ($line->description ? $line->description : $line->desc),
2819 $line->subprice,
2820 $line->qty,
2821 $line->remise_percent,
2822 $line->tva_tx,
2823 $line->localtax1_tx,
2824 $line->localtax2_tx,
2825 'HT',
2826 $line->info_bits,
2827 $line->date_start,
2828 $line->date_end,
2829 $line->product_type,
2830 $line->fk_parent_line,
2831 $line->skip_update_total,
2832 $line->fk_fournprice,
2833 $line->pa_ht,
2834 $line->label,
2835 $line->special_code,
2836 $line->array_options,
2837 $line->fk_unit,
2838 $line->multicurrency_subprice
2839 );
2840 break;
2841 case 'facture':
2844 '@phan-var-force Facture $this';
2845 '@phan-var-force FactureLigne $line';
2846 $this->updateline(
2847 $line->id,
2848 ($line->description ? $line->description : $line->desc),
2849 $line->subprice,
2850 $line->qty,
2851 $line->remise_percent,
2852 $line->date_start,
2853 $line->date_end,
2854 $line->tva_tx,
2855 $line->localtax1_tx,
2856 $line->localtax2_tx,
2857 'HT',
2858 $line->info_bits,
2859 $line->product_type,
2860 $line->fk_parent_line,
2861 $line->skip_update_total,
2862 $line->fk_fournprice,
2863 $line->pa_ht,
2864 $line->label,
2865 $line->special_code,
2866 $line->array_options,
2867 $line->situation_percent,
2868 $line->fk_unit,
2869 $line->multicurrency_subprice
2870 );
2871 break;
2872 case 'supplier_proposal':
2875 '@phan-var-force SupplierProposal $this';
2876 '@phan-var-force SupplierProposalLine $line';
2877 $this->updateline(
2878 $line->id,
2879 $line->subprice,
2880 $line->qty,
2881 $line->remise_percent,
2882 $line->tva_tx,
2883 $line->localtax1_tx,
2884 $line->localtax2_tx,
2885 ($line->description ? $line->description : $line->desc),
2886 'HT',
2887 $line->info_bits,
2888 $line->special_code,
2889 $line->fk_parent_line,
2890 $line->skip_update_total,
2891 $line->fk_fournprice,
2892 $line->pa_ht,
2893 $line->label,
2894 $line->product_type,
2895 $line->array_options,
2896 $line->ref_fourn,
2897 $line->multicurrency_subprice
2898 );
2899 break;
2900 case 'order_supplier':
2903 '@phan-var-force CommandeFournisseur $this';
2904 '@phan-var-force CommandeFournisseurLigne $line';
2905 $this->updateline(
2906 $line->id,
2907 ($line->description ? $line->description : $line->desc),
2908 $line->subprice,
2909 $line->qty,
2910 $line->remise_percent,
2911 $line->tva_tx,
2912 $line->localtax1_tx,
2913 $line->localtax2_tx,
2914 'HT',
2915 $line->info_bits,
2916 $line->product_type,
2917 0,
2918 $line->date_start,
2919 $line->date_end,
2920 $line->array_options,
2921 $line->fk_unit,
2922 $line->multicurrency_subprice,
2923 $line->ref_supplier
2924 );
2925 break;
2926 case 'invoice_supplier':
2929 '@phan-var-force FactureFournisseur $this';
2930 '@phan-var-force SupplierInvoiceLIne $line';
2931 $this->updateline(
2932 $line->id,
2933 ($line->description ? $line->description : $line->desc),
2934 $line->subprice,
2935 $line->tva_tx,
2936 $line->localtax1_tx,
2937 $line->localtax2_tx,
2938 $line->qty,
2939 0,
2940 'HT',
2941 $line->info_bits,
2942 $line->product_type,
2943 $line->remise_percent,
2944 0,
2945 $line->date_start,
2946 $line->date_end,
2947 $line->array_options,
2948 $line->fk_unit,
2949 $line->multicurrency_subprice,
2950 $line->ref_supplier
2951 );
2952 break;
2953 default:
2954 dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
2955 break;
2956 }
2957 }
2958 }
2959
2960 return 1;
2961 } else {
2962 dol_syslog(get_class($this).'::setMulticurrencyRate Error '.$sql.' - '.$this->db->error());
2963 $this->error = $this->db->error();
2964 return -1;
2965 }
2966 } else {
2967 dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
2968 $this->error = 'Status of the object is incompatible '.$this->status;
2969 return -2;
2970 }
2971 }
2972
2980 public function setPaymentTerms($id, $deposit_percent = null)
2981 {
2982 dol_syslog(get_class($this).'::setPaymentTerms('.$id.', '.var_export($deposit_percent, true).')');
2983 if ($this->status >= 0 || $this->element == 'societe') {
2984 // TODO uniformize field name
2985 $fieldname = 'fk_cond_reglement';
2986 if ($this->element == 'societe') {
2987 $fieldname = 'cond_reglement';
2988 }
2989 if (get_class($this) == 'Fournisseur') {
2990 $fieldname = 'cond_reglement_supplier';
2991 }
2992
2993 if (empty($deposit_percent) || $deposit_percent < 0) {
2994 $deposit_percent = (float) getDictionaryValue('c_payment_term', 'deposit_percent', $id);
2995 }
2996
2997 if ($deposit_percent > 100) {
2998 $deposit_percent = 100;
2999 }
3000
3001 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
3002 $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
3003 if (in_array($this->table_element, array('propal', 'commande', 'societe'))) {
3004 $sql .= " , deposit_percent = " . (empty($deposit_percent) ? 'NULL' : "'".$this->db->escape($deposit_percent)."'");
3005 }
3006 $sql .= ' WHERE rowid='.((int) $this->id);
3007
3008 if ($this->db->query($sql)) {
3009 $this->cond_reglement_id = $id;
3010 // for supplier
3011 if (get_class($this) == 'Fournisseur') {
3012 $this->cond_reglement_supplier_id = $id;
3013 }
3014 $this->cond_reglement = $id; // for compatibility
3015 $this->deposit_percent = $deposit_percent;
3016 return 1;
3017 } else {
3018 dol_syslog(get_class($this).'::setPaymentTerms Error '.$sql.' - '.$this->db->error());
3019 $this->error = $this->db->error();
3020 return -1;
3021 }
3022 } else {
3023 dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
3024 $this->error = 'Status of the object is incompatible '.$this->status;
3025 return -2;
3026 }
3027 }
3028
3035 public function setTransportMode($id)
3036 {
3037 dol_syslog(get_class($this).'::setTransportMode('.$id.')');
3038 if ($this->status >= 0 || $this->element == 'societe') {
3039 $fieldname = 'fk_transport_mode';
3040 if ($this->element == 'societe') {
3041 $fieldname = 'transport_mode';
3042 }
3043 if (get_class($this) == 'Fournisseur') {
3044 $fieldname = 'transport_mode_supplier';
3045 }
3046
3047 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
3048 $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
3049 $sql .= ' WHERE rowid='.((int) $this->id);
3050
3051 if ($this->db->query($sql)) {
3052 $this->transport_mode_id = $id;
3053 // for supplier
3054 if (get_class($this) == 'Fournisseur') {
3055 $this->transport_mode_supplier_id = $id;
3056 }
3057 return 1;
3058 } else {
3059 dol_syslog(get_class($this).'::setTransportMode Error '.$sql.' - '.$this->db->error());
3060 $this->error = $this->db->error();
3061 return -1;
3062 }
3063 } else {
3064 dol_syslog(get_class($this).'::setTransportMode, status of the object is incompatible');
3065 $this->error = 'Status of the object is incompatible '.$this->status;
3066 return -2;
3067 }
3068 }
3069
3077 {
3078 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms('.$id.')');
3079 if ($this->status >= 0 || $this->element == 'societe') {
3080 $fieldname = 'retained_warranty_fk_cond_reglement';
3081
3082 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
3083 $sql .= " SET ".$fieldname." = ".((int) $id);
3084 $sql .= ' WHERE rowid='.((int) $this->id);
3085
3086 if ($this->db->query($sql)) {
3087 $this->retained_warranty_fk_cond_reglement = $id;
3088 return 1;
3089 } else {
3090 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms Error '.$sql.' - '.$this->db->error());
3091 $this->error = $this->db->error();
3092 return -1;
3093 }
3094 } else {
3095 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms, status of the object is incompatible');
3096 $this->error = 'Status of the object is incompatible '.$this->status;
3097 return -2;
3098 }
3099 }
3100
3108 public function setDeliveryAddress($id)
3109 {
3110 $fieldname = 'fk_delivery_address';
3111 if ($this->element == 'delivery' || $this->element == 'shipping') {
3112 $fieldname = 'fk_address';
3113 }
3114
3115 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ".$fieldname." = ".((int) $id);
3116 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
3117
3118 if ($this->db->query($sql)) {
3119 $this->fk_delivery_address = $id;
3120 return 1;
3121 } else {
3122 $this->error = $this->db->error();
3123 dol_syslog(get_class($this).'::setDeliveryAddress Error '.$this->error);
3124 return -1;
3125 }
3126 }
3127
3128
3137 public function setShippingMethod($shipping_method_id, $notrigger = 0, $userused = null)
3138 {
3139 global $user;
3140
3141 if (empty($userused)) {
3142 $userused = $user;
3143 }
3144
3145 $error = 0;
3146
3147 if (!$this->table_element) {
3148 dol_syslog(get_class($this)."::setShippingMethod was called on object with property table_element not defined", LOG_ERR);
3149 return -1;
3150 }
3151
3152 $this->db->begin();
3153
3154 if ($shipping_method_id < 0) {
3155 $shipping_method_id = 'NULL';
3156 }
3157 dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
3158
3159 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3160 $sql .= " SET fk_shipping_method = ".((int) $shipping_method_id);
3161 $sql .= " WHERE rowid=".((int) $this->id);
3162 $resql = $this->db->query($sql);
3163 if (!$resql) {
3164 dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
3165 $this->error = $this->db->lasterror();
3166 $error++;
3167 } else {
3168 if (!$notrigger) {
3169 // Call trigger
3170 $this->context = array('shippingmethodupdate' => 1);
3171 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused);
3172 if ($result < 0) {
3173 $error++;
3174 }
3175 // End call trigger
3176 }
3177 }
3178 if ($error) {
3179 $this->db->rollback();
3180 return -1;
3181 } else {
3182 $this->shipping_method_id = ($shipping_method_id == 'NULL') ? null : $shipping_method_id;
3183 $this->db->commit();
3184 return 1;
3185 }
3186 }
3187
3188
3195 public function setWarehouse($warehouse_id)
3196 {
3197 if (!$this->table_element) {
3198 dol_syslog(get_class($this)."::setWarehouse was called on object with property table_element not defined", LOG_ERR);
3199 return -1;
3200 }
3201 if ($warehouse_id < 0) {
3202 $warehouse_id = 'NULL';
3203 }
3204 dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
3205
3206 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3207 $sql .= " SET fk_warehouse = ".((int) $warehouse_id);
3208 $sql .= " WHERE rowid=".((int) $this->id);
3209
3210 if ($this->db->query($sql)) {
3211 $this->warehouse_id = ($warehouse_id == 'NULL') ? null : $warehouse_id;
3212 return 1;
3213 } else {
3214 dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
3215 $this->error = $this->db->error();
3216 return 0;
3217 }
3218 }
3219
3220
3228 public function setDocModel($user, $modelpdf)
3229 {
3230 if (!$this->table_element) {
3231 dol_syslog(get_class($this)."::setDocModel was called on object with property table_element not defined", LOG_ERR);
3232 return -1;
3233 }
3234
3235 $newmodelpdf = dol_trunc($modelpdf, 255);
3236
3237 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3238 $sql .= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
3239 $sql .= " WHERE rowid = ".((int) $this->id);
3240
3241 dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
3242 $resql = $this->db->query($sql);
3243 if ($resql) {
3244 $this->model_pdf = $modelpdf;
3245 return 1;
3246 } else {
3247 dol_print_error($this->db);
3248 return 0;
3249 }
3250 }
3251
3252
3261 public function setBankAccount($fk_account, $notrigger = 0, $userused = null)
3262 {
3263 global $user;
3264
3265 if (empty($userused)) {
3266 $userused = $user;
3267 }
3268
3269 $error = 0;
3270
3271 if (!$this->table_element) {
3272 dol_syslog(get_class($this)."::setBankAccount was called on object with property table_element not defined", LOG_ERR);
3273 return -1;
3274 }
3275 $this->db->begin();
3276
3277 if ($fk_account < 0) {
3278 $fk_account = 'NULL';
3279 }
3280 dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
3281
3282 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3283 $sql .= " SET fk_account = ".((int) $fk_account);
3284 $sql .= " WHERE rowid=".((int) $this->id);
3285
3286 $resql = $this->db->query($sql);
3287 if (!$resql) {
3288 dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
3289 $this->error = $this->db->lasterror();
3290 $error++;
3291 } else {
3292 if (!$notrigger) {
3293 // Call trigger
3294 $this->context['bankaccountupdate'] = 1;
3295 $triggerName = strtoupper(get_class($this)).'_MODIFY';
3296 // Special cases
3297 if ($triggerName == 'FACTUREREC_MODIFY') {
3298 $triggerName = 'BILLREC_MODIFY';
3299 }
3300 $result = $this->call_trigger($triggerName, $userused);
3301 if ($result < 0) {
3302 $error++;
3303 }
3304 // End call trigger
3305 }
3306 }
3307 if ($error) {
3308 $this->db->rollback();
3309 return -1;
3310 } else {
3311 $this->fk_account = ($fk_account == 'NULL') ? null : $fk_account;
3312 $this->db->commit();
3313 return 1;
3314 }
3315 }
3316
3317
3318 // TODO: Move line related operations to CommonObjectLine?
3319
3320 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3330 public function line_order($renum = false, $rowidorder = 'ASC', $fk_parent_line = true)
3331 {
3332 // phpcs:enable
3333 if (!$this->table_element_line) {
3334 dol_syslog(get_class($this)."::line_order was called on object with property table_element_line not defined", LOG_ERR);
3335 return -1;
3336 }
3337 if (!$this->fk_element) {
3338 dol_syslog(get_class($this)."::line_order was called on object with property fk_element not defined", LOG_ERR);
3339 return -1;
3340 }
3341
3342 $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3343 if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3344 $fieldposition = 'position';
3345 }
3346
3347 // Count number of lines to reorder (according to choice $renum)
3348 $nl = 0;
3349 $sql = "SELECT count(rowid) FROM ".$this->db->prefix().$this->table_element_line;
3350 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3351 if (!$renum) {
3352 $sql .= " AND " . $fieldposition . " = 0";
3353 }
3354 if ($renum) {
3355 $sql .= " AND " . $fieldposition . " <> 0";
3356 }
3357
3358 dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
3359 $resql = $this->db->query($sql);
3360 if ($resql) {
3361 $row = $this->db->fetch_row($resql);
3362 $nl = $row[0];
3363 } else {
3364 dol_print_error($this->db);
3365 }
3366 if ($nl > 0) {
3367 // The goal of this part is to reorder all lines, with all children lines sharing the same counter that parents.
3368 $rows = array();
3369
3370 // We first search all lines that are parent lines (for multilevel details lines)
3371 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3372 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3373 if ($fk_parent_line) {
3374 $sql .= ' AND fk_parent_line IS NULL';
3375 }
3376 $sql .= " ORDER BY " . $fieldposition . " ASC, rowid " . $rowidorder;
3377
3378 dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
3379 $resql = $this->db->query($sql);
3380 if ($resql) {
3381 $i = 0;
3382 $num = $this->db->num_rows($resql);
3383 $grandchild = getDolGlobalInt('MAIN_CARE_GRANDCHILD');
3384 while ($i < $num) {
3385 $row = $this->db->fetch_row($resql);
3386 $rows[] = $row[0]; // Add parent line into array rows
3387 $children = $this->getChildrenOfLine($row[0], $grandchild);
3388 if (!empty($children)) {
3389 foreach ($children as $child) {
3390 array_push($rows, $child);
3391 }
3392 }
3393 $i++;
3394 }
3395
3396 // Now we set a new number for each lines (parent and children with children included into parent tree)
3397 if (!empty($rows)) {
3398 foreach ($rows as $key => $row) {
3399 $this->updateRangOfLine($row, ($key + 1));
3400 }
3401 }
3402 } else {
3403 dol_print_error($this->db);
3404 }
3405 }
3406 return 1;
3407 }
3408
3416 public function getChildrenOfLine($id, $includealltree = 0)
3417 {
3418 $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3419 if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3420 $fieldposition = 'position';
3421 }
3422
3423 $rows = array();
3424
3425 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3426 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3427 $sql .= ' AND fk_parent_line = '.((int) $id);
3428 $sql .= " ORDER BY " . $fieldposition . " ASC";
3429
3430 dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id, LOG_DEBUG);
3431
3432 $resql = $this->db->query($sql);
3433 if ($resql) {
3434 if ($this->db->num_rows($resql) > 0) {
3435 while ($row = $this->db->fetch_row($resql)) {
3436 $rows[] = $row[0];
3437 if (!empty($includealltree) && $includealltree <= 1000) { // Test <= 1000 is a protection in depth of recursive call to avoid infinite loop
3438 $rows = array_merge($rows, $this->getChildrenOfLine($row[0], $includealltree + 1));
3439 }
3440 }
3441 }
3442 }
3443 return $rows;
3444 }
3445
3446 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3454 public function line_up($rowid, $fk_parent_line = true)
3455 {
3456 // phpcs:enable
3457 $this->line_order(false, 'ASC', $fk_parent_line);
3458
3459 // Get rang of line
3460 $rang = $this->getRangOfLine($rowid);
3461
3462 // Update position of line
3463 $this->updateLineUp($rowid, $rang);
3464 }
3465
3466 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3474 public function line_down($rowid, $fk_parent_line = true)
3475 {
3476 // phpcs:enable
3477 $this->line_order(false, 'ASC', $fk_parent_line);
3478
3479 // Get rang of line
3480 $rang = $this->getRangOfLine($rowid);
3481
3482 // Get max value for rang
3483 $max = $this->line_max();
3484
3485 // Update position of line
3486 $this->updateLineDown($rowid, $rang, $max);
3487 }
3488
3496 public function updateRangOfLine($rowid, $rang)
3497 {
3498 global $hookmanager;
3499 $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3500 if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3501 $fieldposition = 'position';
3502 }
3503
3504 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3505 $sql .= ' WHERE rowid = '.((int) $rowid);
3506
3507 dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
3508 if (!$this->db->query($sql)) {
3509 dol_print_error($this->db);
3510 return -1;
3511 } else {
3512 $parameters = array('rowid' => $rowid, 'rang' => $rang, 'fieldposition' => $fieldposition);
3513 $action = '';
3514 $reshook = $hookmanager->executeHooks('afterRankOfLineUpdate', $parameters, $this, $action);
3515 return 1;
3516 }
3517 }
3518
3519 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3526 public function line_ajaxorder($rows)
3527 {
3528 // phpcs:enable
3529 $num = count($rows);
3530 for ($i = 0; $i < $num; $i++) {
3531 $this->updateRangOfLine($rows[$i], ($i + 1));
3532 }
3533 }
3534
3542 public function updateLineUp($rowid, $rang)
3543 {
3544 if ($rang > 1) {
3545 $fieldposition = 'rang';
3546 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3547 $fieldposition = 'position';
3548 }
3549
3550 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3551 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3552 $sql .= " AND " . $fieldposition . " = " . ((int) ($rang - 1));
3553 if ($this->db->query($sql)) {
3554 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang - 1));
3555 $sql .= ' WHERE rowid = '.((int) $rowid);
3556 if (!$this->db->query($sql)) {
3557 dol_print_error($this->db);
3558 }
3559 } else {
3560 dol_print_error($this->db);
3561 }
3562 }
3563 }
3564
3573 public function updateLineDown($rowid, $rang, $max)
3574 {
3575 if ($rang < $max) {
3576 $fieldposition = 'rang';
3577 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3578 $fieldposition = 'position';
3579 }
3580
3581 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3582 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3583 $sql .= " AND " . $fieldposition . " = " . ((int) ($rang + 1));
3584 if ($this->db->query($sql)) {
3585 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang + 1));
3586 $sql .= ' WHERE rowid = '.((int) $rowid);
3587 if (!$this->db->query($sql)) {
3588 dol_print_error($this->db);
3589 }
3590 } else {
3591 dol_print_error($this->db);
3592 }
3593 }
3594 }
3595
3602 public function getRangOfLine($rowid)
3603 {
3604 $fieldposition = 'rang';
3605 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3606 $fieldposition = 'position';
3607 }
3608
3609 $sql = "SELECT " . $fieldposition . " FROM ".$this->db->prefix().$this->table_element_line;
3610 $sql .= " WHERE rowid = ".((int) $rowid);
3611
3612 dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
3613 $resql = $this->db->query($sql);
3614 if ($resql) {
3615 $row = $this->db->fetch_row($resql);
3616 return $row[0];
3617 }
3618
3619 return 0;
3620 }
3621
3628 public function getIdOfLine($rang)
3629 {
3630 $fieldposition = 'rang';
3631 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3632 $fieldposition = 'position';
3633 }
3634
3635 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3636 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3637 $sql .= " AND " . $fieldposition . " = ".((int) $rang);
3638 $resql = $this->db->query($sql);
3639 if ($resql) {
3640 $row = $this->db->fetch_row($resql);
3641 return $row[0];
3642 }
3643
3644 return 0;
3645 }
3646
3647 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3654 public function line_max($fk_parent_line = 0)
3655 {
3656 // phpcs:enable
3657 $positionfield = 'rang';
3658 if (in_array($this->table_element, array('bom_bom', 'product_attribute'))) {
3659 $positionfield = 'position';
3660 }
3661
3662 // Search the last rang with fk_parent_line
3663 if ($fk_parent_line) {
3664 $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
3665 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3666 $sql .= " AND fk_parent_line = ".((int) $fk_parent_line);
3667
3668 dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
3669 $resql = $this->db->query($sql);
3670 if ($resql) {
3671 $row = $this->db->fetch_row($resql);
3672 if (!empty($row[0])) {
3673 return $row[0];
3674 } else {
3675 return $this->getRangOfLine($fk_parent_line);
3676 }
3677 }
3678 } else {
3679 // If not, search the last rang of element
3680 $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
3681 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3682
3683 dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
3684 $resql = $this->db->query($sql);
3685 if ($resql) {
3686 $row = $this->db->fetch_row($resql);
3687 return $row[0];
3688 }
3689 }
3690
3691 return 0;
3692 }
3693
3694 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3701 public function update_ref_ext($ref_ext)
3702 {
3703 // phpcs:enable
3704 if (!$this->table_element) {
3705 dol_syslog(get_class($this)."::update_ref_ext was called on object with property table_element not defined", LOG_ERR);
3706 return -1;
3707 }
3708
3709 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3710 $sql .= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
3711 // @phan-suppress-next-line PhanUndeclaredProperty
3712 $sql .= " WHERE ".(isset($this->table_rowid) ? $this->table_rowid : 'rowid')." = ".((int) $this->id);
3713
3714 dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
3715 if ($this->db->query($sql)) {
3716 $this->ref_ext = $ref_ext;
3717 return 1;
3718 } else {
3719 $this->error = $this->db->error();
3720 return -1;
3721 }
3722 }
3723
3724 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3733 public function update_note($note, $suffix = '', $notrigger = 0)
3734 {
3735 // phpcs:enable
3736 global $user;
3737
3738 if (!$this->table_element) {
3739 $this->error = 'update_note was called on object with property table_element not defined';
3740 dol_syslog(get_class($this)."::update_note was called on object with property table_element not defined", LOG_ERR);
3741 return -1;
3742 }
3743 if (!in_array($suffix, array('', '_public', '_private'))) {
3744 $this->error = 'update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
3745 dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
3746 return -2;
3747 }
3748
3749 $newsuffix = $suffix;
3750
3751 // Special case
3752 if ($this->table_element == 'product' && $newsuffix == '_private') {
3753 $newsuffix = '';
3754 }
3755 if (in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
3756 $fieldusermod = "fk_user_mod";
3757 } elseif ($this->table_element == 'ecm_files') {
3758 $fieldusermod = "fk_user_m";
3759 } else {
3760 $fieldusermod = "fk_user_modif";
3761 }
3762 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3763 $sql .= " SET note".$newsuffix." = ".(!empty($note) ? ("'".$this->db->escape($note)."'") : "NULL");
3764 $sql .= ", ".$fieldusermod." = ".((int) $user->id);
3765 $sql .= " WHERE rowid = ".((int) $this->id);
3766
3767 dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
3768 if ($this->db->query($sql)) {
3769 if ($suffix == '_public') {
3770 $this->note_public = $note;
3771 } elseif ($suffix == '_private') {
3772 $this->note_private = $note;
3773 } else {
3774 $this->note = $note; // deprecated
3775 $this->note_private = $note;
3776 }
3777 if (empty($notrigger)) {
3778 switch ($this->element) {
3779 case 'societe':
3780 $trigger_name = 'COMPANY_MODIFY';
3781 break;
3782 case 'commande':
3783 $trigger_name = 'ORDER_MODIFY';
3784 break;
3785 case 'facture':
3786 $trigger_name = 'BILL_MODIFY';
3787 break;
3788 case 'invoice_supplier':
3789 $trigger_name = 'BILL_SUPPLIER_MODIFY';
3790 break;
3791 case 'facturerec':
3792 $trigger_name = 'BILLREC_MODIFIY';
3793 break;
3794 case 'expensereport':
3795 $trigger_name = 'EXPENSE_REPORT_MODIFY';
3796 break;
3797 default:
3798 $trigger_name = strtoupper($this->element) . '_MODIFY';
3799 }
3800 $ret = $this->call_trigger($trigger_name, $user);
3801 if ($ret < 0) {
3802 return -1;
3803 }
3804 }
3805 return 1;
3806 } else {
3807 $this->error = $this->db->lasterror();
3808 return -1;
3809 }
3810 }
3811
3812 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3821 public function update_note_public($note)
3822 {
3823 // phpcs:enable
3824 return $this->update_note($note, '_public');
3825 }
3826
3827 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3838 public function update_price($exclspec = 0, $roundingadjust = 'auto', $nodatabaseupdate = 0, $seller = null)
3839 {
3840 // phpcs:enable
3841 global $conf, $hookmanager, $action;
3842
3843 $parameters = array('exclspec' => $exclspec, 'roundingadjust' => $roundingadjust, 'nodatabaseupdate' => $nodatabaseupdate, 'seller' => $seller);
3844 $reshook = $hookmanager->executeHooks('updateTotalPrice', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3845 if ($reshook > 0) {
3846 return 1; // replacement code
3847 } elseif ($reshook < 0) {
3848 return -1; // failure
3849 } // reshook = 0 => execute normal code
3850
3851 // Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
3852 $isElementForSupplier = false;
3853 $roundTotalConstName = 'MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND'; // const for customer by default
3854 $MODULE = "";
3855 if ($this->element == 'propal') {
3856 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
3857 } elseif ($this->element == 'commande' || $this->element == 'order') {
3858 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
3859 } elseif ($this->element == 'facture' || $this->element == 'invoice') {
3860 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
3861 } elseif ($this->element == 'facture_fourn' || $this->element == 'supplier_invoice' || $this->element == 'invoice_supplier' || $this->element == 'invoice_supplier_rec') {
3862 $isElementForSupplier = true;
3863 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
3864 } elseif ($this->element == 'order_supplier' || $this->element == 'supplier_order') {
3865 $isElementForSupplier = true;
3866 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
3867 } elseif ($this->element == 'supplier_proposal') {
3868 $isElementForSupplier = true;
3869 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
3870 }
3871 if ($isElementForSupplier) {
3872 $roundTotalConstName = 'MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND_SUPPLIER'; // const for supplier
3873 }
3874
3875 if (!empty($MODULE)) {
3876 if (getDolGlobalString($MODULE)) {
3877 $modsactivated = explode(',', getDolGlobalString($MODULE));
3878 foreach ($modsactivated as $mod) {
3879 if (isModEnabled($mod)) {
3880 return 1; // update was disabled by specific setup
3881 }
3882 }
3883 }
3884 }
3885
3886 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3887
3888 $forcedroundingmode = $roundingadjust;
3889 if ($forcedroundingmode == 'auto' && isset($conf->global->{$roundTotalConstName})) {
3890 $forcedroundingmode = getDolGlobalString($roundTotalConstName);
3891 } elseif ($forcedroundingmode == 'auto') {
3892 $forcedroundingmode = '0';
3893 }
3894
3895 $error = 0;
3896
3897 $multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
3898
3899 // Define constants to find lines to sum (field name int the table_element_line not into table_element)
3900 $fieldtva = 'total_tva';
3901 $fieldlocaltax1 = 'total_localtax1';
3902 $fieldlocaltax2 = 'total_localtax2';
3903 $fieldup = 'subprice';
3904 $base_price_type = 'HT';
3905 if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
3906 $fieldtva = 'tva';
3907 $fieldup = 'pu_ht';
3908 }
3909 if ($this->element == 'invoice_supplier_rec') {
3910 $fieldup = 'pu_ht';
3911 }
3912 if ($this->element == 'expensereport') {
3913 // Force rounding mode to '0', otherwise when you set MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND to 1, you may have lines with different totals.
3914 // For example, if you have 2 lines with same TTC amounts (6,2 Unit price TTC and VAT rate 20%), on the first line you got 5,17 on HT total
3915 // and 5,16 on HT total and 1,04 on VAT total to get 6,20 on TTT total on second line (see #30051).
3916 $forcedroundingmode = '0';
3917 $fieldup = 'value_unit';
3918 $base_price_type = 'TTC';
3919 }
3920
3921 $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,";
3922 $sql .= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
3923 if ($this->table_element_line == 'facturedet') {
3924 $sql .= ', situation_percent';
3925 }
3926 $sql .= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
3927 $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
3928 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3929 if ($exclspec) {
3930 $product_field = 'product_type';
3931 if ($this->table_element_line == 'contratdet') {
3932 $product_field = ''; // contratdet table has no product_type field
3933 }
3934 if ($product_field) {
3935 $sql .= " AND ".$product_field." <> 9";
3936 }
3937 }
3938 $sql .= ' ORDER by rowid'; // We want to be certain to always use same order of line to not change lines differently when option MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND is used
3939
3940 dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3941
3942 $resql = $this->db->query($sql);
3943 if ($resql) {
3944 $this->total_ht = 0;
3945 $this->total_tva = 0;
3946 $this->total_localtax1 = 0;
3947 $this->total_localtax2 = 0;
3948 $this->total_ttc = 0;
3949 $total_ht_by_vats = array();
3950 $total_tva_by_vats = array();
3951 $total_ttc_by_vats = array();
3952 $this->multicurrency_total_ht = 0;
3953 $this->multicurrency_total_tva = 0;
3954 $this->multicurrency_total_ttc = 0;
3955
3956 $this->db->begin();
3957
3958 $num = $this->db->num_rows($resql);
3959 $i = 0;
3960 while ($i < $num) {
3961 $obj = $this->db->fetch_object($resql);
3962
3963 // Note: There is no check on detail line and no check on total, if $forcedroundingmode = '0'
3964 $parameters = array('fk_element' => $obj->rowid);
3965 $reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3966
3967 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'
3968 // This part of code is to fix data. We should not call it too often.
3969 $localtax_array = array($obj->localtax1_type, $obj->localtax1_tx, $obj->localtax2_type, $obj->localtax2_tx);
3970 $tmpcal = calcul_price_total($obj->qty, $obj->up, $obj->remise_percent, $obj->vatrate, $obj->localtax1_tx, $obj->localtax2_tx, 0, $base_price_type, $obj->info_bits, $obj->product_type, $seller, $localtax_array, (isset($obj->situation_percent) ? $obj->situation_percent : 100), $multicurrency_tx);
3971
3972 $diff_when_using_price_ht = price2num((float) $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.
3973 $diff_on_current_total = price2num($obj->total_ttc - $obj->total_ht - $obj->total_tva - $obj->total_localtax1 - $obj->total_localtax2, 'MT', 1);
3974 //var_dump($obj->total_ht.' '.$obj->total_tva.' '.$obj->total_localtax1.' '.$obj->total_localtax2.' => '.$obj->total_ttc);
3975 //var_dump($diff_when_using_price_ht.' '.$diff_on_current_total);
3976
3977 if ($diff_on_current_total) {
3978 // This should not happen, we should always have in table: total_ttc = total_ht + total_vat + total_localtax1 + total_localtax2
3979 $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line;
3980 $sqlfix .= " SET ".$fieldtva." = ".price2num((float) $tmpcal[1]).", total_ttc = ".price2num((float) $tmpcal[2]);
3981 $sqlfix .= ", multicurrency_total_tva = ".price2num((float) $tmpcal[17]).", multicurrency_total_ttc = ".price2num((float) $tmpcal[18]);
3982 $sqlfix .= " WHERE rowid = ".((int) $obj->rowid);
3983 dol_syslog('Warn1: We found inconsistent 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);
3984 $resqlfix = $this->db->query($sqlfix);
3985 if (!$resqlfix) {
3986 dol_print_error($this->db, 'Failed to update line');
3987 }
3988 $obj->total_tva = $tmpcal[1];
3989 $obj->total_ttc = $tmpcal[2];
3990 $obj->multicurrency_total_tva = $tmpcal[17];
3991 $obj->multicurrency_total_ttc = $tmpcal[18];
3992 } elseif ($diff_when_using_price_ht) {
3993 // If total_ht calculated from unit price is different than the one in database, we do nothing, this may be a regular case to have also a different VAT, that can be explained
3994 // because price was entered included tax and we round the unit price without tax to store it in database (so recalculation will give different results).
3995 if ((float) $tmpcal[0] == (float) $obj->total_ht) {
3996 // After calculation from HT, total is consistent and total_ht is same, but we have found a difference between VAT part calculated from unit price and the VAT part into database,
3997 // and we ask to force the use of rounding on line (like done on calculation) so this should not happen, so we force the update of line to fix.
3998
3999 // This part of code must be called only to fix corrupted data due to the use of the feature to round total instead of rounding lines.
4000 $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line;
4001 $sqlfix .= " SET ".$fieldtva." = ".price2num((float) $tmpcal[1]).", total_ttc = ".price2num((float) $tmpcal[2]);
4002 $sqlfix .= ", multicurrency_total_tva = ".price2num((float) $tmpcal[17]).", multicurrency_total_ttc = ".price2num((float) $tmpcal[18]);
4003 $sqlfix .= " WHERE rowid = ".((int) $obj->rowid);
4004 dol_syslog('Warn2: 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);
4005 $resqlfix = $this->db->query($sqlfix);
4006 if (!$resqlfix) {
4007 dol_print_error($this->db, 'Failed to update line');
4008 }
4009 $obj->total_tva = $tmpcal[1];
4010 $obj->total_ttc = $tmpcal[2];
4011 $obj->multicurrency_total_tva = $tmpcal[17];
4012 $obj->multicurrency_total_ttc = $tmpcal[18];
4013 }
4014 }
4015 }
4016
4017 $this->total_ht += $obj->total_ht; // The field visible at end of line detail
4018 $this->total_tva += $obj->total_tva;
4019 $this->total_localtax1 += $obj->total_localtax1;
4020 $this->total_localtax2 += $obj->total_localtax2;
4021 $this->total_ttc += $obj->total_ttc;
4022 $this->multicurrency_total_ht += $obj->multicurrency_total_ht; // The field visible at end of line detail
4023 $this->multicurrency_total_tva += $obj->multicurrency_total_tva;
4024 $this->multicurrency_total_ttc += $obj->multicurrency_total_ttc;
4025
4026 if (!isset($total_ht_by_vats[$obj->vatrate])) {
4027 $total_ht_by_vats[$obj->vatrate] = 0;
4028 }
4029 if (!isset($total_tva_by_vats[$obj->vatrate])) {
4030 $total_tva_by_vats[$obj->vatrate] = 0;
4031 }
4032 if (!isset($total_ttc_by_vats[$obj->vatrate])) {
4033 $total_ttc_by_vats[$obj->vatrate] = 0;
4034 }
4035 $total_ht_by_vats[$obj->vatrate] += $obj->total_ht;
4036 $total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
4037 $total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
4038
4039 if ($forcedroundingmode == '1') { // Check if we need adjustment onto line for vat. TODO This works on the company currency but not on foreign currency
4040 if ($base_price_type == 'TTC') {
4041 $tmpvat = price2num($total_ttc_by_vats[$obj->vatrate] * $obj->vatrate / (100 + $obj->vatrate), 'MT', 1);
4042 } else {
4043 $tmpvat = price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
4044 }
4045 $diff = price2num($total_tva_by_vats[$obj->vatrate] - (float) $tmpvat, 'MT', 1);
4046 //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";
4047 if ($diff) {
4048 $maxdiff = (10 * pow(10, -1 * getDolGlobalInt('MAIN_MAX_DECIMALS_TOT', 0)));
4049 if (abs((float) $diff) > $maxdiff) {
4050 // If error is more than 10 times the accuracy of rounding. This should not happen.
4051 $errmsg = 'We found a rounding difference after line '.($obj->rowid).' between HT*VAT='.$tmpvat.' and total in database='.$total_tva_by_vats[$obj->vatrate].' (calculated with UP*qty) but diff='.$diff.' is too high (> '.$maxdiff.') to be corrected. Some data in your lines may be corrupted. Try to edit each line manually to fix this before restarting.';
4052 dol_syslog($errmsg, LOG_WARNING);
4053 $this->error = $errmsg;
4054 $error++;
4055 break;
4056 }
4057
4058 if ($base_price_type == 'TTC') {
4059 $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldtva." = ".price2num($obj->total_tva - (float) $diff).", total_ht = ".price2num($obj->total_ht + (float) $diff)." WHERE rowid = ".((int) $obj->rowid);
4060 dol_syslog('We found a difference of '.$diff.' for line rowid = '.$obj->rowid.' between TotalHT('.$total_ht_by_vats[$obj->vatrate].')*VATrate('.$obj->vatrate.')='.$tmpvat.' and total in database='.$total_tva_by_vats[$obj->vatrate]." (calculated with UP*qty). We fix the total_vat and total_ht of line by running sqlfix = ".$sqlfix);
4061 } else {
4062 $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldtva." = ".price2num($obj->total_tva - (float) $diff).", total_ttc = ".price2num($obj->total_ttc - (float) $diff)." WHERE rowid = ".((int) $obj->rowid);
4063 dol_syslog('We found a difference of '.$diff.' for line rowid = '.$obj->rowid.' between TotalHT('.$total_ht_by_vats[$obj->vatrate].')*VATrate('.$obj->vatrate.')='.$tmpvat.' and total in database='.$total_tva_by_vats[$obj->vatrate]." (calculated with UP*qty). We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix);
4064 }
4065
4066 $resqlfix = $this->db->query($sqlfix);
4067
4068 if (!$resqlfix) {
4069 dol_print_error($this->db, 'Failed to update line');
4070 }
4071
4072 $this->total_tva = (float) price2num($this->total_tva - (float) $diff, '', 1);
4073 $total_tva_by_vats[$obj->vatrate] = (float) price2num($total_tva_by_vats[$obj->vatrate] - (float) $diff, '', 1);
4074 if ($base_price_type == 'TTC') {
4075 $this->total_ht = (float) price2num($this->total_ht + (float) $diff, '', 1);
4076 $total_ht_by_vats[$obj->vatrate] = (float) price2num($total_ht_by_vats[$obj->vatrate] + (float) $diff, '', 1);
4077 } else {
4078 $this->total_ttc = (float) price2num($this->total_ttc - (float) $diff, '', 1);
4079 $total_ttc_by_vats[$obj->vatrate] = (float) price2num($total_ttc_by_vats[$obj->vatrate] - (float) $diff, '', 1);
4080 }
4081 }
4082 }
4083
4084 $i++;
4085 }
4086
4087 // Add revenue stamp to total
4088 // @phan-suppress-next-line PhanUndeclaredProperty
4089 $this->total_ttc += isset($this->revenuestamp) ? $this->revenuestamp : 0;
4090 // @phan-suppress-next-line PhanUndeclaredProperty
4091 $this->multicurrency_total_ttc += isset($this->revenuestamp) ? ($this->revenuestamp * $multicurrency_tx) : 0;
4092
4093 // Situations totals
4094 if (!empty($this->situation_cycle_ref) && !empty($this->situation_counter) && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits')) { // @phan-suppress-current-line PhanUndeclaredProperty
4095 '@phan-var-force Facture $this';
4096 include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; // Note: possibly useless as $this is normally already Facture, so the class file should be loaded
4097 if ($this->type != Facture::TYPE_CREDIT_NOTE) { // @phpstan-ignore-line
4098 if (getDolGlobalInt('INVOICE_USE_SITUATION') != 2) {
4099 $prev_sits = $this->get_prev_sits();
4100
4101 foreach ($prev_sits as $sit) { // $sit is an object Facture loaded with a fetch.
4102 $this->total_ht -= $sit->total_ht;
4103 $this->total_tva -= $sit->total_tva;
4104 $this->total_localtax1 -= $sit->total_localtax1;
4105 $this->total_localtax2 -= $sit->total_localtax2;
4106 $this->total_ttc -= $sit->total_ttc;
4107 $this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
4108 $this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
4109 $this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
4110 }
4111 }
4112 }
4113 }
4114
4115 // Clean total
4116 $this->total_ht = (float) price2num($this->total_ht);
4117 $this->total_tva = (float) price2num($this->total_tva);
4118 $this->total_localtax1 = (float) price2num($this->total_localtax1);
4119 $this->total_localtax2 = (float) price2num($this->total_localtax2);
4120 $this->total_ttc = (float) price2num($this->total_ttc);
4121
4122 $this->db->free($resql);
4123
4124 // Now update global fields total_ht, total_ttc, total_tva, total_localtax1, total_localtax2, multicurrency_total_* of main object
4125 $fieldht = 'total_ht';
4126 $fieldtva = 'tva';
4127 $fieldlocaltax1 = 'localtax1';
4128 $fieldlocaltax2 = 'localtax2';
4129 $fieldttc = 'total_ttc';
4130 // Specific code for backward compatibility with old field names
4131 if (in_array($this->element, array('propal', 'commande', 'facture', 'facturerec', 'supplier_proposal', 'order_supplier', 'facture_fourn', 'invoice_supplier', 'invoice_supplier_rec', 'expensereport'))) {
4132 $fieldtva = 'total_tva';
4133 }
4134
4135 if (!$error && empty($nodatabaseupdate)) {
4136 $sql = "UPDATE ".$this->db->prefix().$this->table_element.' SET';
4137 $sql .= " ".$this->db->sanitize($fieldht)." = ".((float) price2num($this->total_ht, 'MT', 1)).",";
4138 $sql .= " ".$this->db->sanitize($fieldtva)." = ".((float) price2num($this->total_tva, 'MT', 1)).",";
4139 $sql .= " ".$this->db->sanitize($fieldlocaltax1)." = ".((float) price2num($this->total_localtax1, 'MT', 1)).",";
4140 $sql .= " ".$this->db->sanitize($fieldlocaltax2)." = ".((float) price2num($this->total_localtax2, 'MT', 1)).",";
4141 $sql .= " ".$this->db->sanitize($fieldttc)." = ".((float) price2num($this->total_ttc, 'MT', 1));
4142 $sql .= ", multicurrency_total_ht = ".((float) price2num($this->multicurrency_total_ht, 'MT', 1));
4143 $sql .= ", multicurrency_total_tva = ".((float) price2num($this->multicurrency_total_tva, 'MT', 1));
4144 $sql .= ", multicurrency_total_ttc = ".((float) price2num($this->multicurrency_total_ttc, 'MT', 1));
4145 $sql .= " WHERE rowid = ".((int) $this->id);
4146
4147 dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
4148 $resql = $this->db->query($sql);
4149
4150 if (!$resql) {
4151 $error++;
4152 $this->error = $this->db->lasterror();
4153 $this->errors[] = $this->db->lasterror();
4154 }
4155 }
4156
4157 if (!$error) {
4158 $this->db->commit();
4159 return 1;
4160 } else {
4161 $this->db->rollback();
4162 return -1;
4163 }
4164 } else {
4165 dol_print_error($this->db, 'Bad request in update_price');
4166 return -1;
4167 }
4168 }
4169
4170 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4181 public function add_object_linked($origin = null, $origin_id = null, $f_user = null, $notrigger = 0)
4182 {
4183 // phpcs:enable
4184 global $user, $hookmanager, $action;
4185 $origin = (!empty($origin) ? $origin : $this->origin);
4186 $origin_id = (!empty($origin_id) ? $origin_id : $this->origin_id);
4187 $f_user = isset($f_user) ? $f_user : $user;
4188
4189 // Special case
4190 if ($origin == 'order') {
4191 $origin = 'commande';
4192 }
4193 if ($origin == 'invoice') {
4194 $origin = 'facture';
4195 }
4196 if ($origin == 'invoice_template') {
4197 $origin = 'facturerec';
4198 }
4199 if ($origin == 'supplierorder') {
4200 $origin = 'order_supplier';
4201 }
4202
4203 // Add module part to target type
4204 $targettype = $this->getElementType();
4205
4206 $parameters = array('targettype' => $targettype);
4207 // Hook for explicitly set the targettype if it must be different than $this->element
4208 $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4209 if ($reshook > 0) {
4210 if (!empty($hookmanager->resArray['targettype'])) {
4211 $targettype = $hookmanager->resArray['targettype'];
4212 }
4213 }
4214
4215 $this->db->begin();
4216 $error = 0;
4217
4218 $sql = "INSERT INTO " . $this->db->prefix() . "element_element (";
4219 $sql .= "fk_source";
4220 $sql .= ", sourcetype";
4221 $sql .= ", fk_target";
4222 $sql .= ", targettype";
4223 $sql .= ") VALUES (";
4224 $sql .= ((int) $origin_id);
4225 $sql .= ", '" . $this->db->escape($origin) . "'";
4226 $sql .= ", " . ((int) $this->id);
4227 $sql .= ", '" . $this->db->escape($targettype) . "'";
4228 $sql .= ")";
4229
4230 dol_syslog(get_class($this) . "::add_object_linked", LOG_DEBUG);
4231 if ($this->db->query($sql)) {
4232 if (!$notrigger) {
4233 // Call trigger
4234 $this->context['link_origin'] = $origin;
4235 $this->context['link_origin_id'] = $origin_id;
4236 $result = $this->call_trigger('OBJECT_LINK_INSERT', $f_user);
4237 if ($result < 0) {
4238 $error++;
4239 }
4240 // End call triggers
4241 }
4242 } else {
4243 $this->error = $this->db->lasterror();
4244 $error++;
4245 }
4246
4247 if (!$error) {
4248 $this->db->commit();
4249 return 1;
4250 } else {
4251 $this->db->rollback();
4252 return 0;
4253 }
4254 }
4255
4261 public function getElementType()
4262 {
4263 // Elements of the core modules having a `$module` property but for which we may not want to prefix the element name with the module name for finding the linked object in llx_element_element.
4264 // It's because existing llx_element_element entries inserted prior to this modification (version <=14.2) may already use the element name alone in fk_source or fk_target (without the module name prefix).
4265 $coreModule = array('knowledgemanagement', 'partnership', 'workstation', 'ticket', 'recruitment', 'eventorganization', 'asset');
4266 // Add module part to target type if object has $module property and isn't in core modules.
4267 return ((!empty($this->module) && !in_array($this->module, $coreModule)) ? $this->module.'_' : '').$this->element;
4268 }
4269
4270
4293 public function fetchObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1)
4294 {
4295 global $hookmanager, $action;
4296
4297 // Important for pdf generation time reduction
4298 // This boolean is true if $this->linkedObjects has already been loaded with all objects linked without filter
4299 // If you need to force the reload, you can call clearObjectLinkedCache() before calling fetchObjectLinked()
4300 if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
4301 return 1;
4302 }
4303
4304 $this->linkedObjectsIds = array();
4305 $this->linkedObjects = array();
4306
4307 $justsource = false;
4308 $justtarget = false;
4309 $withtargettype = false;
4310 $withsourcetype = false;
4311
4312 $parameters = array('sourcetype' => $sourcetype, 'sourceid' => $sourceid, 'targettype' => $targettype, 'targetid' => $targetid);
4313 // Hook for explicitly set the targettype if it must be differtent than $this->element
4314 $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4315 if ($reshook > 0) {
4316 if (!empty($hookmanager->resArray['sourcetype'])) {
4317 $sourcetype = $hookmanager->resArray['sourcetype'];
4318 }
4319 if (!empty($hookmanager->resArray['sourceid'])) {
4320 $sourceid = $hookmanager->resArray['sourceid'];
4321 }
4322 if (!empty($hookmanager->resArray['targettype'])) {
4323 $targettype = $hookmanager->resArray['targettype'];
4324 }
4325 if (!empty($hookmanager->resArray['targetid'])) {
4326 $targetid = $hookmanager->resArray['targetid'];
4327 }
4328 }
4329
4330 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid)) {
4331 $justsource = true; // the source (id and type) is a search criteria
4332 if (!empty($targettype)) {
4333 $withtargettype = true;
4334 }
4335 }
4336 if (!empty($targetid) && !empty($targettype) && empty($sourceid)) {
4337 $justtarget = true; // the target (id and type) is a search criteria
4338 if (!empty($sourcetype)) {
4339 $withsourcetype = true;
4340 }
4341 }
4342
4343 $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
4344 $targetid = (!empty($targetid) ? $targetid : $this->id);
4345 $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->getElementType());
4346 $targettype = (!empty($targettype) ? $targettype : $this->getElementType());
4347
4348 /*if (empty($sourceid) && empty($targetid))
4349 {
4350 dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
4351 return -1;
4352 }*/
4353
4354 // Links between objects are stored in table element_element
4355 $sql = "SELECT rowid, fk_source, sourcetype, fk_target, targettype";
4356 $sql .= " FROM ".$this->db->prefix()."element_element";
4357 $sql .= " WHERE ";
4358 if ($justsource || $justtarget) {
4359 if ($justsource) {
4360 $sql .= "fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."'";
4361 if ($withtargettype) {
4362 $sql .= " AND targettype = '".$this->db->escape($targettype)."'";
4363 }
4364 } elseif ($justtarget) {
4365 $sql .= "fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."'";
4366 if ($withsourcetype) {
4367 $sql .= " AND sourcetype = '".$this->db->escape($sourcetype)."'";
4368 }
4369 }
4370 } else {
4371 $sql .= "(fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."')";
4372 $sql .= " ".$clause." (fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."')";
4373 if ($loadalsoobjects && $this->id > 0 && $sourceid == $this->id && $sourcetype == $this->element && $targetid == $this->id && $targettype == $this->element && $clause == 'OR') {
4374 $this->linkedObjectsFullLoaded[$this->id] = true;
4375 }
4376 }
4377 $sql .= " ORDER BY ".$orderby;
4378
4379 dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
4380 $resql = $this->db->query($sql);
4381 if ($resql) {
4382 $num = $this->db->num_rows($resql);
4383 $i = 0;
4384 while ($i < $num) {
4385 $obj = $this->db->fetch_object($resql);
4386 if ($justsource || $justtarget) {
4387 if ($justsource) {
4388 $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
4389 } elseif ($justtarget) {
4390 $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
4391 }
4392 } else {
4393 if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype) {
4394 $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
4395 }
4396 if ($obj->fk_target == $targetid && $obj->targettype == $targettype) {
4397 $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
4398 }
4399 }
4400 $i++;
4401 }
4402
4403 if (!empty($this->linkedObjectsIds)) {
4404 $tmparray = $this->linkedObjectsIds;
4405 foreach ($tmparray as $objecttype => $objectids) { // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
4406 $element_properties = getElementProperties($objecttype);
4407 $element = $element_properties['element'];
4408 $classPath = $element_properties['classpath'];
4409 $classFile = $element_properties['classfile'];
4410 $className = $element_properties['classname'];
4411 $module = $element_properties['module'];
4412
4413 // Here $module, $classFile and $className are set, we can use them.
4414 if (isModEnabled($module) && (($element != $this->element) || $alsosametype)) {
4415 if ($loadalsoobjects && (is_numeric($loadalsoobjects) || ($loadalsoobjects === $objecttype))) {
4416 dol_include_once('/'.$classPath.'/'.$classFile.'.class.php');
4417 if (class_exists($className)) {
4418 foreach ($objectids as $i => $objectid) { // $i is rowid into llx_element_element
4419 $object = new $className($this->db);
4420 '@phan-var-force CommonObject $object';
4421 $ret = $object->fetch($objectid);
4422 if ($ret >= 0) {
4423 $this->linkedObjects[$objecttype][$i] = $object;
4424 }
4425 }
4426 }
4427 }
4428 } else {
4429 unset($this->linkedObjectsIds[$objecttype]);
4430 }
4431 }
4432 }
4433 return 1;
4434 } else {
4435 dol_print_error($this->db);
4436 return -1;
4437 }
4438 }
4439
4446 public function clearObjectLinkedCache()
4447 {
4448 if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
4449 unset($this->linkedObjectsFullLoaded[$this->id]);
4450 }
4451
4452 return 1;
4453 }
4454
4467 public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $f_user = null, $notrigger = 0)
4468 {
4469 global $user;
4470 $updatesource = false;
4471 $updatetarget = false;
4472 $f_user = isset($f_user) ? $f_user : $user;
4473
4474 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4475 $updatesource = true;
4476 } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4477 $updatetarget = true;
4478 }
4479
4480 $this->db->begin();
4481 $error = 0;
4482
4483 $sql = "UPDATE " . $this->db->prefix() . "element_element SET ";
4484 if ($updatesource) {
4485 $sql .= "fk_source = " . ((int) $sourceid);
4486 $sql .= ", sourcetype = '" . $this->db->escape($sourcetype) . "'";
4487 $sql .= " WHERE fk_target = " . ((int) $this->id);
4488 $sql .= " AND targettype = '" . $this->db->escape($this->element) . "'";
4489 } elseif ($updatetarget) {
4490 $sql .= "fk_target = " . ((int) $targetid);
4491 $sql .= ", targettype = '" . $this->db->escape($targettype) . "'";
4492 $sql .= " WHERE fk_source = " . ((int) $this->id);
4493 $sql .= " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4494 }
4495
4496 dol_syslog(get_class($this) . "::updateObjectLinked", LOG_DEBUG);
4497 if ($this->db->query($sql)) {
4498 if (!$notrigger) {
4499 // Call trigger
4500 $this->context['link_source_id'] = $sourceid;
4501 $this->context['link_source_type'] = $sourcetype;
4502 $this->context['link_target_id'] = $targetid;
4503 $this->context['link_target_type'] = $targettype;
4504 $result = $this->call_trigger('OBJECT_LINK_MODIFY', $f_user);
4505 if ($result < 0) {
4506 $error++;
4507 }
4508 // End call triggers
4509 }
4510 } else {
4511 $this->error = $this->db->lasterror();
4512 $error++;
4513 }
4514
4515 if (!$error) {
4516 $this->db->commit();
4517 return 1;
4518 } else {
4519 $this->db->rollback();
4520 return -1;
4521 }
4522 }
4523
4537 public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = 0, $f_user = null, $notrigger = 0)
4538 {
4539 global $user;
4540 $deletesource = false;
4541 $deletetarget = false;
4542 $f_user = isset($f_user) ? $f_user : $user;
4543
4544 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4545 $deletesource = true;
4546 } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4547 $deletetarget = true;
4548 }
4549
4550 $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
4551 $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
4552 $targetid = (!empty($targetid) ? $targetid : $this->id);
4553 $targettype = (!empty($targettype) ? $targettype : $this->element);
4554 $this->db->begin();
4555 $error = 0;
4556
4557 if (!$notrigger) {
4558 // Call trigger
4559 $this->context['link_id'] = $rowid;
4560 $this->context['link_source_id'] = $sourceid;
4561 $this->context['link_source_type'] = $sourcetype;
4562 $this->context['link_target_id'] = $targetid;
4563 $this->context['link_target_type'] = $targettype;
4564 $result = $this->call_trigger('OBJECT_LINK_DELETE', $f_user);
4565 if ($result < 0) {
4566 $error++;
4567 }
4568 // End call triggers
4569 }
4570
4571 if (!$error) {
4572 $sql = "DELETE FROM " . $this->db->prefix() . "element_element";
4573 $sql .= " WHERE";
4574 if ($rowid > 0) {
4575 $sql .= " rowid = " . ((int) $rowid);
4576 } else {
4577 if ($deletesource) {
4578 $sql .= " fk_source = " . ((int) $sourceid) . " AND sourcetype = '" . $this->db->escape($sourcetype) . "'";
4579 $sql .= " AND fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "'";
4580 } elseif ($deletetarget) {
4581 $sql .= " fk_target = " . ((int) $targetid) . " AND targettype = '" . $this->db->escape($targettype) . "'";
4582 $sql .= " AND fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4583 } else {
4584 $sql .= " (fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "')";
4585 $sql .= " OR";
4586 $sql .= " (fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "')";
4587 }
4588 }
4589
4590 dol_syslog(get_class($this) . "::deleteObjectLinked", LOG_DEBUG);
4591 if (!$this->db->query($sql)) {
4592 $this->error = $this->db->lasterror();
4593 $this->errors[] = $this->error;
4594 $error++;
4595 }
4596 }
4597
4598 if (!$error) {
4599 $this->db->commit();
4600 return 1;
4601 } else {
4602 $this->db->rollback();
4603 return 0;
4604 }
4605 }
4606
4616 public static function getAllItemsLinkedByObjectID($fk_object_where, $field_select, $field_where, $table_element)
4617 {
4618 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4619 return -1;
4620 }
4621 if (!preg_match('/^[_a-zA-Z0-9]+$/', $field_select)) {
4622 dol_syslog('Invalid value $field_select for parameter '.$field_select.' in call to getAllItemsLinkedByObjectID(). Must be a single field name.', LOG_ERR);
4623 }
4624
4625 global $db;
4626
4627 $sql = "SELECT ".$field_select." FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4628 $resql = $db->query($sql);
4629
4630 $TRes = array();
4631 if (!empty($resql)) {
4632 while ($res = $db->fetch_object($resql)) {
4633 $TRes[] = $res->{$field_select};
4634 }
4635 }
4636
4637 return $TRes;
4638 }
4639
4648 public static function getCountOfItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
4649 {
4650 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4651 return -1;
4652 }
4653
4654 global $db;
4655
4656 $sql = "SELECT COUNT(*) as nb FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4657 $resql = $db->query($sql);
4658 $n = 0;
4659 if ($resql) {
4660 $res = $db->fetch_object($resql);
4661 if ($res) {
4662 $n = $res->nb;
4663 }
4664 }
4665
4666 return $n;
4667 }
4668
4677 public static function deleteAllItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
4678 {
4679 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4680 return -1;
4681 }
4682
4683 global $db;
4684
4685 $sql = "DELETE FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4686 $resql = $db->query($sql);
4687
4688 if (empty($resql)) {
4689 return 0;
4690 }
4691
4692 return 1;
4693 }
4694
4705 public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '', $fieldstatus = 'fk_statut')
4706 {
4707 global $user;
4708
4709 $savElementId = $elementId; // To be used later to know if we were using the method using the id of this or not.
4710
4711 $elementId = (!empty($elementId) ? $elementId : $this->id);
4712 $elementTable = (!empty($elementType) ? $elementType : $this->table_element);
4713
4714 $this->db->begin();
4715
4716 if ($elementTable == 'facture_rec') {
4717 $fieldstatus = "suspended";
4718 }
4719 if ($elementTable == 'mailing') {
4720 $fieldstatus = "statut";
4721 }
4722 if ($elementTable == 'cronjob') {
4723 $fieldstatus = "status";
4724 }
4725 if ($elementTable == 'user') {
4726 $fieldstatus = "statut";
4727 }
4728 if ($elementTable == 'expensereport') {
4729 $fieldstatus = "fk_statut";
4730 }
4731 if ($elementTable == 'receptiondet_batch') {
4732 $fieldstatus = "status";
4733 }
4734 if ($elementTable == 'prelevement_bons') {
4735 $fieldstatus = "statut";
4736 }
4737 if (isset($this->fields) && is_array($this->fields) && array_key_exists('status', $this->fields)) {
4738 $fieldstatus = 'status';
4739 }
4740
4741 $sql = "UPDATE ".$this->db->prefix().$this->db->sanitize($elementTable);
4742 $sql .= " SET ".$this->db->sanitize($fieldstatus)." = ".((int) $status);
4743 // If status = 1 = validated, update also fk_user_valid
4744 // TODO Replace the test on $elementTable by doing a test on existence of the field in $this->fields
4745 if ($status == 1 && in_array($elementTable, array('expensereport', 'inventory'))) {
4746 $sql .= ", fk_user_valid = ".((int) $user->id);
4747 }
4748 if ($status == 1 && in_array($elementTable, array('expensereport'))) {
4749 $sql .= ", date_valid = '".$this->db->idate(dol_now())."'";
4750 }
4751 if ($status == 1 && in_array($elementTable, array('inventory'))) {
4752 $sql .= ", date_validation = '".$this->db->idate(dol_now())."'";
4753 }
4754 $sql .= " WHERE rowid = ".((int) $elementId);
4755 $sql .= " AND ".$fieldstatus." <> ".((int) $status); // We avoid update if status already correct
4756
4757 dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
4758 $resql = $this->db->query($sql);
4759 if ($resql) {
4760 $error = 0;
4761
4762 $nb_rows_affected = $this->db->affected_rows($resql); // should be 1 or 0 if status was already correct
4763
4764 if ($nb_rows_affected > 0) {
4765 if (empty($trigkey)) {
4766 // Try to guess trigkey (for backward compatibility, now we should have trigkey defined into the call of setStatus)
4767 if ($this->element == 'supplier_proposal' && $status == 2) {
4768 $trigkey = 'SUPPLIER_PROPOSAL_SIGN'; // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
4769 }
4770 if ($this->element == 'supplier_proposal' && $status == 3) {
4771 $trigkey = 'SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
4772 }
4773 if ($this->element == 'supplier_proposal' && $status == 4) {
4774 $trigkey = 'SUPPLIER_PROPOSAL_CLOSE'; // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
4775 }
4776 if ($this->element == 'fichinter' && $status == 3) {
4777 $trigkey = 'FICHINTER_CLASSIFY_DONE';
4778 }
4779 if ($this->element == 'fichinter' && $status == 2) {
4780 $trigkey = 'FICHINTER_CLASSIFY_BILLED';
4781 }
4782 if ($this->element == 'fichinter' && $status == 1) {
4783 $trigkey = 'FICHINTER_CLASSIFY_UNBILLED';
4784 }
4785 }
4786
4787 $this->context = array_merge($this->context, array('newstatus' => $status));
4788
4789 if ($trigkey) {
4790 $this->oldcopy = dol_clone($this);
4791
4792 // Call trigger
4793 $result = $this->call_trigger($trigkey, $user);
4794 if ($result < 0) {
4795 $error++;
4796 }
4797 // End call triggers
4798 }
4799 } else {
4800 // The status was probably already good. We do nothing more, no triggers.
4801 }
4802
4803 if (!$error) {
4804 $this->db->commit();
4805
4806 if (empty($savElementId)) {
4807 // If the element we update is $this (so $elementId was provided as null)
4808 if ($fieldstatus == 'tosell') {
4809 $this->status = $status;
4810 } elseif ($fieldstatus == 'tobuy') {
4811 $this->status_buy = $status; // @phpstan-ignore-line
4812 } else {
4813 $this->status = $status;
4814 }
4815 }
4816
4817 return 1;
4818 } else {
4819 $this->db->rollback();
4820 dol_syslog(get_class($this)."::setStatut ".$this->error, LOG_ERR);
4821 return -1;
4822 }
4823 } else {
4824 $this->error = $this->db->lasterror();
4825 $this->db->rollback();
4826 return -1;
4827 }
4828 }
4829
4830
4838 public function getCanvas($id = 0, $ref = '')
4839 {
4840 global $conf;
4841
4842 if (empty($id) && empty($ref)) {
4843 return 0;
4844 }
4845 if (getDolGlobalString('MAIN_DISABLE_CANVAS')) {
4846 return 0; // To increase speed. Not enabled by default.
4847 }
4848
4849 // Clean parameters
4850 $ref = trim($ref);
4851
4852 $sql = "SELECT rowid, canvas";
4853 $sql .= " FROM ".$this->db->prefix().$this->table_element;
4854 $sql .= " WHERE entity IN (".getEntity($this->element).")";
4855 if (!empty($id)) {
4856 $sql .= " AND rowid = ".((int) $id);
4857 }
4858 if (!empty($ref)) {
4859 $sql .= " AND ref = '".$this->db->escape($ref)."'";
4860 }
4861
4862 $resql = $this->db->query($sql);
4863 if ($resql) {
4864 $obj = $this->db->fetch_object($resql);
4865 if ($obj) {
4866 $this->canvas = $obj->canvas;
4867 return 1;
4868 } else {
4869 return 0;
4870 }
4871 } else {
4872 dol_print_error($this->db);
4873 return -1;
4874 }
4875 }
4876
4877
4884 public function getSpecialCode($lineid)
4885 {
4886 $sql = "SELECT special_code FROM ".$this->db->prefix().$this->table_element_line;
4887 $sql .= " WHERE rowid = ".((int) $lineid);
4888 $resql = $this->db->query($sql);
4889 if ($resql) {
4890 $row = $this->db->fetch_row($resql);
4891 return (!empty($row[0]) ? $row[0] : 0);
4892 }
4893
4894 return 0;
4895 }
4896
4905 public function isObjectUsed($id = 0, $entity = 0)
4906 {
4907 global $langs;
4908
4909 if (empty($id)) {
4910 $id = $this->id;
4911 }
4912
4913 // Check parameters
4914 if (!isset($this->childtables) || !is_array($this->childtables) || count($this->childtables) == 0) {
4915 dol_print_error(null, 'Called isObjectUsed on a class with property this->childtables not defined');
4916 return -1;
4917 }
4918
4919 $arraytoscan = $this->childtables; // array('tablename'=>array('fk_element'=>'parentfield'), ...) or array('tablename'=>array('parent'=>table_parent, 'parentkey'=>'nameoffieldforparentfkkey'), ...)
4920 // For backward compatibility, we check if array is old format array('tablename1', 'tablename2', ...)
4921 $tmparray = array_keys($this->childtables);
4922 if (is_numeric($tmparray[0])) {
4923 $arraytoscan = array_flip($this->childtables);
4924 }
4925
4926 // Test if child exists
4927 $haschild = 0;
4928 foreach ($arraytoscan as $table => $element) {
4929 //print $id.'-'.$table.'-'.$elementname.'<br>';
4930
4931 // Check if module is enabled (to avoid error if tables of module not created)
4932 if (isset($element['enabled']) && !empty($element['enabled'])) {
4933 $enabled = (int) dol_eval($element['enabled'], 1);
4934 if (empty($enabled)) {
4935 continue;
4936 }
4937 }
4938
4939 // Check if element can be deleted
4940 $sql = "SELECT COUNT(*) as nb";
4941 $sql .= " FROM ".$this->db->prefix().$table." as c";
4942 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4943 $sql .= ", ".$this->db->prefix().$element['parent']." as p";
4944 }
4945 if (!empty($element['fk_element'])) {
4946 $sql .= " WHERE c.".$element['fk_element']." = ".((int) $id);
4947 } else {
4948 $sql .= " WHERE c.".$this->fk_element." = ".((int) $id);
4949 }
4950 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4951 $sql .= " AND c.".$element['parentkey']." = p.rowid";
4952 }
4953 if (!empty($element['parent']) && !empty($element['parenttypefield']) && !empty($element['parenttypevalue'])) {
4954 $sql .= " AND c.".$element['parenttypefield']." = '".$this->db->escape($element['parenttypevalue'])."'";
4955 }
4956 if (!empty($entity)) {
4957 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4958 $sql .= " AND p.entity = ".((int) $entity);
4959 } else {
4960 $sql .= " AND c.entity = ".((int) $entity);
4961 }
4962 }
4963
4964 $resql = $this->db->query($sql);
4965 if ($resql) {
4966 $obj = $this->db->fetch_object($resql);
4967 if ($obj->nb > 0) {
4968 $langs->load("errors");
4969 //print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
4970 $haschild += $obj->nb;
4971 if (is_numeric($element)) { // very old usage array('table1', 'table2', ...)
4972 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $table);
4973 } elseif (is_string($element)) { // old usage array('table1' => 'TranslateKey1', 'table2' => 'TranslateKey2', ...)
4974 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element));
4975 } else { // new usage: $element['name']=Translation key
4976 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element['name']));
4977 }
4978 break; // We found at least one, we stop here
4979 }
4980 } else {
4981 $this->errors[] = $this->db->lasterror();
4982 return -1;
4983 }
4984 }
4985 if ($haschild > 0) {
4986 $this->errors[] = "ErrorRecordHasChildren";
4987 return $haschild;
4988 } else {
4989 return 0;
4990 }
4991 }
4992
4999 public function hasProductsOrServices($predefined = -1)
5000 {
5001 $nb = 0;
5002
5003 foreach ($this->lines as $key => $val) {
5004 $qualified = 0;
5005 if ($predefined == -1) {
5006 $qualified = 1;
5007 }
5008 if ($predefined == 1 && $val->fk_product > 0) {
5009 $qualified = 1;
5010 }
5011 if ($predefined == 0 && $val->fk_product <= 0) {
5012 $qualified = 1;
5013 }
5014 if ($predefined == 2 && $val->fk_product > 0 && $val->product_type == 0) {
5015 $qualified = 1;
5016 }
5017 if ($predefined == 3 && $val->fk_product > 0 && $val->product_type == 1) {
5018 $qualified = 1;
5019 }
5020 if ($qualified) {
5021 $nb++;
5022 }
5023 }
5024 dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
5025 return $nb;
5026 }
5027
5033 public function getTotalDiscount()
5034 {
5035 if (!empty($this->table_element_line) && ($this->table_element_line != 'expeditiondet')) {
5036 $total_discount = 0.00;
5037
5038 $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
5039 $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
5040 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
5041
5042 dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
5043 $resql = $this->db->query($sql);
5044 if ($resql) {
5045 $num = $this->db->num_rows($resql);
5046 $i = 0;
5047 while ($i < $num) {
5048 $obj = $this->db->fetch_object($resql);
5049
5050 $pu_ht = $obj->pu_ht;
5051 $qty = $obj->qty;
5052 $total_ht = $obj->total_ht;
5053
5054 $total_discount_line = (float) price2num(($pu_ht * $qty) - $total_ht, 'MT');
5055 $total_discount += $total_discount_line;
5056
5057 $i++;
5058 }
5059 }
5060
5061 //print $total_discount; exit;
5062 return (float) price2num($total_discount);
5063 }
5064
5065 return null;
5066 }
5067
5068
5075 public function getTotalWeightVolume()
5076 {
5077 $totalWeight = 0;
5078 $totalVolume = 0;
5079 // defined for shipment only
5080 $totalOrdered = 0;
5081 // defined for shipment only
5082 $totalToShip = 0;
5083
5084 if (empty($this->lines)) {
5085 return array('weight' => $totalWeight, 'volume' => $totalVolume, 'ordered' => $totalOrdered, 'toship' => $totalToShip);
5086 }
5087
5088 foreach ($this->lines as $line) {
5089 if (isset($line->qty_asked)) {
5090 $totalOrdered += $line->qty_asked; // defined for shipment only
5091 }
5092 if (isset($line->qty_shipped)) {
5093 $totalToShip += $line->qty_shipped; // defined for shipment only
5094 } elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty)) {
5095 if (empty($totalToShip)) {
5096 $totalToShip = 0;
5097 }
5098 $totalToShip += $line->qty; // defined for reception only
5099 }
5100
5101 // Define qty, weight, volume, weight_units, volume_units
5102 if ($this->element == 'shipping') {
5103 // for shipments
5104 $qty = $line->qty_shipped ? $line->qty_shipped : 0;
5105 } else {
5106 $qty = $line->qty ? $line->qty : 0;
5107 }
5108
5109 $weight = !empty($line->weight) ? $line->weight : 0;
5110 ($weight == 0 && !empty($line->product->weight)) ? $weight = $line->product->weight : 0;
5111 $volume = !empty($line->volume) ? $line->volume : 0;
5112 ($volume == 0 && !empty($line->product->volume)) ? $volume = $line->product->volume : 0;
5113
5114 $weight_units = !empty($line->weight_units) ? $line->weight_units : 0;
5115 ($weight_units == 0 && !empty($line->product->weight_units)) ? $weight_units = $line->product->weight_units : 0;
5116 $volume_units = !empty($line->volume_units) ? $line->volume_units : 0;
5117 ($volume_units == 0 && !empty($line->product->volume_units)) ? $volume_units = $line->product->volume_units : 0;
5118
5119 $weightUnit = 0;
5120 $volumeUnit = 0;
5121 if (!empty($weight_units)) {
5122 $weightUnit = $weight_units;
5123 }
5124 if (!empty($volume_units)) {
5125 $volumeUnit = $volume_units;
5126 }
5127
5128 if (empty($totalWeight)) {
5129 $totalWeight = 0; // Avoid warning because $totalWeight is ''
5130 }
5131 if (empty($totalVolume)) {
5132 $totalVolume = 0; // Avoid warning because $totalVolume is ''
5133 }
5134
5135 //var_dump($line->volume_units);
5136 if ($weight_units < 50) { // < 50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
5137 $trueWeightUnit = pow(10, $weightUnit);
5138 $totalWeight += $weight * $qty * $trueWeightUnit;
5139 } else {
5140 if ($weight_units == 99) {
5141 // conversion 1 Pound = 0.45359237 KG
5142 $trueWeightUnit = 0.45359237;
5143 $totalWeight += $weight * $qty * $trueWeightUnit;
5144 } elseif ($weight_units == 98) {
5145 // conversion 1 Ounce = 0.0283495 KG
5146 $trueWeightUnit = 0.0283495;
5147 $totalWeight += $weight * $qty * $trueWeightUnit;
5148 } else {
5149 $totalWeight += $weight * $qty; // This may be wrong if we mix different units
5150 }
5151 }
5152 if ($volume_units < 50) { // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
5153 //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
5154 $trueVolumeUnit = pow(10, $volumeUnit);
5155 //print $line->volume;
5156 $totalVolume += $volume * $qty * $trueVolumeUnit;
5157 } else {
5158 $totalVolume += $volume * $qty; // This may be wrong if we mix different units
5159 }
5160 }
5161
5162 return array('weight' => $totalWeight, 'volume' => $totalVolume, 'ordered' => $totalOrdered, 'toship' => $totalToShip);
5163 }
5164
5165
5171 public function setExtraParameters()
5172 {
5173 $this->db->begin();
5174
5175 $extraparams = (!empty($this->extraparams) ? json_encode($this->extraparams) : null);
5176
5177 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
5178 $sql .= " SET extraparams = ".(!empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
5179 $sql .= " WHERE rowid = ".((int) $this->id);
5180
5181 dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
5182 $resql = $this->db->query($sql);
5183 if (!$resql) {
5184 $this->error = $this->db->lasterror();
5185 $this->db->rollback();
5186 return -1;
5187 } else {
5188 $this->db->commit();
5189 return 1;
5190 }
5191 }
5192
5193
5194 // --------------------
5195 // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
5196 // --------------------
5197
5198 /* This is to show add lines */
5199
5209 public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl')
5210 {
5211 global $conf, $user, $langs, $object, $hookmanager, $extrafields, $form;
5212
5213 // Line extrafield
5214 if (!is_object($extrafields)) {
5215 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5216 $extrafields = new ExtraFields($this->db);
5217 }
5218 $extrafields->fetch_name_optionals_label($this->table_element_line);
5219
5220 // Output template part (modules that overwrite templates must declare this into descriptor)
5221 // Use global variables + $dateSelector + $seller and $buyer
5222 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'.
5223 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5224 foreach ($dirtpls as $module => $reldir) {
5225 if (!empty($module)) {
5226 $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
5227 } else {
5228 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php';
5229 }
5230
5231 if (empty($conf->file->strict_mode)) {
5232 $res = @include $tpl;
5233 } else {
5234 $res = include $tpl; // for debug
5235 }
5236 if ($res) {
5237 break;
5238 }
5239 }
5240 }
5241
5242
5243
5244 /* This is to show array of line of details */
5245
5246
5261 public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl')
5262 {
5263 global $conf, $hookmanager, $langs, $user, $form, $extrafields, $object;
5264 // TODO We should not use global var for this
5265 global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
5266
5267 // Define $usemargins (used by objectline_xxx.tpl.php files)
5268 $usemargins = 0;
5269 if (isModEnabled('margin') && !empty($this->element) && in_array($this->element, array('facture', 'facturerec', 'propal', 'commande'))) {
5270 $usemargins = 1;
5271 }
5272
5273 $num = count($this->lines);
5274
5275 // Line extrafield
5276 if (!is_object($extrafields)) {
5277 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5278 $extrafields = new ExtraFields($this->db);
5279 }
5280 $extrafields->fetch_name_optionals_label($this->table_element_line);
5281
5282 if (method_exists($this, 'loadExpeditions')) {
5283 $this->loadExpeditions();
5284 }
5285
5286 $parameters = array('num' => $num, 'dateSelector' => $dateSelector, 'seller' => $seller, 'buyer' => $buyer, 'selected' => $selected, 'table_element_line' => $this->table_element_line);
5287 $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5288 if (empty($reshook)) {
5289 // Output template part (modules that overwrite templates must declare this into descriptor)
5290 // Use global variables + $dateSelector + $seller and $buyer
5291 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook.
5292 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5293 foreach ($dirtpls as $module => $reldir) {
5294 $res = 0;
5295 if (!empty($module)) {
5296 $tpl = dol_buildpath($reldir.'/objectline_title.tpl.php');
5297 } else {
5298 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php';
5299 }
5300 if (file_exists($tpl)) {
5301 if (empty($conf->file->strict_mode)) {
5302 $res = @include $tpl;
5303 } else {
5304 $res = include $tpl; // for debug
5305 }
5306 }
5307 if ($res) {
5308 break;
5309 }
5310 }
5311 }
5312
5313 $i = 0;
5314
5315 print "<!-- begin printObjectLines() -->\n";
5316 foreach ($this->lines as $line) {
5317 //Line extrafield
5318 $line->fetch_optionals();
5319
5320 //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line)))
5321 if (is_object($hookmanager)) { // Old code is commented on preceding line.
5322 if (empty($line->fk_parent_line)) {
5323 $parameters = array('line' => $line, 'num' => $num, 'i' => $i, 'dateSelector' => $dateSelector, 'seller' => $seller, 'buyer' => $buyer, 'selected' => $selected, 'table_element_line' => $line->table_element, 'defaulttpldir' => $defaulttpldir);
5324 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5325 } else {
5326 $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);
5327 $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5328 }
5329 }
5330 if (empty($reshook)) {
5331 $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafields, $defaulttpldir);
5332 }
5333
5334 $i++;
5335 }
5336 print "<!-- end printObjectLines() -->\n";
5337 }
5338
5356 public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/core/tpl')
5357 {
5358 global $conf, $langs, $user, $object, $hookmanager;
5359 global $form;
5360 global $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
5361
5362 // var used into tpl
5363 $text = '';
5364 $description = '';
5365
5366 // Line in view mode
5367 if ($action != 'editline' || $selected != $line->id) {
5368 // Product
5369 if (!empty($line->fk_product) && $line->fk_product > 0) {
5370 $product_static = new Product($this->db);
5371 $product_static->fetch($line->fk_product);
5372
5373 $product_static->ref = $line->ref; //can change ref in hook
5374 $product_static->label = !empty($line->label) ? $line->label : ""; //can change label in hook
5375
5376 $text = $product_static->getNomUrl(1);
5377
5378 // Define output language and label
5379 if (getDolGlobalInt('MAIN_MULTILANGS')) {
5380 if (property_exists($this, 'socid') && !is_object($this->thirdparty)) {
5381 dol_print_error(null, 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
5382 return;
5383 }
5384
5385 $prod = new Product($this->db);
5386 $prod->fetch($line->fk_product);
5387
5388 $outputlangs = $langs;
5389 $newlang = '';
5390 if (empty($newlang) && GETPOST('lang_id', 'aZ09')) {
5391 $newlang = GETPOST('lang_id', 'aZ09');
5392 }
5393 if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE') && empty($newlang) && is_object($this->thirdparty)) {
5394 $newlang = $this->thirdparty->default_lang; // To use language of customer
5395 }
5396 if (!empty($newlang)) {
5397 $outputlangs = new Translate("", $conf);
5398 $outputlangs->setDefaultLang($newlang);
5399 }
5400
5401 $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
5402 } else {
5403 $label = $line->product_label;
5404 }
5405
5406 $text .= ' - '.(!empty($line->label) ? $line->label : $label);
5407 $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.
5408 }
5409
5410 $line->pu_ttc = price2num((!empty($line->subprice) ? $line->subprice : 0) * (1 + ((!empty($line->tva_tx) ? $line->tva_tx : 0) / 100)), 'MU');
5411
5412 // Output template part (modules that overwrite templates must declare this into descriptor)
5413 // Use global variables + $dateSelector + $seller and $buyer
5414 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5415
5416 $qty_shipped = 0;
5417 if (isset($this->expeditions[$line->id])) {
5418 $qty_shipped = $this->expeditions[$line->id];
5419 }
5420 $disableedit = ($qty_shipped > 0) && ($qty_shipped >= $line->qty);
5421
5422 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5423 foreach ($dirtpls as $module => $reldir) {
5424 $res = 0;
5425 if (!empty($module)) {
5426 $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
5427 } else {
5428 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php';
5429 }
5430 //var_dump($tpl);
5431 if (file_exists($tpl)) {
5432 if (empty($conf->file->strict_mode)) {
5433 $res = @include $tpl;
5434 } else {
5435 $res = include $tpl; // for debug
5436 }
5437 }
5438 if ($res) {
5439 break;
5440 }
5441 }
5442 }
5443
5444 // Line in update mode
5445 if ($this->status == 0 && $action == 'editline' && $selected == $line->id) {
5446 $label = (!empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
5447
5448 $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU');
5449
5450 // Output template part (modules that overwrite templates must declare this into descriptor)
5451 // Use global variables + $dateSelector + $seller and $buyer
5452 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5453 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5454 foreach ($dirtpls as $module => $reldir) {
5455 if (!empty($module)) {
5456 $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
5457 } else {
5458 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php';
5459 }
5460
5461 if (empty($conf->file->strict_mode)) {
5462 $res = @include $tpl;
5463 } else {
5464 $res = include $tpl; // for debug
5465 }
5466 if ($res) {
5467 break;
5468 }
5469 }
5470 }
5471 }
5472
5473
5474 /* This is to show array of line of details of source object */
5475
5476
5487 public function printOriginLinesList($restrictlist = '', $selectedLines = array())
5488 {
5489 global $langs, $hookmanager, $form, $action;
5490
5491 print '<tr class="liste_titre">';
5492 print '<td class="linecolref">'.$langs->trans('Ref').'</td>';
5493 print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
5494 print '<td class="linecolvat right">'.$langs->trans('VATRate').'</td>';
5495 print '<td class="linecoluht right">'.$langs->trans('PriceUHT').'</td>';
5496 if (isModEnabled("multicurrency")) {
5497 print '<td class="linecoluht_currency right">'.$langs->trans('PriceUHTCurrency').'</td>';
5498 }
5499 print '<td class="linecolqty right">'.$langs->trans('Qty').'</td>';
5500 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5501 print '<td class="linecoluseunit left">'.$langs->trans('Unit').'</td>';
5502 }
5503 print '<td class="linecoldiscount right">'.$langs->trans('ReductionShort').'</td>';
5504 print '<td class="linecolht right">'.$langs->trans('TotalHT').'</td>';
5505 print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
5506 print '</tr>';
5507 $i = 0;
5508
5509 if (!empty($this->lines)) {
5510 foreach ($this->lines as $line) {
5511 $reshook = 0;
5512 //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line))) {
5513 if (is_object($hookmanager)) { // Old code is commented on preceding line.
5514 $parameters = array('line' => $line, 'i' => $i, 'restrictlist' => $restrictlist, 'selectedLines' => $selectedLines);
5515 if (!empty($line->fk_parent_line)) {
5516 $parameters['fk_parent_line'] = $line->fk_parent_line;
5517 }
5518 $reshook = $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5519 }
5520 if (empty($reshook)) {
5521 $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines);
5522 }
5523
5524 $i++;
5525 }
5526 }
5527 }
5528
5542 public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
5543 {
5544 global $langs, $conf;
5545
5546 //var_dump($line);
5547 if (!empty($line->date_start)) {
5548 // @phan-suppress-next-line PhanUndeclaredProperty
5549 $date_start = $line->date_start;
5550 } else {
5551 $date_start = $line->date_debut_prevue;
5552 if ($line->date_debut_reel) {
5553 $date_start = $line->date_debut_reel;
5554 }
5555 }
5556 if (!empty($line->date_end)) {
5557 // @phan-suppress-next-line PhanUndeclaredProperty
5558 $date_end = $line->date_end;
5559 } else {
5560 $date_end = $line->date_fin_prevue;
5561 if ($line->date_fin_reel) {
5562 $date_end = $line->date_fin_reel;
5563 }
5564 }
5565
5566 $this->tpl['id'] = $line->id;
5567
5568 $this->tpl['label'] = '';
5569 if (!empty($line->fk_parent_line)) {
5570 $this->tpl['label'] .= img_picto('', 'rightarrow');
5571 }
5572
5573 if (($line->info_bits & 2) == 2) { // TODO Not sure this is used for source object
5574 $discount = new DiscountAbsolute($this->db);
5575 if (property_exists($this, 'socid')) {
5576 $discount->fk_soc = $this->socid;
5577 $discount->socid = $this->socid;
5578 }
5579 $this->tpl['label'] .= $discount->getNomUrl(0, 'discount');
5580 } elseif (!empty($line->fk_product)) {
5581 $productstatic = new Product($this->db);
5582 $productstatic->id = $line->fk_product;
5583 $productstatic->ref = $line->ref;
5584 $productstatic->type = $line->fk_product_type;
5585 if (empty($productstatic->ref)) {
5586 $line->fetch_product();
5587 $productstatic = $line->product;
5588 }
5589
5590 $this->tpl['label'] .= $productstatic->getNomUrl(1);
5591 $this->tpl['label'] .= ' - '.(!empty($line->label) ? $line->label : $line->product_label);
5592 // Dates
5593 if ($line->product_type == 1 && ($date_start || $date_end)) {
5594 $this->tpl['label'] .= get_date_range($date_start, $date_end);
5595 }
5596 } else {
5597 $this->tpl['label'] .= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
5598 if (!empty($line->desc)) {
5599 $this->tpl['label'] .= $line->desc;
5600 } else {
5601 $this->tpl['label'] .= ($line->label ? '&nbsp;'.$line->label : '');
5602 }
5603
5604 // Dates
5605 if ($line->product_type == 1 && ($date_start || $date_end)) {
5606 $this->tpl['label'] .= get_date_range($date_start, $date_end);
5607 }
5608 }
5609
5610 if (!empty($line->desc)) {
5611 '@phan-var-force OrderLine|FactureLigne|ContratLigne|FactureFournisseurLigneRec|SupplierInvoiceLine|SupplierProposalLine $line';
5612 if ($line->desc == '(CREDIT_NOTE)') { // TODO Not sure this is used for source object
5613 $discount = new DiscountAbsolute($this->db);
5614 $discount->fetch($line->fk_remise_except);
5615 $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
5616 } elseif ($line->desc == '(DEPOSIT)') { // TODO Not sure this is used for source object
5617 $discount = new DiscountAbsolute($this->db);
5618 $discount->fetch($line->fk_remise_except);
5619 $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
5620 } elseif ($line->desc == '(EXCESS RECEIVED)') {
5621 $discount = new DiscountAbsolute($this->db);
5622 $discount->fetch($line->fk_remise_except);
5623 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
5624 } elseif ($line->desc == '(EXCESS PAID)') {
5625 $discount = new DiscountAbsolute($this->db);
5626 $discount->fetch($line->fk_remise_except);
5627 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
5628 } else {
5629 $this->tpl['description'] = dol_trunc($line->desc, 60);
5630 }
5631 } else {
5632 $this->tpl['description'] = '&nbsp;';
5633 }
5634
5635 // VAT Rate
5636 $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
5637 $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
5638 if (!empty($line->vat_src_code) && !preg_match('/\‍(/', $this->tpl['vat_rate'])) {
5639 $this->tpl['vat_rate'] .= ' ('.$line->vat_src_code.')';
5640 }
5641
5642 $this->tpl['price'] = price($line->subprice);
5643 $this->tpl['total_ht'] = price($line->total_ht);
5644 $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
5645 $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
5646 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5647 $this->tpl['unit'] = $line->getLabelOfUnit('long', $langs);
5648 $this->tpl['unit_short'] = $line->getLabelOfUnit('short', $langs);
5649 //$this->tpl['unit_code'] = $line->getLabelOfUnit('code');
5650 }
5651 $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate((string) $line->remise_percent, true) : '&nbsp;';
5652
5653 // Is the line strike or not
5654 $this->tpl['strike'] = 0;
5655 if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) {
5656 $this->tpl['strike'] = 1;
5657 }
5658
5659 // Output template part (modules that overwrite templates must declare this into descriptor)
5660 // Use global variables + $dateSelector + $seller and $buyer
5661 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5662 foreach ($dirtpls as $module => $reldir) {
5663 if (!empty($module)) {
5664 $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
5665 } else {
5666 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php';
5667 }
5668
5669 if (empty($conf->file->strict_mode)) {
5670 $res = @include $tpl;
5671 } else {
5672 $res = include $tpl; // for debug
5673 }
5674 if ($res) {
5675 break;
5676 }
5677 }
5678 }
5679
5680
5681 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5692 public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
5693 {
5694 // phpcs:enable
5695 $this->db->begin();
5696
5697 $sql = "INSERT INTO ".$this->db->prefix()."element_resources (";
5698 $sql .= "resource_id";
5699 $sql .= ", resource_type";
5700 $sql .= ", element_id";
5701 $sql .= ", element_type";
5702 $sql .= ", busy";
5703 $sql .= ", mandatory";
5704 $sql .= ") VALUES (";
5705 $sql .= ((int) $resource_id);
5706 $sql .= ", '".$this->db->escape($resource_type)."'";
5707 $sql .= ", '".$this->db->escape($this->id)."'";
5708 $sql .= ", '".$this->db->escape($this->element)."'";
5709 $sql .= ", '".$this->db->escape($busy)."'";
5710 $sql .= ", '".$this->db->escape($mandatory)."'";
5711 $sql .= ")";
5712
5713 dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
5714 if ($this->db->query($sql)) {
5715 $this->db->commit();
5716 return 1;
5717 } else {
5718 $this->error = $this->db->lasterror();
5719 $this->db->rollback();
5720 return 0;
5721 }
5722 }
5723
5724 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5733 public function delete_resource($rowid, $element, $notrigger = 0)
5734 {
5735 // phpcs:enable
5736 global $user;
5737
5738 $this->db->begin();
5739
5740 $sql = "DELETE FROM ".$this->db->prefix()."element_resources";
5741 $sql .= " WHERE rowid = ".((int) $rowid);
5742
5743 dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
5744
5745 $resql = $this->db->query($sql);
5746 if (!$resql) {
5747 $this->error = $this->db->lasterror();
5748 $this->db->rollback();
5749 return -1;
5750 } else {
5751 if (!$notrigger) {
5752 $result = $this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
5753 if ($result < 0) {
5754 $this->db->rollback();
5755 return -1;
5756 }
5757 }
5758 $this->db->commit();
5759 return 1;
5760 }
5761 }
5762
5763
5769 public function __clone()
5770 {
5771 // Force a copy of this->lines, otherwise it will point to same object.
5772 if (isset($this->lines) && is_array($this->lines)) {
5773 $nboflines = count($this->lines);
5774 for ($i = 0; $i < $nboflines; $i++) {
5775 if (is_object($this->lines[$i])) {
5776 $this->lines[$i] = clone $this->lines[$i];
5777 }
5778 }
5779 }
5780 }
5781
5795 protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
5796 {
5797 global $conf, $langs, $user, $hookmanager, $action;
5798
5799 $srctemplatepath = '';
5800
5801 $parameters = array('modelspath' => $modelspath, 'modele' => $modele, 'outputlangs' => $outputlangs, 'hidedetails' => $hidedetails, 'hidedesc' => $hidedesc, 'hideref' => $hideref, 'moreparams' => $moreparams);
5802 $reshook = $hookmanager->executeHooks('commonGenerateDocument', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5803
5804 if (!empty($reshook)) {
5805 return $reshook;
5806 }
5807
5808 dol_syslog("commonGenerateDocument modele=".$modele." outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
5809
5810 if (empty($modele)) {
5811 $this->error = 'BadValueForParameterModele';
5812 return -1;
5813 }
5814
5815 // Increase limit for PDF build
5816 $err = error_reporting();
5817 error_reporting(0);
5818 @set_time_limit(120);
5819 error_reporting($err);
5820
5821 // If selected model is a filename template (then $modele="modelname" or "modelname:filename")
5822 $tmp = explode(':', $modele, 2);
5823 $saved_model = $modele;
5824 if (!empty($tmp[1])) {
5825 $modele = $tmp[0];
5826 $srctemplatepath = $tmp[1];
5827 }
5828
5829 // Search template files
5830 $file = '';
5831 $classname = '';
5832 $filefound = '';
5833 $dirmodels = array('/');
5834 if (is_array($conf->modules_parts['models'])) {
5835 $dirmodels = array_merge($dirmodels, $conf->modules_parts['models']);
5836 }
5837 foreach ($dirmodels as $reldir) {
5838 foreach (array('doc', 'pdf') as $prefix) {
5839 if (in_array(get_class($this), array('Adherent'))) {
5840 // Member module use prefix_modele.class.php
5841 $file = $prefix."_".$modele.".class.php";
5842 } else {
5843 // Other module use prefix_modele.modules.php
5844 $file = $prefix."_".$modele.".modules.php";
5845 }
5846
5847 $file = dol_sanitizeFileName($file);
5848
5849 // We check if the file exists
5850 $file = dol_buildpath($reldir.$modelspath.$file, 0);
5851 if (file_exists($file)) {
5852 $filefound = $file;
5853 $classname = $prefix.'_'.$modele;
5854 break;
5855 }
5856 }
5857 if ($filefound) {
5858 break;
5859 }
5860 }
5861
5862 if ($filefound === '' || $classname === '') {
5863 $this->error = $langs->trans("Error").' Failed to load doc generator with modelpaths='.$modelspath.' - modele='.$modele;
5864 $this->errors[] = $this->error;
5865 dol_syslog($this->error, LOG_ERR);
5866 return -1;
5867 }
5868
5869 // Sanitize $filefound
5870 $filefound = dol_sanitizePathName($filefound);
5871
5872 // If generator was found
5873 global $db; // Required to solve a conception error making an include of some code that uses $db instead of $this->db just after.
5874
5875 require_once $filefound;
5876
5877 $obj = new $classname($this->db);
5878
5879 // TODO: Check the following classes that seem possible for $obj, but removed for compatibility:
5880 // ModeleBankAccountDoc|ModeleExpenseReport|ModelePDFBom|ModelePDFCommandes|ModelePDFContract|
5881 // ModelePDFDeliveryOrder|ModelePDFEvaluation|ModelePDFFactures|ModelePDFFicheinter|
5882 // ModelePDFMo|ModelePDFMovement|ModelePDFProduct|ModelePDFProjects|ModelePDFPropales|
5883 // ModelePDFRecruitmentJobPosition|ModelePDFSupplierProposal|ModelePDFSuppliersInvoices|
5884 // ModelePDFSuppliersOrders|ModelePDFSuppliersPayments|ModelePdfExpedition|ModelePdfReception|
5885 // ModelePDFStock|ModelePDFStockTransfer|
5886 // ModeleDon|ModelePDFTask|
5887 // ModelePDFAsset|ModelePDFTicket|ModelePDFUserGroup|ModeleThirdPartyDoc|ModelePDFUser
5888 // Has no write_file: ModeleBarCode|ModeleImports|ModeleExports|
5889 '@phan-var-force ModelePDFMember $obj';
5890 // '@phan-var-force ModelePDFMember|ModeleBarCode|ModeleDon|ModeleExports|ModeleImports|ModelePDFAsset|ModelePDFContract|ModelePDFDeliveryOrder|ModelePDFEvaluation|ModelePDFFactures|ModelePDFFicheinter|ModelePDFMo|ModelePDFMovement|ModelePDFProduct|ModelePDFProjects|ModelePDFPropales|ModelePDFRecruitmentJobPosition|ModelePDFStock|ModelePDFStockTransfer|ModelePDFSupplierProposal|ModelePDFSuppliersInvoices|ModelePDFSuppliersOrders|ModelePDFSuppliersPayments|ModelePDFTask|ModelePDFTicket|ModelePDFUser|ModelePDFUserGroup|ModelePdfExpedition|ModelePdfReception|ModeleThirdPartyDoc $obj';
5891
5892 // If generator is ODT, we must have srctemplatepath defined, if not we set it.
5893 if ($obj->type == 'odt' && empty($srctemplatepath)) {
5894 $varfortemplatedir = $obj->scandir;
5895 if ($varfortemplatedir && getDolGlobalString($varfortemplatedir)) {
5896 $dirtoscan = getDolGlobalString($varfortemplatedir);
5897
5898 $listoffiles = array();
5899
5900 // Now we add first model found in directories scanned
5901 $listofdir = explode(',', $dirtoscan);
5902 foreach ($listofdir as $key => $tmpdir) {
5903 $tmpdir = trim($tmpdir);
5904 $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
5905 if (!$tmpdir) {
5906 unset($listofdir[$key]);
5907 continue;
5908 }
5909 if (is_dir($tmpdir)) {
5910 $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
5911 if (count($tmpfiles)) {
5912 $listoffiles = array_merge($listoffiles, $tmpfiles);
5913 }
5914 }
5915 }
5916
5917 if (count($listoffiles)) {
5918 foreach ($listoffiles as $record) {
5919 $srctemplatepath = $record['fullname'];
5920 break;
5921 }
5922 }
5923 }
5924
5925 if (empty($srctemplatepath)) {
5926 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
5927 return -1;
5928 }
5929 }
5930
5931 if ($obj->type == 'odt' && !empty($srctemplatepath)) {
5932 if (!dol_is_file($srctemplatepath)) {
5933 dol_syslog("Failed to locate template file ".$srctemplatepath, LOG_WARNING);
5934 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
5935 return -1;
5936 }
5937 }
5938
5939 // We save charset_output to restore it because write_file can change it if needed for
5940 // output format that does not support UTF8.
5941 $sav_charset_output = empty($outputlangs->charset_output) ? '' : $outputlangs->charset_output;
5942
5943 // update model_pdf in object
5944 $this->model_pdf = $saved_model;
5945
5946 if ($obj instanceof ModelePDFMember) {
5947 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, 'tmp_cards');
5948 } else {
5949 // TODO: Try to set type above again
5950 '@phan-var-force ModeleBarCode|ModeleDon|ModeleExports|ModeleImports|ModelePDFAsset|ModelePDFContract|ModelePDFDeliveryOrder|ModelePDFEvaluation|ModelePDFFactures|ModelePDFFicheinter|ModelePDFMo|ModelePDFMovement|ModelePDFProduct|ModelePDFProjects|ModelePDFPropales|ModelePDFRecruitmentJobPosition|ModelePDFStock|ModelePDFStockTransfer|ModelePDFSupplierProposal|ModelePDFSuppliersInvoices|ModelePDFSuppliersOrders|ModelePDFSuppliersPayments|ModelePDFTask|ModelePDFTicket|ModelePDFUser|ModelePDFUserGroup|ModelePdfExpedition|ModelePdfReception|ModeleThirdPartyDoc $obj';
5951 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
5952 }
5953 // After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
5954
5955 if ($resultwritefile > 0) {
5956 $outputlangs->charset_output = $sav_charset_output;
5957
5958 // We delete old preview
5959 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5960 dol_delete_preview($this);
5961
5962 // Index file in database
5963 if (!empty($obj->result['fullpath'])) {
5964 $destfull = $obj->result['fullpath'];
5965
5966 // Update the last_main_doc field into main object (if document generator has property ->update_main_doc_field set)
5967 $update_main_doc_field = 0;
5968 if (!empty($obj->update_main_doc_field)) {
5969 $update_main_doc_field = 1;
5970 }
5971
5972 // Check that the file exists, before indexing it.
5973 // Hint: It does not exist, if we create a PDF and auto delete the ODT File
5974 if (dol_is_file($destfull)) {
5975 $this->indexFile($destfull, $update_main_doc_field);
5976 }
5977 } else {
5978 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);
5979 }
5980
5981 // Success in building document. We build meta file.
5982 dol_meta_create($this);
5983
5984 return 1;
5985 } else {
5986 $outputlangs->charset_output = $sav_charset_output;
5987 $this->error = $obj->error;
5988 $this->errors = $obj->errors;
5989 dol_syslog("Error generating document for ".__CLASS__.". Error: ".$obj->error, LOG_ERR);
5990 return -1;
5991 }
5992 }
5993
6003 public function indexFile($destfull, $update_main_doc_field)
6004 {
6005 global $conf, $user;
6006
6007 $upload_dir = dirname($destfull);
6008 $destfile = basename($destfull);
6009 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
6010
6011 if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a tmp dir
6012 $filename = basename($destfile);
6013 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
6014 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
6015
6016 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
6017 $ecmfile = new EcmFiles($this->db);
6018 $result = $ecmfile->fetch(0, '', ($rel_dir ? $rel_dir.'/' : '').$filename);
6019
6020 // Set the public "share" key
6021 $setsharekey = false;
6022 if ($this->element == 'propal' || $this->element == 'proposal') {
6023 if (getDolGlobalInt("PROPOSAL_ALLOW_ONLINESIGN")) {
6024 $setsharekey = true; // feature to make online signature is not set or set to on (default)
6025 }
6026 if (getDolGlobalInt("PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD")) {
6027 $setsharekey = true;
6028 }
6029 }
6030 if ($this->element == 'commande' && getDolGlobalInt("ORDER_ALLOW_EXTERNAL_DOWNLOAD")) {
6031 $setsharekey = true;
6032 }
6033 if ($this->element == 'facture' && getDolGlobalInt("INVOICE_ALLOW_EXTERNAL_DOWNLOAD")) {
6034 $setsharekey = true;
6035 }
6036 if ($this->element == 'bank_account' && getDolGlobalInt("BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD")) {
6037 $setsharekey = true;
6038 }
6039 if ($this->element == 'product' && getDolGlobalInt("PRODUCT_ALLOW_EXTERNAL_DOWNLOAD")) {
6040 $setsharekey = true;
6041 }
6042 if ($this->element == 'contrat' && getDolGlobalInt("CONTRACT_ALLOW_EXTERNAL_DOWNLOAD")) {
6043 $setsharekey = true;
6044 }
6045 if ($this->element == 'fichinter' && getDolGlobalInt("FICHINTER_ALLOW_EXTERNAL_DOWNLOAD")) {
6046 $setsharekey = true;
6047 }
6048 if ($this->element == 'supplier_proposal' && getDolGlobalInt("SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD")) {
6049 $setsharekey = true;
6050 }
6051 if ($this->element == 'societe_rib' && getDolGlobalInt("SOCIETE_RIB_ALLOW_ONLINESIGN")) {
6052 $setsharekey = true;
6053 }
6054
6055 if ($setsharekey) {
6056 if (empty($ecmfile->share)) { // Because object not found or share not set yet
6057 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
6058 $ecmfile->share = getRandomPassword(true);
6059 }
6060 }
6061
6062 if ($result > 0) {
6063 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
6064 $ecmfile->fullpath_orig = '';
6065 $ecmfile->gen_or_uploaded = 'generated';
6066 $ecmfile->description = ''; // indexed content
6067 $ecmfile->keywords = ''; // keyword content
6068 $ecmfile->src_object_type = $this->table_element;
6069 $ecmfile->src_object_id = $this->id;
6070 $result = $ecmfile->update($user);
6071 if ($result < 0) {
6072 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
6073 return -1;
6074 }
6075 } else {
6076 $ecmfile->entity = $conf->entity;
6077 $ecmfile->filepath = $rel_dir;
6078 $ecmfile->filename = $filename;
6079 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
6080 $ecmfile->fullpath_orig = '';
6081 $ecmfile->gen_or_uploaded = 'generated';
6082 $ecmfile->description = ''; // indexed content
6083 $ecmfile->keywords = ''; // keyword content
6084 $ecmfile->src_object_type = $this->table_element; // $this->table_name is 'myobject' or 'mymodule_myobject'.
6085 $ecmfile->src_object_id = $this->id;
6086
6087 $result = $ecmfile->create($user);
6088 if ($result < 0) {
6089 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
6090 return -1;
6091 }
6092 }
6093
6094 /*$this->result['fullname']=$destfull;
6095 $this->result['filepath']=$ecmfile->filepath;
6096 $this->result['filename']=$ecmfile->filename;*/
6097 //var_dump($obj->update_main_doc_field);exit;
6098
6099 if ($update_main_doc_field && !empty($this->table_element)) {
6100 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath."/".$ecmfile->filename)."'";
6101 $sql .= " WHERE rowid = ".((int) $this->id);
6102
6103 $resql = $this->db->query($sql);
6104 if (!$resql) {
6105 dol_print_error($this->db);
6106 return -1;
6107 } else {
6108 $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
6109 }
6110 }
6111 }
6112
6113 return 1;
6114 }
6115
6124 public function addThumbs($file, $quality = 50)
6125 {
6126 $file_osencoded = dol_osencode($file);
6127
6128 if (file_exists($file_osencoded)) {
6129 require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
6130
6131 $tmparraysize = getDefaultImageSizes();
6132 $maxwidthsmall = $tmparraysize['maxwidthsmall'];
6133 $maxheightsmall = $tmparraysize['maxheightsmall'];
6134 $maxwidthmini = $tmparraysize['maxwidthmini'];
6135 $maxheightmini = $tmparraysize['maxheightmini'];
6136 //$quality = $tmparraysize['quality'];
6137
6138 // Create small thumbs for company (Ratio is near 16/9)
6139 // Used on logon for example
6140 vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
6141
6142 // Create mini thumbs for company (Ratio is near 16/9)
6143 // Used on menu or for setup page for example
6144 vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
6145 }
6146 }
6147
6155 public function delThumbs($file)
6156 {
6157 $imgThumbName = getImageFileNameForSize($file, '_small'); // Full path of thumb file
6158 dol_delete_file($imgThumbName);
6159 $imgThumbName = getImageFileNameForSize($file, '_mini'); // Full path of thumb file
6160 dol_delete_file($imgThumbName);
6161 }
6162
6163
6164 /* Functions common to commonobject and commonobjectline */
6165
6166 /* For default values */
6167
6181 public function getDefaultCreateValueFor($fieldname, $alternatevalue = null, $type = 'alphanohtml')
6182 {
6183 // If param here has been posted, we use this value first.
6184 if (GETPOSTISSET($fieldname)) {
6185 return GETPOST($fieldname, $type, 3);
6186 }
6187
6188 if (isset($alternatevalue)) {
6189 return $alternatevalue;
6190 }
6191
6192 $newelement = $this->element;
6193 if ($newelement == 'facture') {
6194 $newelement = 'invoice';
6195 }
6196 if ($newelement == 'commande') {
6197 $newelement = 'order';
6198 }
6199 if (empty($newelement)) {
6200 dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
6201 return '';
6202 }
6203
6204 $keyforfieldname = strtoupper($newelement.'_DEFAULT_'.$fieldname);
6205 //var_dump($keyforfieldname);
6206 if (getDolGlobalString($keyforfieldname)) {
6207 return getDolGlobalString($keyforfieldname);
6208 }
6209
6210 // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
6211 // store content into $conf->cache['overwrite_default']
6212
6213 return '';
6214 }
6215
6216
6217 /* For triggers */
6218
6219
6220 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6231 public function call_trigger($triggerName, $user)
6232 {
6233 // phpcs:enable
6234 global $langs, $conf;
6235
6236 if (!empty(self::TRIGGER_PREFIX) && strpos($triggerName, self::TRIGGER_PREFIX . '_') !== 0) {
6237 dol_print_error(null, 'The trigger "' . $triggerName . '" does not start with "' . self::TRIGGER_PREFIX . '_" as required.');
6238 exit;
6239 }
6240 if (!is_object($langs)) { // If lang was not defined, we set it. It is required by run_triggers().
6241 dol_syslog("call_trigger was called with no langs variable defined".getCallerInfoString(), LOG_WARNING);
6242 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
6243 $langs = new Translate('', $conf);
6244 $langs->load("main");
6245 }
6246
6247 include_once DOL_DOCUMENT_ROOT.'/core/class/interfaces.class.php';
6248 $interface = new Interfaces($this->db);
6249 $result = $interface->run_triggers($triggerName, $this, $user, $langs, $conf);
6250
6251 if ($result < 0) {
6252 if (!empty($this->errors)) {
6253 $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.
6254 } else {
6255 $this->errors = $interface->errors;
6256 }
6257 }
6258 return $result;
6259 }
6260
6261
6262 /* Functions for data in other language */
6263
6264
6273 {
6274 // To avoid SQL errors. Probably not the better solution though
6275 if (!$this->element) {
6276 return 0;
6277 }
6278 if (!($this->id > 0)) {
6279 return 0;
6280 }
6281 if (is_array($this->array_languages)) {
6282 return 1;
6283 }
6284
6285 $this->array_languages = array();
6286
6287 $element = $this->element;
6288 if ($element == 'categorie') {
6289 $element = 'categories'; // For compatibility
6290 }
6291
6292 // Request to get translation values for object
6293 $sql = "SELECT rowid, property, lang , value";
6294 $sql .= " FROM ".$this->db->prefix()."object_lang";
6295 $sql .= " WHERE type_object = '".$this->db->escape($element)."'";
6296 $sql .= " AND fk_object = ".((int) $this->id);
6297
6298 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6299 $resql = $this->db->query($sql);
6300 if ($resql) {
6301 $numrows = $this->db->num_rows($resql);
6302 if ($numrows) {
6303 $i = 0;
6304 while ($i < $numrows) {
6305 $obj = $this->db->fetch_object($resql);
6306 $key = $obj->property;
6307 $value = $obj->value;
6308 $codelang = $obj->lang;
6309 $type = $this->fields[$key]['type'];
6310
6311 // we can add this attribute to object
6312 if (preg_match('/date/', $type)) {
6313 $this->array_languages[$key][$codelang] = $this->db->jdate($value);
6314 } else {
6315 $this->array_languages[$key][$codelang] = $value;
6316 }
6317
6318 $i++;
6319 }
6320 }
6321
6322 $this->db->free($resql);
6323
6324 if ($numrows) {
6325 return $numrows;
6326 } else {
6327 return 0;
6328 }
6329 } else {
6330 dol_print_error($this->db);
6331 return -1;
6332 }
6333 }
6334
6341 public function setValuesForExtraLanguages($onlykey = '')
6342 {
6343 // Get extra fields
6344 foreach ($_POST as $postfieldkey => $postfieldvalue) {
6345 $tmparray = explode('-', $postfieldkey);
6346 if ($tmparray[0] != 'field') {
6347 continue;
6348 }
6349
6350 $element = $tmparray[1];
6351 $key = $tmparray[2];
6352 $codelang = $tmparray[3];
6353 //var_dump("postfieldkey=".$postfieldkey." element=".$element." key=".$key." codelang=".$codelang);
6354
6355 if (!empty($onlykey) && $key != $onlykey) {
6356 continue;
6357 }
6358 if ($element != $this->element) {
6359 continue;
6360 }
6361
6362 $key_type = $this->fields[$key]['type'];
6363
6364 $enabled = 1;
6365 if (isset($this->fields[$key]['enabled'])) {
6366 $enabled = (int) dol_eval($this->fields[$key]['enabled'], 1, 1, '1');
6367 }
6368 /*$perms = 1;
6369 if (isset($this->fields[$key]['perms']))
6370 {
6371 $perms = (int) dol_eval($this->fields[$key]['perms'], 1, 1, '1');
6372 }*/
6373 if (empty($enabled)) {
6374 continue;
6375 }
6376 //if (empty($perms)) continue;
6377
6378 if (in_array($key_type, array('date'))) {
6379 // Clean parameters
6380 // TODO GMT date in memory must be GMT so we should add gm=true in parameters
6381 $value_key = dol_mktime(0, 0, 0, GETPOSTINT($postfieldkey."month"), GETPOSTINT($postfieldkey."day"), GETPOSTINT($postfieldkey."year"));
6382 } elseif (in_array($key_type, array('datetime'))) {
6383 // Clean parameters
6384 // TODO GMT date in memory must be GMT so we should add gm=true in parameters
6385 $value_key = dol_mktime(GETPOSTINT($postfieldkey."hour"), GETPOSTINT($postfieldkey."min"), 0, GETPOSTINT($postfieldkey."month"), GETPOSTINT($postfieldkey."day"), GETPOSTINT($postfieldkey."year"));
6386 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
6387 $value_arr = GETPOST($postfieldkey, 'array'); // check if an array
6388 if (!empty($value_arr)) {
6389 $value_key = implode(',', $value_arr);
6390 } else {
6391 $value_key = '';
6392 }
6393 } elseif (in_array($key_type, array('price', 'double'))) {
6394 $value_arr = GETPOST($postfieldkey, 'alpha');
6395 $value_key = price2num($value_arr);
6396 } else {
6397 $value_key = GETPOST($postfieldkey);
6398 if (in_array($key_type, array('link')) && $value_key == '-1') {
6399 $value_key = '';
6400 }
6401 }
6402
6403 $this->array_languages[$key][$codelang] = $value_key;
6404
6405 /*if ($nofillrequired) {
6406 $langs->load('errors');
6407 setEventMessages($langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required), null, 'errors');
6408 return -1;
6409 }*/
6410 }
6411
6412 return 1;
6413 }
6414
6415
6416 /* Functions for extrafields */
6417
6424 public function fetchNoCompute($id)
6425 {
6426 global $conf;
6427
6428 $savDisableCompute = $conf->disable_compute;
6429 $conf->disable_compute = 1;
6430
6431 $ret = $this->fetch($id); /* @phpstan-ignore-line */
6432
6433 $conf->disable_compute = $savDisableCompute;
6434
6435 return $ret;
6436 }
6437
6438 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6448 public function fetch_optionals($rowid = null, $optionsArray = null)
6449 {
6450 // phpcs:enable
6451 global $conf, $extrafields;
6452
6453 if (empty($rowid)) {
6454 $rowid = $this->id;
6455 }
6456 if (empty($rowid) && isset($this->rowid)) { // @phan-suppress-current-line PhanUndeclaredProperty
6457 $rowid = $this->rowid; // deprecated @phan-suppress-current-line PhanUndeclaredProperty
6458 }
6459
6460 // To avoid SQL errors. Probably not the better solution though
6461 if (!$this->table_element) {
6462 return 0;
6463 }
6464
6465 $this->array_options = array();
6466
6467 if (!is_array($optionsArray)) {
6468 // If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
6469 if (!isset($extrafields) || !is_object($extrafields)) {
6470 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6471 $extrafields = new ExtraFields($this->db);
6472 }
6473
6474 // Load array of extrafields for elementype = $this->table_element
6475 if (empty($extrafields->attributes[$this->table_element]['loaded'])) {
6476 $extrafields->fetch_name_optionals_label($this->table_element);
6477 }
6478 $optionsArray = (!empty($extrafields->attributes[$this->table_element]['label']) ? $extrafields->attributes[$this->table_element]['label'] : null);
6479 } else {
6480 global $extrafields;
6481 dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
6482 }
6483
6484 $table_element = $this->table_element;
6485 if ($table_element == 'categorie') {
6486 $table_element = 'categories'; // For compatibility
6487 }
6488
6489 // Request to get complementary values
6490 if (is_array($optionsArray) && count($optionsArray) > 0) {
6491 $sql = "SELECT rowid";
6492 foreach ($optionsArray as $name => $label) {
6493 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || (!in_array($extrafields->attributes[$this->table_element]['type'][$name], ['separate', 'point', 'multipts', 'linestrg','polygon']))) {
6494 $sql .= ", ".$name;
6495 }
6496 // use geo sql fonction to read as text
6497 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] == 'point') {
6498 $sql .= ", ST_AsWKT(".$name.") as ".$name;
6499 }
6500 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] == 'multipts') {
6501 $sql .= ", ST_AsWKT(".$name.") as ".$name;
6502 }
6503 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] == 'linestrg') {
6504 $sql .= ", ST_AsWKT(".$name.") as ".$name;
6505 }
6506 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] == 'polygon') {
6507 $sql .= ", ST_AsWKT(".$name.") as ".$name;
6508 }
6509 }
6510 $sql .= " FROM ".$this->db->prefix().$table_element."_extrafields";
6511 $sql .= " WHERE fk_object = ".((int) $rowid);
6512
6513 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6514 $resql = $this->db->query($sql);
6515 if ($resql) {
6516 $numrows = $this->db->num_rows($resql);
6517 if ($numrows) {
6518 $tab = $this->db->fetch_array($resql);
6519
6520 foreach ($tab as $key => $value) {
6521 // 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)
6522 if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && !is_int($key)) {
6523 // we can add this attribute to object
6524 if (!empty($extrafields->attributes[$this->table_element]) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date', 'datetime'))) {
6525 //var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
6526 $this->array_options["options_".$key] = $this->db->jdate($value);
6527 } else {
6528 $this->array_options["options_".$key] = $value;
6529 }
6530
6531 //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
6532 }
6533 if (!empty($extrafields->attributes[$this->table_element]['type'][$key]) && $extrafields->attributes[$this->table_element]['type'][$key] == 'password') {
6534 if (!empty($value) && preg_match('/^dolcrypt:/', $value)) {
6535 $this->array_options["options_".$key] = dolDecrypt($value);
6536 }
6537 }
6538 }
6539 } else {
6544 if (is_array($extrafields->attributes[$this->table_element]['label'])) {
6545 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
6546 $this->array_options['options_' . $key] = null;
6547 }
6548 }
6549 }
6550
6551 // If field is a computed field, value must become result of compute (regardless of whether a row exists
6552 // in the element's extrafields table)
6553 if (is_array($extrafields->attributes[$this->table_element]['label'])) {
6554 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
6555 if (!empty($extrafields->attributes[$this->table_element]) && !empty($extrafields->attributes[$this->table_element]['computed'][$key])) {
6556 //var_dump($conf->disable_compute);
6557 if (empty($conf->disable_compute)) {
6558 global $objectoffield; // We set a global variable to $objectoffield so
6559 $objectoffield = $this; // we can use it inside computed formula
6560 $this->array_options['options_' . $key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0, '2');
6561 }
6562 }
6563 }
6564 }
6565
6566 $this->db->free($resql);
6567
6568 if ($numrows) {
6569 return $numrows;
6570 } else {
6571 return 0;
6572 }
6573 } else {
6574 $this->errors[] = $this->db->lasterror;
6575 return -1;
6576 }
6577 }
6578 return 0;
6579 }
6580
6587 public function deleteExtraFields()
6588 {
6589 global $conf;
6590
6591 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6592 return 0;
6593 }
6594
6595 $this->db->begin();
6596
6597 $table_element = $this->table_element;
6598 if ($table_element == 'categorie') {
6599 $table_element = 'categories'; // For compatibility
6600 }
6601
6602 dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
6603
6604 $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6605
6606 $resql = $this->db->query($sql_del);
6607 if (!$resql) {
6608 $this->error = $this->db->lasterror();
6609 $this->db->rollback();
6610 return -1;
6611 } else {
6612 $this->db->commit();
6613 return 1;
6614 }
6615 }
6616
6627 public function insertExtraFields($trigger = '', $userused = null)
6628 {
6629 global $langs, $user;
6630
6631 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6632 return 0;
6633 }
6634
6635 if (empty($userused)) {
6636 $userused = $user;
6637 }
6638
6639 $error = 0;
6640
6641 if (!empty($this->array_options)) {
6642 // Check parameters
6643 $langs->load('admin');
6644 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6645 $extrafields = new ExtraFields($this->db);
6646 $target_extrafields = $extrafields->fetch_name_optionals_label($this->table_element);
6647
6648 // Eliminate copied source object extra fields that do not exist in target object
6649 $new_array_options = array();
6650 foreach ($this->array_options as $key => $value) {
6651 if (in_array(substr($key, 8), array_keys($target_extrafields))) { // We remove the 'options_' from $key for test
6652 $new_array_options[$key] = $value;
6653 } elseif (in_array($key, array_keys($target_extrafields))) { // We test on $key that does not contain the 'options_' prefix
6654 $new_array_options['options_'.$key] = $value;
6655 }
6656 }
6657
6658 foreach ($new_array_options as $key => $value) {
6659 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6660 $attributeType = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
6661 $attributeLabel = $langs->transnoentities($extrafields->attributes[$this->table_element]['label'][$attributeKey]);
6662 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
6663 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
6664 $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
6665 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$attributeKey];
6666
6667 // If we clone, we have to clean unique extrafields to prevent duplicates.
6668 // This behaviour can be prevented by external code by changing $this->context['createfromclone'] value in createFrom hook
6669 if (!empty($this->context['createfromclone']) && $this->context['createfromclone'] == 'createfromclone' && !empty($attributeUnique)) {
6670 $new_array_options[$key] = null;
6671 }
6672
6673 // If we create product combination, we have to clean unique extrafields to prevent duplicates.
6674 // This behaviour can be prevented by external code by changing $this->context['createproductcombination'] value in hook
6675 if (!empty($this->context['createproductcombination']) && $this->context['createproductcombination'] == 'createproductcombination' && !empty($attributeUnique)) {
6676 $new_array_options[$key] = null;
6677 }
6678
6679 // Similar code than into insertExtraFields
6680 if ($attributeRequired) {
6681 $v = $this->array_options[$key];
6682 if (ExtraFields::isEmptyValue($v, $attributeType)) {
6683 $langs->load("errors");
6684 dol_syslog("Mandatory field '".$key."' is empty during create and set to required into definition of extrafields");
6685 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6686 return -1;
6687 }
6688 }
6689
6690 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6691 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6692
6693 if (!empty($attrfieldcomputed)) {
6694 if (getDolGlobalString('MAIN_STORE_COMPUTED_EXTRAFIELDS')) {
6695 $value = dol_eval($attrfieldcomputed, 1, 0, '2');
6696 dol_syslog($langs->trans("Extrafieldcomputed")." on ".$attributeLabel."(".$value.")", LOG_DEBUG);
6697 $new_array_options[$key] = $value;
6698 } else {
6699 $new_array_options[$key] = null;
6700 }
6701 }
6702
6703 switch ($attributeType) {
6704 case 'int':
6705 case 'duration':
6706 case 'stars':
6707 if (!is_numeric($value) && $value != '') {
6708 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6709 return -1;
6710 } elseif ($value === '' || $value === false || $value === null) {
6711 $new_array_options[$key] = null;
6712 }
6713 break;
6714 case 'boolean':
6715 if ($value === '' || $value === false || $value === null) {
6716 $new_array_options[$key] = null;
6717 }
6718 break;
6719 case 'price':
6720 case 'double':
6721 $value = price2num($value);
6722 if (!is_numeric($value) && $value != '') {
6723 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." for ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6724 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6725 return -1;
6726 } elseif ($value == '') {
6727 $value = null;
6728 }
6729 //dol_syslog("double value"." on ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6730 $new_array_options[$key] = $value;
6731 break;
6732 /*case 'select': // Not required, we chose value='0' for undefined values
6733 if ($value=='-1')
6734 {
6735 $this->array_options[$key] = null;
6736 }
6737 break;*/
6738 case 'password':
6739 $algo = '';
6740 if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
6741 // If there is an encryption choice, we use it to encrypt data before insert
6742 $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
6743 $algo = reset($tmparrays);
6744 if ($algo != '') {
6745 //global $action; // $action may be 'create', 'update', 'update_extras'...
6746 //var_dump($action);
6747 //var_dump($this->oldcopy);exit;
6748 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
6749 //var_dump('algo='.$algo.' '.$this->oldcopy->array_options[$key].' -> '.$this->array_options[$key]);
6750 if (isset($this->oldcopy->array_options[$key]) && $this->array_options[$key] == $this->oldcopy->array_options[$key]) {
6751 // If old value encrypted in database is same than submitted new value, it means we don't change it, so we don't update.
6752 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
6753 if (!preg_match('/^dolcrypt:/', $this->array_options[$key])) {
6754 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6755 } else {
6756 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6757 }
6758 } else {
6759 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6760 }
6761 } else {
6762 // If value has changed
6763 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
6764 if (!preg_match('/^dolcrypt:/', $this->array_options[$key])) {
6765 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6766 } else {
6767 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6768 }
6769 } else {
6770 $new_array_options[$key] = dol_hash($this->array_options[$key], $algo);
6771 }
6772 }
6773 } else {
6774 //var_dump('jjj'.$algo.' '.$this->oldcopy->array_options[$key].' -> '.$this->array_options[$key]);
6775 // If this->oldcopy is not defined, we can't know if we change attribute or not, so we must keep value
6776 if ($algo == 'dolcrypt' && !preg_match('/^dolcrypt:/', $this->array_options[$key])) { // dolibarr reversible encryption
6777 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6778 } else {
6779 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6780 }
6781 }
6782 } else {
6783 // No encryption
6784 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6785 }
6786 } else { // Common usage
6787 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6788 }
6789 break;
6790 case 'date':
6791 case 'datetime':
6792 // If data is a string instead of a timestamp, we convert it
6793 if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6794 $this->array_options[$key] = strtotime($this->array_options[$key]);
6795 }
6796 $new_array_options[$key] = $this->db->idate($this->array_options[$key]);
6797 break;
6798 case 'datetimegmt':
6799 // If data is a string instead of a timestamp, we convert it
6800 if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6801 $this->array_options[$key] = strtotime($this->array_options[$key]);
6802 }
6803 $new_array_options[$key] = $this->db->idate($this->array_options[$key], 'gmt');
6804 break;
6805 case 'link':
6806 if ($value === '' || $value === false || $value === null) {
6807 $new_array_options[$key] = null;
6808 break;
6809 }
6810 $param_list = array_keys($attributeParam['options']);
6811 // 0 : ObjectName
6812 // 1 : classPath
6813 $InfoFieldList = explode(":", $param_list[0]);
6814 dol_include_once($InfoFieldList[1]);
6815 if ($InfoFieldList[0] && class_exists($InfoFieldList[0])) {
6816 if ($value == '-1') { // -1 is key for no defined in combo list of objects
6817 $new_array_options[$key] = '';
6818 } elseif ($value) {
6819 $object = new $InfoFieldList[0]($this->db);
6820 '@phan-var-force CommonObject $object';
6821 if (is_numeric($value)) {
6822 $res = $object->fetch($value); // Common case
6823 } else {
6824 $res = $object->fetch(0, $value); // For compatibility
6825 }
6826
6827 if ($res > 0) {
6828 $new_array_options[$key] = $object->id;
6829 } else {
6830 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
6831 return -1;
6832 }
6833 }
6834 } else {
6835 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6836 }
6837 break;
6838 case 'checkbox':
6839 case 'chkbxlst':
6840 if (is_array($this->array_options[$key])) {
6841 $new_array_options[$key] = implode(',', $this->array_options[$key]);
6842 } else {
6843 $new_array_options[$key] = $this->array_options[$key];
6844 }
6845 break;
6846 }
6847 }
6848
6849 $this->db->begin();
6850
6851 $table_element = $this->table_element;
6852 if ($table_element == 'categorie') {
6853 $table_element = 'categories'; // For compatibility
6854 }
6855
6856 dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG);
6857
6858 $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6859 $this->db->query($sql_del);
6860
6861 $sql = "INSERT INTO ".$this->db->prefix().$table_element."_extrafields (fk_object";
6862 foreach ($new_array_options as $key => $value) {
6863 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6864 // Add field of attribute
6865 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator
6866 $sql .= ",".$attributeKey;
6867 }
6868 }
6869 // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6870 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6871 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6872 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6873 $sql .= ",".$tmpkey;
6874 }
6875 }
6876 }
6877 $sql .= ") VALUES (".$this->id;
6878
6879 foreach ($new_array_options as $key => $value) {
6880 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6881 // Add field of attribute
6882 if (!in_array($extrafields->attributes[$this->table_element]['type'][$attributeKey], ['separate', 'point', 'multipts', 'linestrg', 'polygon'])) { // Only for other type than separator)
6883 if ($new_array_options[$key] != '' || $new_array_options[$key] == '0') {
6884 $sql .= ",'".$this->db->escape($new_array_options[$key])."'";
6885 } else {
6886 $sql .= ",null";
6887 }
6888 }
6889 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] == 'point') { // for point type
6890 if (!empty($new_array_options[$key])) {
6891 if (!preg_match('/error/i', $new_array_options[$key])) {
6892 // Text must be a WKT string, so "POINT(15 20)"
6893 $sql .= ",ST_PointFromText('".$this->db->escape($new_array_options[$key])."')";
6894 } else {
6895 dol_syslog("Bad syntax string for point ".$new_array_options[$key]." to generate SQL request", LOG_WARNING);
6896 $sql .= ",null";
6897 }
6898 } else {
6899 $sql .= ",null";
6900 }
6901 }
6902 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] == 'multipts') { // for point type
6903 if (!empty($new_array_options[$key])) {
6904 if (!preg_match('/error/i', $new_array_options[$key])) {
6905 // Text must be a WKT string, so "MULTIPOINT(0 0, 20 20, 60 60)"
6906 $sql .= ",ST_MultiPointFromText('".$this->db->escape($new_array_options[$key])."')";
6907 } else {
6908 dol_syslog("Bad syntax string for multipoint ".$new_array_options[$key]." to generate SQL request", LOG_WARNING);
6909 $sql .= ",null";
6910 }
6911 } else {
6912 $sql .= ",null";
6913 }
6914 }
6915 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] == 'linestrg') { // for linestring type
6916 if (!empty($new_array_options[$key])) {
6917 if (!preg_match('/error/i', $new_array_options[$key])) {
6918 // Text must be a WKT string, so "LINESTRING(0 0, 10 10, 20 25, 50 60)"
6919 $sql .= ",ST_LineFromText('".$this->db->escape($new_array_options[$key])."')";
6920 } else {
6921 dol_syslog("Bad syntax string for line ".$new_array_options[$key]." to generate SQL request", LOG_WARNING);
6922 $sql .= ",null";
6923 }
6924 } else {
6925 $sql .= ",null";
6926 }
6927 }
6928 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] == 'polygon') { // for polygon type
6929 if (!empty($new_array_options[$key])) {
6930 if (!preg_match('/error/i', $new_array_options[$key])) {
6931 // Text must be a WKT string, so "POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7, 5 5))"
6932 $sql .= ",ST_PolyFromText('".$this->db->escape($new_array_options[$key])."')";
6933 } else {
6934 dol_syslog("Bad syntax string for polygon ".$new_array_options[$key]." to generate SQL request", LOG_WARNING);
6935 $sql .= ",null";
6936 }
6937 } else {
6938 $sql .= ",null";
6939 }
6940 }
6941 }
6942 // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6943 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6944 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6945 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6946 if (in_array($tmpval, array('int', 'double', 'price'))) {
6947 $sql .= ", 0";
6948 } else {
6949 $sql .= ", ''";
6950 }
6951 }
6952 }
6953 }
6954
6955 $sql .= ")";
6956
6957 $resql = $this->db->query($sql);
6958 if (!$resql) {
6959 $this->error = $this->db->lasterror();
6960 $error++;
6961 }
6962
6963 if (!$error && $trigger) {
6964 // Call trigger
6965 $this->context = array('extrafieldaddupdate' => 1);
6966 $result = $this->call_trigger($trigger, $userused);
6967 if ($result < 0) {
6968 $error++;
6969 }
6970 // End call trigger
6971 }
6972
6973 if ($error) {
6974 $this->db->rollback();
6975 return -1;
6976 } else {
6977 $this->db->commit();
6978 return 1;
6979 }
6980 } else {
6981 return 0;
6982 }
6983 }
6984
6995 public function insertExtraLanguages($trigger = '', $userused = null)
6996 {
6997 global $conf, $langs, $user;
6998
6999 if (empty($userused)) {
7000 $userused = $user;
7001 }
7002
7003 $error = 0;
7004
7005 if (getDolGlobalString('MAIN_EXTRALANGUAGES_DISABLED')) {
7006 return 0; // For avoid conflicts if trigger used
7007 }
7008
7009 if (is_array($this->array_languages)) {
7010 $new_array_languages = $this->array_languages;
7011
7012 foreach ($new_array_languages as $key => $value) {
7013 $attributeKey = $key;
7014 $attributeType = $this->fields[$attributeKey]['type'];
7015 $attributeLabel = $this->fields[$attributeKey]['label'];
7016
7017 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
7018 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
7019
7020 switch ($attributeType) {
7021 case 'int':
7022 if (is_array($value) || (!is_numeric($value) && $value != '')) {
7023 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
7024 return -1;
7025 } elseif ($value == '') { // @phan-suppress-current-line PhanTypeComparisonFromArray
7026 $new_array_languages[$key] = null;
7027 }
7028 break;
7029 case 'double':
7030 $value = price2num((string) $value);
7031 if (!is_numeric($value) && $value != '') {
7032 dol_syslog($langs->trans("ExtraLanguageHasWrongValue")." on ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
7033 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
7034 return -1;
7035 } elseif ($value == '') {
7036 $new_array_languages[$key] = null;
7037 } else {
7038 $new_array_languages[$key] = $value;
7039 }
7040 break;
7041 /*case 'select': // Not required, we chose value='0' for undefined values
7042 if ($value=='-1')
7043 {
7044 $this->array_options[$key] = null;
7045 }
7046 break;*/
7047 }
7048 }
7049
7050 $this->db->begin();
7051
7052 $table_element = $this->table_element;
7053 if ($table_element == 'categorie') { // TODO Rename table llx_categories_extrafields into llx_categorie_extrafields so we can remove this.
7054 $table_element = 'categories'; // For compatibility
7055 }
7056
7057 dol_syslog(get_class($this)."::insertExtraLanguages delete then insert", LOG_DEBUG);
7058
7059 foreach ($new_array_languages as $key => $langcodearray) { // $key = 'name', 'town', ...
7060 foreach ($langcodearray as $langcode => $value) {
7061 $sql_del = "DELETE FROM ".$this->db->prefix()."object_lang";
7062 $sql_del .= " WHERE fk_object = ".((int) $this->id)." AND property = '".$this->db->escape($key)."' AND type_object = '".$this->db->escape($table_element)."'";
7063 $sql_del .= " AND lang = '".$this->db->escape($langcode)."'";
7064 $this->db->query($sql_del);
7065
7066 if ($value !== '') {
7067 $sql = "INSERT INTO ".$this->db->prefix()."object_lang (fk_object, property, type_object, lang, value";
7068 $sql .= ") VALUES (".$this->id.", '".$this->db->escape($key)."', '".$this->db->escape($table_element)."', '".$this->db->escape($langcode)."', '".$this->db->escape($value)."'";
7069 $sql .= ")";
7070
7071 $resql = $this->db->query($sql);
7072 if (!$resql) {
7073 $this->error = $this->db->lasterror();
7074 $error++;
7075 break;
7076 }
7077 }
7078 }
7079 }
7080
7081 if (!$error && $trigger) {
7082 // Call trigger
7083 $this->context = array('extralanguagesaddupdate' => 1);
7084 $result = $this->call_trigger($trigger, $userused);
7085 if ($result < 0) {
7086 $error++;
7087 }
7088 // End call trigger
7089 }
7090
7091 if ($error) {
7092 $this->db->rollback();
7093 return -1;
7094 } else {
7095 $this->db->commit();
7096 return 1;
7097 }
7098 } else {
7099 return 0;
7100 }
7101 }
7102
7113 public function updateExtraField($key, $trigger = null, $userused = null)
7114 {
7115 global $conf, $langs, $user, $hookmanager;
7116
7117 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
7118 return 0;
7119 }
7120
7121 if (empty($userused)) {
7122 $userused = $user;
7123 }
7124
7125 $error = 0;
7126
7127 if (!empty($this->array_options) && isset($this->array_options["options_".$key])) {
7128 // Check parameters
7129 $langs->load('admin');
7130 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
7131 $extrafields = new ExtraFields($this->db);
7132 $extrafields->fetch_name_optionals_label($this->table_element);
7133
7134 $value = $this->array_options["options_".$key];
7135
7136 $attributeKey = $key;
7137 $attributeType = $extrafields->attributes[$this->table_element]['type'][$key];
7138 $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key];
7139 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key];
7140 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
7141 $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
7142 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$key];
7143
7144 // Similar code than into insertExtraFields
7145 if ($attributeRequired) {
7146 $mandatorypb = false;
7147 if ($attributeType == 'link' && $this->array_options["options_".$key] == '-1') {
7148 $mandatorypb = true;
7149 }
7150 if ($this->array_options["options_".$key] === '') {
7151 $mandatorypb = true;
7152 }
7153 if ($mandatorypb) {
7154 $langs->load("errors");
7155 dol_syslog("Mandatory field 'options_".$key."' is empty during update and set to required into definition of extrafields");
7156 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
7157 return -1;
7158 }
7159 }
7160
7161 // $new_array_options will be used for direct update, so must contains formatted data for the UPDATE.
7162 $new_array_options = $this->array_options;
7163
7164 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
7165 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
7166 if (!empty($attrfieldcomputed)) {
7167 if (getDolGlobalString('MAIN_STORE_COMPUTED_EXTRAFIELDS')) {
7168 $value = dol_eval($attrfieldcomputed, 1, 0, '2');
7169 dol_syslog($langs->trans("Extrafieldcomputed")." on ".$attributeLabel."(".$value.")", LOG_DEBUG);
7170
7171 $new_array_options["options_".$key] = $value;
7172
7173 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7174 } else {
7175 $new_array_options["options_".$key] = null;
7176
7177 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7178 }
7179 }
7180
7181 switch ($attributeType) {
7182 case 'int':
7183 if (!is_numeric($value) && $value != '') {
7184 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
7185 return -1;
7186 } elseif ($value === '') {
7187 $new_array_options["options_".$key] = null;
7188
7189 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7190 }
7191 break;
7192 case 'price':
7193 case 'double':
7194 $value = price2num($value);
7195 if (!is_numeric($value) && $value != '') {
7196 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." on ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
7197 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
7198 return -1;
7199 } elseif ($value === '') {
7200 $value = null;
7201 }
7202 //dol_syslog("double value"." on ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
7203 $new_array_options["options_".$key] = $value;
7204
7205 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7206 break;
7207 /*case 'select': // Not required, we chose value='0' for undefined values
7208 if ($value=='-1')
7209 {
7210 $new_array_options["options_".$key] = $value;
7211
7212 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7213 }
7214 break;*/
7215 case 'password':
7216 $algo = '';
7217 if ($this->array_options["options_".$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
7218 // If there is an encryption choice, we use it to encrypt data before insert
7219 $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
7220 $algo = reset($tmparrays);
7221 if ($algo != '') {
7222 //global $action; // $action may be 'create', 'update', 'update_extras'...
7223 //var_dump($action);
7224 //var_dump($this->oldcopy);exit;
7225 //var_dump($key.' '.$this->array_options["options_".$key].' '.$algo);
7226 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
7227 //var_dump($this->oldcopy->array_options["options_".$key]); var_dump($this->array_options["options_".$key]);
7228 if (isset($this->oldcopy->array_options["options_".$key]) && $this->array_options["options_".$key] == $this->oldcopy->array_options["options_".$key]) { // If old value encrypted in database is same than submitted new value, it means we don't change it, so we don't update.
7229 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
7230 if (!preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) {
7231 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]); // warning, must be called when on the master
7232 } else {
7233 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7234 }
7235 } else {
7236 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7237 }
7238 } else {
7239 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
7240 if (!preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) {
7241 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]);
7242 } else {
7243 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7244 }
7245 } else {
7246 $new_array_options["options_".$key] = dol_hash($this->array_options["options_".$key], $algo);
7247 }
7248 }
7249 } else {
7250 if ($algo == 'dolcrypt' && !preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) { // dolibarr reversible encryption
7251 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]); // warning, must be called when on the master
7252 } else {
7253 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7254 }
7255 }
7256 } else {
7257 // No encryption
7258 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7259 }
7260 } else { // Common usage
7261 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7262 }
7263
7264 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7265 break;
7266 case 'date':
7267 case 'datetime':
7268 if (empty($this->array_options["options_".$key])) {
7269 $new_array_options["options_".$key] = null;
7270
7271 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7272 } else {
7273 $new_array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key]);
7274 }
7275 break;
7276 case 'datetimegmt':
7277 if (empty($this->array_options["options_".$key])) {
7278 $new_array_options["options_".$key] = null;
7279
7280 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7281 } else {
7282 $new_array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key], 'gmt');
7283 }
7284 break;
7285 case 'boolean':
7286 if (empty($this->array_options["options_".$key])) {
7287 $new_array_options["options_".$key] = null;
7288
7289 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7290 }
7291 break;
7292 case 'link':
7293 if ($this->array_options["options_".$key] === '') {
7294 $new_array_options["options_".$key] = null;
7295
7296 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7297 }
7298 break;
7299 /*
7300 case 'link':
7301 $param_list = array_keys($attributeParam['options']);
7302 // 0 : ObjectName
7303 // 1 : classPath
7304 $InfoFieldList = explode(":", $param_list[0]);
7305 dol_include_once($InfoFieldList[1]);
7306 if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
7307 {
7308 if ($value == '-1') // -1 is key for no defined in combo list of objects
7309 {
7310 $new_array_options[$key] = '';
7311 } elseif ($value) {
7312 $object = new $InfoFieldList[0]($this->db);
7313 if (is_numeric($value)) $res = $object->fetch($value); // Common case
7314 else $res = $object->fetch(0, $value); // For compatibility
7315
7316 if ($res > 0) $new_array_options[$key] = $object->id;
7317 else {
7318 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
7319 $this->db->rollback();
7320 return -1;
7321 }
7322 }
7323 } else {
7324 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
7325 }
7326 break;
7327 */
7328 case 'checkbox':
7329 case 'chkbxlst':
7330 $new_array_options = array();
7331 if (is_array($this->array_options["options_".$key])) {
7332 $new_array_options["options_".$key] = implode(',', $this->array_options["options_".$key]);
7333 } else {
7334 $new_array_options["options_".$key] = $this->array_options["options_".$key];
7335 }
7336
7337 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7338 break;
7339 }
7340
7341 $this->db->begin();
7342
7343 $linealreadyfound = 0;
7344
7345 // 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)
7346 $table_element = $this->table_element;
7347 if ($table_element == 'categorie') { // TODO Rename table llx_categories_extrafields into llx_categorie_extrafields so we can remove this.
7348 $table_element = 'categories'; // For compatibility
7349 }
7350
7351 $sql = "SELECT COUNT(rowid) as nb FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
7352 $resql = $this->db->query($sql);
7353 if ($resql) {
7354 $tmpobj = $this->db->fetch_object($resql);
7355 if ($tmpobj) {
7356 $linealreadyfound = $tmpobj->nb;
7357 }
7358 }
7359
7360 //var_dump('linealreadyfound='.$linealreadyfound.' sql='.$sql); exit;
7361 if ($linealreadyfound) {
7362 if ($this->array_options["options_".$key] === null) {
7363 $sql = "UPDATE ".$this->db->prefix().$table_element."_extrafields SET ".$key." = null";
7364 } else {
7365 $sql = "UPDATE ".$this->db->prefix().$table_element."_extrafields SET ".$key." = '".$this->db->escape($new_array_options["options_".$key])."'";
7366 }
7367 $sql .= " WHERE fk_object = ".((int) $this->id);
7368
7369 $resql = $this->db->query($sql);
7370 if (!$resql) {
7371 $error++;
7372 $this->error = $this->db->lasterror();
7373 }
7374 } else {
7375 $result = $this->insertExtraFields('', $user);
7376 if ($result < 0) {
7377 $error++;
7378 }
7379 }
7380
7381 if (!$error) {
7382 $parameters = array('key' => $key);
7383 global $action;
7384 $reshook = $hookmanager->executeHooks('updateExtraFieldBeforeCommit', $parameters, $this, $action);
7385 if ($reshook < 0) {
7386 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
7387 }
7388 }
7389
7390 if (!$error && $trigger) {
7391 // Call trigger
7392 $this->context = array('extrafieldupdate' => 1);
7393 $result = $this->call_trigger($trigger, $userused);
7394 if ($result < 0) {
7395 $error++;
7396 }
7397 // End call trigger
7398 }
7399
7400 if ($error) {
7401 dol_syslog(__METHOD__.$this->error, LOG_ERR);
7402 $this->db->rollback();
7403 return -1;
7404 } else {
7405 $this->db->commit();
7406 return 1;
7407 }
7408 } else {
7409 return 0;
7410 }
7411 }
7412
7419 public function getExtraField($key)
7420 {
7421 return $this->array_options['options_'.$key] ?? null;
7422 }
7423
7431 public function setExtraField($key, $value)
7432 {
7433 $this->array_options['options_'.$key] = $value;
7434 }
7435
7446 public function updateExtraLanguages($key, $trigger = null, $userused = null)
7447 {
7448 global $conf, $langs, $user;
7449
7450 if (empty($userused)) {
7451 $userused = $user;
7452 }
7453
7454 $error = 0;
7455
7456 if (getDolGlobalString('MAIN_EXTRALANGUAGES_DISABLED')) {
7457 return 0; // For avoid conflicts if trigger used
7458 }
7459
7460 return 0;
7461 }
7462
7463
7479 public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0, $nonewbutton = 0)
7480 {
7481 global $conf, $langs, $form;
7482
7483 // TODO pass the current object as a parameter to give more flexibility (like disable showing input for extra fields when canAlwaysBeEdited is false and $object->status is not draft...)
7484
7485 if (!is_object($form)) {
7486 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
7487 $form = new Form($this->db);
7488 }
7489
7490 if (!empty($this->fields)) {
7491 $val = $this->fields[$key];
7492 }
7493
7494 // Validation tests and output
7495 $fieldValidationErrorMsg = '';
7496 $validationClass = '';
7497 $fieldValidationErrorMsg = $this->getFieldError($key);
7498 if (!empty($fieldValidationErrorMsg)) {
7499 $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
7500 } else {
7501 $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
7502 }
7503
7504 //$valuemultiselectinput = array();
7505 $out = '';
7506 $type = '';
7507 $isDependList = 0;
7508 $param = array();
7509 $param['options'] = array();
7510 $reg = array();
7511 // @phan-suppress-next-line PhanTypeMismatchProperty
7512 $size = !empty($this->fields[$key]['size']) ? $this->fields[$key]['size'] : 0;
7513 // Because we work on extrafields
7514 if (preg_match('/^(integer|link):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7515 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
7516 $type = 'link';
7517 } elseif (preg_match('/^(integer|link):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7518 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7519 $type = 'link';
7520 } elseif (preg_match('/^(integer|link):(.*):(.*)/i', $val['type'], $reg)) {
7521 $param['options'] = array($reg[2].':'.$reg[3] => 'N');
7522 $type = 'link';
7523 } elseif (preg_match('/^(sellist):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7524 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
7525 $type = 'sellist';
7526 } elseif (preg_match('/^(sellist):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7527 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7528 $type = 'sellist';
7529 } elseif (preg_match('/^(sellist):(.*):(.*)/i', $val['type'], $reg)) {
7530 $param['options'] = array($reg[2].':'.$reg[3] => 'N');
7531 $type = 'sellist';
7532 } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
7533 $param['options'] = array($reg[1] => 'N');
7534 $type = 'chkbxlst';
7535 } elseif (preg_match('/varchar\‍((\d+)\‍)/', $val['type'], $reg)) {
7536 $param['options'] = array();
7537 $type = 'varchar';
7538 $size = $reg[1];
7539 } elseif (preg_match('/varchar/', $val['type'])) {
7540 $param['options'] = array();
7541 $type = 'varchar';
7542 } elseif (preg_match('/stars\‍((\d+)\‍)/', $val['type'], $reg)) {
7543 $param['options'] = array();
7544 $type = 'stars';
7545 $size = $reg[1];
7546 } else {
7547 $param['options'] = array();
7548 $type = $this->fields[$key]['type'];
7549 }
7550 //var_dump($type); var_dump($param['options']);
7551
7552 // Special case that force options and type ($type can be integer, varchar, ...)
7553 if (!empty($this->fields[$key]['arrayofkeyval']) && is_array($this->fields[$key]['arrayofkeyval'])) {
7554 $param['options'] = $this->fields[$key]['arrayofkeyval'];
7555 // Special case that prevent to force $type to have multiple input @phan-suppress-next-line PhanTypeMismatchProperty
7556 if (empty($this->fields[$key]['multiinput'])) {
7557 $type = (($this->fields[$key]['type'] == 'checkbox') ? $this->fields[$key]['type'] : 'select');
7558 }
7559 }
7560
7561 $label = $this->fields[$key]['label'];
7562 //$elementtype=$this->fields[$key]['elementtype']; // Seems not used
7563 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7564 $default = (!empty($this->fields[$key]['default']) ? $this->fields[$key]['default'] : '');
7565 // @phan-suppress-next-line PhanTypeMismatchProperty
7566 $computed = (!empty($this->fields[$key]['computed']) ? $this->fields[$key]['computed'] : '');
7567 // @phan-suppress-next-line PhanTypeMismatchProperty
7568 $unique = (!empty($this->fields[$key]['unique']) ? $this->fields[$key]['unique'] : 0);
7569 // @phan-suppress-next-line PhanTypeMismatchProperty
7570 $required = (!empty($this->fields[$key]['required']) ? $this->fields[$key]['required'] : 0);
7571 // @phan-suppress-next-line PhanTypeMismatchProperty
7572 $autofocusoncreate = (!empty($this->fields[$key]['autofocusoncreate']) ? $this->fields[$key]['autofocusoncreate'] : 0);
7573 // @phan-suppress-next-line PhanTypeMismatchProperty
7574 $placeholder = (!empty($this->fields[$key]['placeholder']) ? $this->fields[$key]['placeholder'] : 0);
7575
7576 // @phan-suppress-next-line PhanTypeMismatchProperty
7577 $langfile = (!empty($this->fields[$key]['langfile']) ? $this->fields[$key]['langfile'] : '');
7578 // @phan-suppress-next-line PhanTypeMismatchProperty
7579 $list = (!empty($this->fields[$key]['list']) ? $this->fields[$key]['list'] : 0);
7580 $hidden = (in_array(abs($this->fields[$key]['visible']), array(0, 2)) ? 1 : 0);
7581
7582 $objectid = $this->id;
7583
7584 if ($computed) {
7585 if (!preg_match('/^search_/', $keyprefix)) {
7586 return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
7587 } else {
7588 return '';
7589 }
7590 }
7591
7592 // Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine
7593 if (empty($morecss) && !empty($val['css'])) {
7594 $morecss = $val['css'];
7595 } elseif (empty($morecss)) {
7596 if ($type == 'date') {
7597 $morecss = 'minwidth100imp';
7598 } elseif ($type == 'datetime' || $type == 'link') { // link means an foreign key to another primary id
7599 $morecss = 'minwidth200imp';
7600 } elseif (in_array($type, array('int', 'integer', 'price')) || preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', (string) $type)) {
7601 $morecss = 'maxwidth75';
7602 } elseif ($type == 'url') {
7603 $morecss = 'minwidth400';
7604 } elseif ($type == 'boolean') {
7605 $morecss = '';
7606 } else {
7607 if (is_numeric($size) && round((float) $size) < 12) {
7608 $morecss = 'minwidth100';
7609 } elseif (is_numeric($size) && round((float) $size) <= 48) {
7610 $morecss = 'minwidth200';
7611 } else {
7612 $morecss = 'minwidth400';
7613 }
7614 }
7615 }
7616
7617 // Add validation state class
7618 if (!empty($validationClass)) {
7619 $morecss .= $validationClass;
7620 }
7621
7622 if (in_array($type, array('date'))) {
7623 $tmp = explode(',', $size);
7624 $newsize = $tmp[0];
7625 $showtime = 0;
7626
7627 // Do not show current date when field not required (see selectDate() method)
7628 if (!$required && $value == '') {
7629 $value = '-1';
7630 }
7631
7632 // TODO Must also support $moreparam
7633 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
7634 } elseif (in_array($type, array('datetime'))) {
7635 $tmp = explode(',', $size);
7636 $newsize = $tmp[0];
7637 $showtime = 1;
7638
7639 // Do not show current date when field not required (see selectDate() method)
7640 if (!$required && $value == '') {
7641 $value = '-1';
7642 }
7643
7644 // TODO Must also support $moreparam
7645 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
7646 } elseif (in_array($type, array('duration'))) {
7647 $out = $form->select_duration($keyprefix.$key.$keysuffix, $value, 0, 'text', 0, 1);
7648 } elseif (in_array($type, array('int', 'integer'))) {
7649 $tmp = explode(',', $size);
7650 $newsize = $tmp[0];
7651 $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' : '').'>';
7652 } elseif (in_array($type, array('real'))) {
7653 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7654 } elseif (preg_match('/varchar/', (string) $type)) {
7655 $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 : '').($placeholder ? ' placeholder="'.dolPrintHTMLForAttribute($placeholder).'"' : '').($autofocusoncreate ? ' autofocus' : '').'>';
7656 } elseif (in_array($type, array('email', 'mail', 'phone', 'url', 'ip'))) {
7657 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7658 } elseif (preg_match('/^text/', (string) $type)) {
7659 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7660 if (!empty($param['options'])) {
7661 // If the textarea field has a list of arrayofkeyval into its definition, we suggest a combo with possible values to fill the textarea.
7662 //var_dump($param['options']);
7663 $out .= $form->selectarray($keyprefix.$key.$keysuffix."_multiinput", $param['options'], '', 1, 0, 0, "flat maxwidthonphone".$morecss);
7664 $out .= '<input id="'.$keyprefix.$key.$keysuffix.'_multiinputadd" type="button" class="button" value="'.$langs->trans("Add").'">';
7665 $out .= "<script>";
7666 $out .= '
7667 function handlemultiinputdisabling(htmlname){
7668 console.log("We handle the disabling of used options for "+htmlname+"_multiinput");
7669 multiinput = $("#"+htmlname+"_multiinput");
7670 multiinput.find("option").each(function(){
7671 tmpval = $("#"+htmlname).val();
7672 tmpvalarray = tmpval.split("\n");
7673 valtotest = $(this).val();
7674 if(tmpvalarray.includes(valtotest)){
7675 $(this).prop("disabled",true);
7676 } else {
7677 if($(this).prop("disabled") == true){
7678 console.log(valtotest)
7679 $(this).prop("disabled", false);
7680 }
7681 }
7682 });
7683 }
7684
7685 $(document).ready(function () {
7686 $("#'.$keyprefix.$key.$keysuffix.'_multiinputadd").on("click",function() {
7687 tmpval = $("#'.$keyprefix.$key.$keysuffix.'").val();
7688 tmpvalarray = tmpval.split(",");
7689 valtotest = $("#'.$keyprefix.$key.$keysuffix.'_multiinput").val();
7690 if(valtotest != -1 && !tmpvalarray.includes(valtotest)){
7691 console.log("We add the selected value to the text area '.$keyprefix.$key.$keysuffix.'");
7692 if(tmpval == ""){
7693 tmpval = valtotest;
7694 } else {
7695 tmpval = tmpval + "\n" + valtotest;
7696 }
7697 $("#'.$keyprefix.$key.$keysuffix.'").val(tmpval);
7698 handlemultiinputdisabling("'.$keyprefix.$key.$keysuffix.'");
7699 $("#'.$keyprefix.$key.$keysuffix.'_multiinput").val(-1);
7700 } else {
7701 console.log("We add nothing the text area '.$keyprefix.$key.$keysuffix.'");
7702 }
7703 });
7704 $("#'.$keyprefix.$key.$keysuffix.'").on("change",function(){
7705 handlemultiinputdisabling("'.$keyprefix.$key.$keysuffix.'");
7706 });
7707 handlemultiinputdisabling("'.$keyprefix.$key.$keysuffix.'");
7708 })';
7709 $out .= "</script>";
7710 $value = str_replace(',', "\n", $value);
7711 }
7712 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7713 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
7714 $out .= (string) $doleditor->Create(1, '', true, '', '', '', $morecss);
7715 } else {
7716 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7717 }
7718 } elseif (preg_match('/^html/', (string) $type)) {
7719 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7720 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7721 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && getDolGlobalInt('FCKEDITOR_ENABLE_SOCIETE'), ROWS_5, '90%');
7722 $out = (string) $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
7723 } else {
7724 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7725 }
7726 } elseif ($type == 'boolean') {
7727 $checked = '';
7728 if (!empty($value)) {
7729 $checked = ' checked value="1" ';
7730 } else {
7731 $checked = ' value="1" ';
7732 }
7733 $out = '<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
7734 } elseif ($type == 'price') {
7735 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7736 $value = price($value);
7737 }
7738 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '.$langs->getCurrencySymbol($conf->currency);
7739 } elseif ($type == 'stars') {
7740 $out = '<input type="hidden" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7741 $out .= '<div class="star-selection" id="'.$keyprefix.$key.$keysuffix.'_selection">';
7742 $i = 1;
7743 while ($i <= $size) {
7744 $out .= '<span class="star" data-value="'.$i.'">'.img_picto('', 'fontawesome_star_fas').'</span>';
7745 $i++;
7746 }
7747 $out .= '</div>';
7748 $out .= '<script>
7749 jQuery(function($) {
7750 let container = $("#'.$keyprefix.$key.$keysuffix.'_selection");
7751 let selectedStars = parseInt($("#'.$keyprefix.$key.$keysuffix.'").val()) || 0;
7752 container.find(".star").each(function() {
7753 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
7754 });
7755 container.find(".star").on("mouseover", function() {
7756 let selectedStar = $(this).data("value");
7757 container.find(".star").each(function() {
7758 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
7759 });
7760 });
7761 container.on("mouseout", function() {
7762 container.find(".star").each(function() {
7763 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
7764 });
7765 });
7766 container.find(".star").off("click").on("click", function() {
7767 selectedStars = $(this).data("value");
7768 if (selectedStars === 1 && $("#'.$keyprefix.$key.$keysuffix.'").val() == 1) {
7769 selectedStars = 0;
7770 }
7771 $("#'.$keyprefix.$key.$keysuffix.'").val(selectedStars);
7772 container.find(".star").each(function() {
7773 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
7774 });
7775 });
7776 });
7777 </script>';
7778 } elseif (preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', (string) $type)) {
7779 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7780 $value = price($value);
7781 }
7782 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
7783 } elseif ($type == 'select') { // combo list
7784 $out = '';
7785 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
7786 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7787 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7788 }
7789
7790 $tmpselect = '';
7791 $nbchoice = 0;
7792
7793 foreach ($param['options'] as $keyb => $valb) {
7794 if ((string) $keyb == '') {
7795 continue;
7796 }
7797 if (strpos($valb, "|") !== false) {
7798 list($valb, $parent) = explode('|', $valb);
7799 }
7800 $nbchoice++;
7801 $tmpselect .= '<option value="'.$keyb.'"';
7802 $tmpselect .= (((string) $value == (string) $keyb) ? ' selected' : '');
7803 if (!empty($parent)) {
7804 $isDependList = 1;
7805 }
7806 $tmpselect .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
7807 $tmpselect .= '>'.$langs->trans($valb).'</option>';
7808 }
7809
7810 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7811 if ((!isset($this->fields[$key]['default'])) || empty($this->fields[$key]['notnull']) || ($this->fields[$key]['notnull'] != 1) || $nbchoice >= 2) {
7812 $out .= '<option value="0">&nbsp;</option>';
7813 }
7814 $out .= $tmpselect;
7815 $out .= '</select>';
7816 } elseif ($type == 'sellist') {
7817 $out = '';
7818 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
7819 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7820 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7821 }
7822
7823 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7824 if (is_array($param['options'])) {
7825 $tmpparamoptions = array_keys($param['options']);
7826 $paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
7827
7828 $InfoFieldList = explode(":", $paramoptions[0], 5);
7829 // 0 : tableName
7830 // 1 : label field name
7831 // 2 : key fields name (if different of rowid)
7832 // optional parameters...
7833 // 3 : key field parent (for dependent lists). How this is used ?
7834 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value. Or use USF on a second line separated by "\n".
7835 // 5 : string category type. This replace the filter.
7836 // 6 : ids categories list separated by comma for category root. This replace the filter.
7837 // 7 : sort field
7838
7839 // If there is filter
7840 if (! empty($InfoFieldList[4])) {
7841 $pos = 0;
7842 $parenthesisopen = 0;
7843 while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
7844 if (substr($InfoFieldList[4], $pos, 1) == '(') {
7845 $parenthesisopen++;
7846 }
7847 if (substr($InfoFieldList[4], $pos, 1) == ')') {
7848 $parenthesisopen--;
7849 }
7850 $pos++;
7851 }
7852 $tmpbefore = substr($InfoFieldList[4], 0, $pos);
7853 $tmpafter = substr($InfoFieldList[4], $pos + 1);
7854 //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
7855 $InfoFieldList[4] = $tmpbefore;
7856 if ($tmpafter !== '') {
7857 $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
7858 }
7859 //var_dump($InfoFieldList);
7860
7861 // Fix better compatibility with some old extrafield syntax filter "(field=123)"
7862 $reg = array();
7863 if (preg_match('/^\‍(?([a-z0-9]+)([=<>]+)(\d+)\‍)?$/i', $InfoFieldList[4], $reg)) {
7864 $InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
7865 }
7866
7867 //var_dump($InfoFieldList);
7868 }
7869
7870 //$Usf = empty($paramoptions[1]) ? '' :$paramoptions[1];
7871
7872 $parentName = '';
7873 $parentField = '';
7874
7875 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7876
7877 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7878 if (strpos($InfoFieldList[4], 'extra.') !== false) {
7879 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7880 } else {
7881 $keyList = $InfoFieldList[2].' as rowid';
7882 }
7883 }
7884 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7885 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7886 if (!empty($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra.') !== false) {
7887 $keyList .= ', main.'.$parentField;
7888 } else {
7889 $keyList .= ', '.$parentField;
7890 }
7891 }
7892
7893 $filter_categorie = false;
7894 if (count($InfoFieldList) > 5) {
7895 if ($InfoFieldList[0] == 'categorie') {
7896 $filter_categorie = true;
7897 }
7898 }
7899
7900 if (!$filter_categorie) {
7901 $fields_label = explode('|', $InfoFieldList[1]);
7902 if (is_array($fields_label)) {
7903 $keyList .= ', ';
7904 $keyList .= implode(', ', $fields_label);
7905 }
7906
7907 $sqlwhere = '';
7908 $sql = "SELECT " . $keyList;
7909 $sql .= " FROM " . $this->db->prefix() . $InfoFieldList[0];
7910
7911 if (!empty($InfoFieldList[4])) {
7912 // can use SELECT request
7913 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7914 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7915 }
7916
7917 // current object id can be use into filter
7918 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7919 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
7920 } else {
7921 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7922 }
7923
7924 // We have to join on extrafield table
7925 $errstr = '';
7926 if (strpos($InfoFieldList[4], 'extra') !== false) {
7927 $sql .= " as main, " . $this->db->sanitize($this->db->prefix() . $InfoFieldList[0]) . "_extrafields as extra";
7928 $sqlwhere .= " WHERE extra.fk_object = main." . $this->db->sanitize($InfoFieldList[2]);
7929 $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
7930 } else {
7931 $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
7932 }
7933 } else {
7934 $sqlwhere .= ' WHERE 1=1';
7935 }
7936
7937 // Add Usf filter on second line
7938 /*
7939 if ($Usf) {
7940 $errorstr = '';
7941 $sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
7942 if (!$errorstr) {
7943 $sqlwhere .= $sqlusf;
7944 } else {
7945 $sqlwhere .= " AND invalid_usf_filter_of_extrafield";
7946 }
7947 }
7948 */
7949
7950 // Some tables may have field, some other not. For the moment we disable it.
7951 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7952 $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7953 }
7954 $sql .= $sqlwhere;
7955
7956 // Note: $InfoFieldList can be 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter[:CategoryIdType[:CategoryIdList[:Sortfield]]]]]]'
7957 if (isset($InfoFieldList[7]) && preg_match('/^[a-z0-9_\-,]+$/i', $InfoFieldList[7])) {
7958 $sql .= " ORDER BY ".$this->db->escape($InfoFieldList[7]);
7959 } else {
7960 $sql .= " ORDER BY ".$this->db->sanitize(implode(', ', $fields_label));
7961 }
7962
7963 dol_syslog(get_class($this) . '::showInputField type=sellist', LOG_DEBUG);
7964 $resql = $this->db->query($sql);
7965 if ($resql) {
7966 $out .= '<option value="0">&nbsp;</option>';
7967 $num = $this->db->num_rows($resql);
7968 $i = 0;
7969 while ($i < $num) {
7970 $labeltoshow = '';
7971 $obj = $this->db->fetch_object($resql);
7972
7973 // Several field into label (eq table:code|libelle:rowid)
7974 $notrans = false;
7975 $fields_label = explode('|', $InfoFieldList[1]);
7976 if (count($fields_label) > 1) {
7977 $notrans = true;
7978 foreach ($fields_label as $field_toshow) {
7979 $labeltoshow .= $obj->$field_toshow . ' ';
7980 }
7981 } else {
7982 $labeltoshow = $obj->{$InfoFieldList[1]};
7983 }
7984 $labeltoshow = dol_trunc($labeltoshow, 45);
7985
7986 if ($value == $obj->rowid) {
7987 foreach ($fields_label as $field_toshow) {
7988 $translabel = $langs->trans($obj->$field_toshow);
7989 if ($translabel != $obj->$field_toshow) {
7990 $labeltoshow = dol_trunc($translabel) . ' ';
7991 } else {
7992 $labeltoshow = dol_trunc($obj->$field_toshow) . ' ';
7993 }
7994 }
7995 $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7996 } else {
7997 if (!$notrans) {
7998 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7999 if ($translabel != $obj->{$InfoFieldList[1]}) {
8000 $labeltoshow = dol_trunc($translabel, 18);
8001 } else {
8002 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]});
8003 }
8004 }
8005 if (empty($labeltoshow)) {
8006 $labeltoshow = '(not defined)';
8007 }
8008 if ($value == $obj->rowid) {
8009 $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
8010 }
8011
8012 if (!empty($InfoFieldList[3]) && $parentField) {
8013 $parent = $parentName . ':' . $obj->{$parentField};
8014 $isDependList = 1;
8015 }
8016
8017 $out .= '<option value="' . $obj->rowid . '"';
8018 $out .= ($value == $obj->rowid ? ' selected' : '');
8019 $out .= (!empty($parent) ? ' parent="' . $parent . '"' : '');
8020 $out .= '>' . $labeltoshow . '</option>';
8021 }
8022
8023 $i++;
8024 }
8025 $this->db->free($resql);
8026 } else {
8027 print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
8028 }
8029 } else {
8030 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
8031 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
8032 $out .= '<option value="0">&nbsp;</option>';
8033 foreach ($data as $data_key => $data_value) {
8034 $out .= '<option value="' . $data_key . '"';
8035 $out .= ($value == $data_key ? ' selected' : '');
8036 $out .= '>' . $data_value . '</option>';
8037 }
8038 }
8039 }
8040 $out .= '</select>';
8041 } elseif ($type == 'checkbox') {
8042 $value_arr = explode(',', $value);
8043 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ? null : $param['options']), $value_arr, 0, 0, $morecss, 0, '100%');
8044 } elseif ($type == 'radio') {
8045 $out = '';
8046 foreach ($param['options'] as $keyopt => $valopt) {
8047 $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
8048 $out .= ' value="'.$keyopt.'"';
8049 $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
8050 $out .= ($value == $keyopt ? 'checked' : '');
8051 $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$valopt.'</label><br>';
8052 }
8053 } elseif ($type == 'chkbxlst') {
8054 if (is_array($value)) {
8055 $value_arr = $value;
8056 } else {
8057 $value_arr = explode(',', $value);
8058 }
8059
8060 if (is_array($param['options'])) {
8061 $tmpparamoptions = array_keys($param['options']);
8062 $paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
8063
8064 $InfoFieldList = explode(":", $paramoptions[0], 5);
8065 // 0 : tableName
8066 // 1 : label field name
8067 // 2 : key fields name (if different of rowid)
8068 // optional parameters...
8069 // 3 : key field parent (for dependent lists). How this is used ?
8070 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value. Or use USF on a second line separated by "\n".
8071 // 5 : string category type. This replace the filter.
8072 // 6 : ids categories list separated by comma for category root. This replace the filter.
8073 // 7 : sort field
8074
8075 // If there is a filter
8076 if (! empty($InfoFieldList[4])) {
8077 $pos = 0;
8078 $parenthesisopen = 0;
8079 while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
8080 if (substr($InfoFieldList[4], $pos, 1) == '(') {
8081 $parenthesisopen++;
8082 }
8083 if (substr($InfoFieldList[4], $pos, 1) == ')') {
8084 $parenthesisopen--;
8085 }
8086 $pos++;
8087 }
8088 $tmpbefore = substr($InfoFieldList[4], 0, $pos);
8089 $tmpafter = substr($InfoFieldList[4], $pos + 1);
8090 //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
8091 $InfoFieldList[4] = $tmpbefore;
8092 if ($tmpafter !== '') {
8093 $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
8094 }
8095
8096 // Fix better compatibility with some old extrafield syntax filter "(field=123)"
8097 $reg = array();
8098 if (preg_match('/^\‍(?([a-z0-9]+)([=<>]+)(\d+)\‍)?$/i', $InfoFieldList[4], $reg)) {
8099 $InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
8100 }
8101
8102 //var_dump($InfoFieldList);
8103 }
8104
8105 //$Usf = empty($paramoptions[1]) ? '' :$paramoptions[1];
8106
8107 '@phan-var-force array{0:string,1:string,2:string,3:string,3:string,5:string,6:string} $InfoFieldList';
8108
8109 $parentName = '';
8110 $parentField = '';
8111
8112 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
8113
8114 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
8115 if (strpos($InfoFieldList[4], 'extra.') !== false) {
8116 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
8117 } else {
8118 $keyList = $InfoFieldList[2].' as rowid';
8119 }
8120 }
8121 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
8122 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
8123 if (!empty($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra.') !== false) {
8124 $keyList .= ', main.'.$parentField;
8125 } else {
8126 $keyList .= ', '.$parentField;
8127 }
8128 }
8129
8130 $filter_categorie = false;
8131 if (count($InfoFieldList) > 5) {
8132 if ($InfoFieldList[0] == 'categorie') {
8133 $filter_categorie = true;
8134 }
8135 }
8136
8137 // Common filter
8138 if (!$filter_categorie) {
8139 $fields_label = explode('|', $InfoFieldList[1]);
8140 if (is_array($fields_label)) {
8141 $keyList .= ', ';
8142 $keyList .= implode(', ', $fields_label);
8143 }
8144
8145 $sqlwhere = '';
8146 $sql = "SELECT " . $keyList;
8147 $sql .= ' FROM ' . $this->db->prefix() . $InfoFieldList[0];
8148
8149 if (!empty($InfoFieldList[4])) {
8150 // can use SELECT request
8151 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
8152 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
8153 }
8154
8155 // current object id can be use into filter
8156 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
8157 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
8158 } else {
8159 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
8160 }
8161
8162 // We have to join on extrafield table
8163 $errstr = '';
8164 if (strpos($InfoFieldList[4], 'extra') !== false) {
8165 $sql .= ' as main, ' . $this->db->sanitize($this->db->prefix() . $InfoFieldList[0]) . '_extrafields as extra';
8166 $sqlwhere .= " WHERE extra.fk_object = main." . $this->db->sanitize($InfoFieldList[2]);
8167 $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
8168 } else {
8169 $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
8170 }
8171 } else {
8172 $sqlwhere .= ' WHERE 1=1';
8173 }
8174
8175 // Add Usf filter on second line
8176 /*
8177 if ($Usf) {
8178 $errorstr = '';
8179 $sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
8180 if (!$errorstr) {
8181 $sqlwhere .= $sqlusf;
8182 } else {
8183 $sqlwhere .= " AND invalid_usf_filter_of_extrafield";
8184 }
8185 }
8186 */
8187
8188 // Some tables may have field, some other not. For the moment we disable it.
8189 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
8190 $sqlwhere .= " AND entity = " . ((int) $conf->entity);
8191 }
8192 // $sql.=preg_replace('/^ AND /','',$sqlwhere);
8193 // print $sql;
8194
8195 $sql .= $sqlwhere;
8196
8197 dol_syslog(get_class($this) . '::showInputField type=chkbxlst', LOG_DEBUG);
8198
8199 $resql = $this->db->query($sql);
8200 if ($resql) {
8201 $num = $this->db->num_rows($resql);
8202 $i = 0;
8203
8204 $data = array();
8205
8206 while ($i < $num) {
8207 $labeltoshow = '';
8208 $obj = $this->db->fetch_object($resql);
8209
8210 $notrans = false;
8211 // Several field into label (eq table:code|libelle:rowid)
8212 $fields_label = explode('|', $InfoFieldList[1]);
8213 if (count($fields_label) > 1) {
8214 $notrans = true;
8215 foreach ($fields_label as $field_toshow) {
8216 $labeltoshow .= $obj->$field_toshow . ' ';
8217 }
8218 } else {
8219 $labeltoshow = $obj->{$InfoFieldList[1]};
8220 }
8221 $labeltoshow = dol_trunc($labeltoshow, 45);
8222
8223 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8224 foreach ($fields_label as $field_toshow) {
8225 $translabel = $langs->trans($obj->$field_toshow);
8226 if ($translabel != $obj->$field_toshow) {
8227 $labeltoshow = dol_trunc($translabel, 18) . ' ';
8228 } else {
8229 $labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
8230 }
8231 }
8232
8233 $data[$obj->rowid] = $labeltoshow;
8234 } else {
8235 if (!$notrans) {
8236 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
8237 if ($translabel != $obj->{$InfoFieldList[1]}) {
8238 $labeltoshow = dol_trunc($translabel, 18);
8239 } else {
8240 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
8241 }
8242 }
8243 if (empty($labeltoshow)) {
8244 $labeltoshow = '(not defined)';
8245 }
8246
8247 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8248 $data[$obj->rowid] = $labeltoshow;
8249 }
8250
8251 if (!empty($InfoFieldList[3]) && $parentField) {
8252 $parent = $parentName . ':' . $obj->{$parentField};
8253 $isDependList = 1;
8254 }
8255
8256 $data[$obj->rowid] = $labeltoshow; // Warning: $obj->rowid is an alias and can be an int, but also a string ref.
8257 }
8258
8259 $i++;
8260 }
8261 $this->db->free($resql);
8262
8263 $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, 0, 0, $morecss, 0, '100%');
8264 } else {
8265 print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
8266 }
8267 } else {
8268 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
8269 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[(int) $InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
8270 $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, 0, 0, $morecss, 0, '100%');
8271 }
8272 }
8273 } elseif ($type == 'link') {
8274 // $param_list='ObjectName:classPath[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
8275 // Filter can contains some ':' inside.
8276 $param_list = array_keys($param['options']);
8277 $param_list_array = explode(':', $param_list[0], 4);
8278
8279 $showempty = (($required && $default != '') ? 0 : 1);
8280
8281 if (!preg_match('/search_/', $keyprefix)) {
8282 if (!empty($param_list_array[2])) { // If the entry into $fields is set to add a create button
8283 // @phan-suppress-next-line PhanTypeMismatchProperty
8284 if (!empty($this->fields[$key]['picto'])) {
8285 $morecss .= ' widthcentpercentminusxx';
8286 } else {
8287 $morecss .= ' widthcentpercentminusx';
8288 }
8289 } else {
8290 // @phan-suppress-next-line PhanTypeMismatchProperty
8291 if (!empty($this->fields[$key]['picto'])) {
8292 $morecss .= ' widthcentpercentminusx';
8293 }
8294 }
8295 }
8296 $objectfield = $this->element.($this->module ? '@'.$this->module : '').':'.$key.$keysuffix;
8297 $out = $form->selectForForms($param_list_array[0], $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, $moreparam, 0, (empty($val['disabled']) ? 0 : 1), '', $objectfield);
8298
8299 if (!empty($param_list_array[2])) { // If the entry into $fields is set, we must add a create button
8300 if ((!GETPOSTISSET('backtopage') || strpos(GETPOST('backtopage'), $_SERVER['PHP_SELF']) === 0) // // To avoid to open several times the 'Plus' button (we accept only one level)
8301 && empty($val['disabled']) && empty($nonewbutton)) { // and to avoid to show the button if the field is protected by a "disabled".
8302 list($class, $classfile) = explode(':', $param_list[0]);
8303 if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) {
8304 $url_path = dol_buildpath(dirname(dirname($classfile)).'/card.php', 1);
8305 } else {
8306 $url_path = dol_buildpath(dirname(dirname($classfile)).'/'.strtolower($class).'_card.php', 1);
8307 }
8308 $paramforthenewlink = '';
8309 $paramforthenewlink .= (GETPOSTISSET('action') ? '&action='.GETPOST('action', 'aZ09') : '');
8310 $paramforthenewlink .= (GETPOSTISSET('id') ? '&id='.GETPOSTINT('id') : '');
8311 $paramforthenewlink .= (GETPOSTISSET('origin') ? '&origin='.GETPOST('origin', 'aZ09') : '');
8312 $paramforthenewlink .= (GETPOSTISSET('originid') ? '&originid='.GETPOSTINT('originid') : '');
8313 $paramforthenewlink .= '&fk_'.strtolower($class).'=--IDFORBACKTOPAGE--';
8314 // TODO Add JavaScript code to add input fields already filled into $paramforthenewlink so we won't loose them when going back to main page
8315 $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>';
8316 }
8317 }
8318 } elseif ($type == 'password') {
8319 // If prefix is 'search_', field is used as a filter, we use a common text field.
8320 if ($keyprefix.$key.$keysuffix == 'pass_crypted') {
8321 $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="pass" id="pass" value="" '.($moreparam ? $moreparam : '').'>';
8322 $out .= '<input type="hidden" name="pass_crypted" id="pass_crypted" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
8323 } else {
8324 $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
8325 }
8326 } elseif ($type == 'array') {
8327 $newval = $val;
8328 $newval['type'] = 'varchar(256)';
8329
8330 $out = '';
8331 if (!empty($value)) {
8332 foreach ($value as $option) {
8333 $out .= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
8334 $out .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $morecss).'<br></span>';
8335 }
8336 }
8337 $out .= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
8338
8339 $newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
8340 $newInput .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $morecss).'<br></span>';
8341
8342 if (!empty($conf->use_javascript_ajax)) {
8343 $out .= '
8344 <script nonce="'.getNonce().'">
8345 $(document).ready(function() {
8346 $("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
8347 $("'.dol_escape_js($newInput).'").insertBefore(this);
8348 });
8349
8350 $(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
8351 $(this).parent().remove();
8352 });
8353 });
8354 </script>';
8355 }
8356 }
8357 if (!empty($hidden)) {
8358 $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
8359 }
8360
8361 if ($isDependList == 1) {
8362 $out .= $this->getJSListDependancies('_common');
8363 }
8364 /* Add comments
8365 if ($type == 'date') $out.=' (YYYY-MM-DD)';
8366 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
8367 */
8368
8369 // Display error message for field
8370 if (!empty($fieldValidationErrorMsg) && function_exists('getFieldErrorIcon')) {
8371 $out .= ' '.getFieldErrorIcon($fieldValidationErrorMsg);
8372 }
8373
8374 return $out;
8375 }
8376
8390 public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '')
8391 {
8392 global $conf, $langs, $form;
8393
8394 // TODO pass the current object as a parameter to give more flexibility (like disable ajax update when canAlwaysBeEdited is false and $object->status is not draft...)
8395
8396 if (!is_object($form)) {
8397 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
8398 $form = new Form($this->db);
8399 }
8400
8401 //$label = empty($val['label']) ? '' : $val['label'];
8402 $type = empty($val['type']) ? '' : $val['type'];
8403 $size = empty($val['css']) ? '' : $val['css'];
8404 $reg = array();
8405
8406 // Convert var to be able to share same code than showOutputField of extrafields
8407 if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
8408 $type = 'varchar'; // convert varchar(xx) int varchar
8409 $size = $reg[1];
8410 } elseif (preg_match('/varchar/', $type)) {
8411 $type = 'varchar'; // convert varchar(xx) int varchar
8412 }
8413 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8414 // @phan-suppress-next-line PhanTypeMismatchProperty
8415 if (empty($this->fields[$key]['multiinput'])) {
8416 $type = (($this->fields[$key]['type'] == 'checkbox') ? $this->fields[$key]['type'] : 'select');
8417 }
8418 }
8419 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8420 $type = 'link';
8421 }
8422
8423 $default = empty($val['default']) ? '' : $val['default'];
8424 $computed = empty($val['computed']) ? '' : $val['computed'];
8425 $unique = empty($val['unique']) ? '' : $val['unique'];
8426 $required = empty($val['required']) ? '' : $val['required'];
8427 $param = array();
8428 $param['options'] = array();
8429
8430 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8431 $param['options'] = $val['arrayofkeyval'];
8432 }
8433 if (preg_match('/^integer:([^:]*):([^:]*)/i', $val['type'], $reg)) { // ex: integer:User:user/class/user.class.php
8434 $type = 'link';
8435 $stringforoptions = $reg[1].':'.$reg[2];
8436 // Special case: Force addition of getnomurlparam1 to -1 for users
8437 if ($reg[1] == 'User') {
8438 $stringforoptions .= ':#getnomurlparam1=-1';
8439 }
8440 $param['options'] = array($stringforoptions => $stringforoptions);
8441 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
8442 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
8443 $type = 'sellist';
8444 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
8445 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
8446 $type = 'sellist';
8447 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
8448 $param['options'] = array($reg[1].':'.$reg[2] => 'N');
8449 $type = 'sellist';
8450 } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
8451 $param['options'] = array($reg[1] => 'N');
8452 $type = 'chkbxlst';
8453 } elseif (preg_match('/stars\‍((\d+)\‍)/', $val['type'], $reg)) {
8454 $param['options'] = array();
8455 $type = 'stars';
8456 $size = $reg[1];
8457 }
8458
8459 $langfile = empty($val['langfile']) ? '' : $val['langfile'];
8460 $list = (empty($val['list']) ? '' : $val['list']);
8461 $help = (empty($val['help']) ? '' : $val['help']);
8462 $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)
8463
8464 if ($hidden) {
8465 return '';
8466 }
8467
8468 // If field is a computed field, value must become result of compute
8469 if ($computed) {
8470 // Make the eval of compute string
8471 //var_dump($computed);
8472 $value = dol_eval($computed, 1, 0, '2');
8473 }
8474
8475 if (empty($morecss)) {
8476 if ($type == 'date') {
8477 $morecss = 'minwidth100imp';
8478 } elseif ($type == 'datetime' || $type == 'timestamp') {
8479 $morecss = 'minwidth200imp';
8480 } elseif (in_array($type, array('int', 'double', 'price'))) {
8481 $morecss = 'maxwidth75';
8482 } elseif ($type == 'url') {
8483 $morecss = 'minwidth400';
8484 } elseif ($type == 'boolean') {
8485 $morecss = '';
8486 } else {
8487 if (is_numeric($size) && round((float) $size) < 12) {
8488 $morecss = 'minwidth100';
8489 } elseif (is_numeric($size) && round((float) $size) <= 48) {
8490 $morecss = 'minwidth200';
8491 } else {
8492 $morecss = 'minwidth400';
8493 }
8494 }
8495 }
8496
8497 // Format output value differently according to properties of field
8498 if (in_array($key, array('rowid', 'ref')) && method_exists($this, 'getNomUrl')) {
8499 // @phan-suppress-next-line PhanTypeMismatchProperty
8500 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.
8501 $value = $this->getNomUrl(1, '', 0, '', 1);
8502 }
8503 } elseif ($key == 'status' && method_exists($this, 'getLibStatut')) {
8504 $value = $this->getLibStatut(3);
8505 } elseif ($type == 'date') {
8506 if (!empty($value)) {
8507 $value = dol_print_date($value, 'day'); // We suppose dates without time are always gmt (storage of course + output)
8508 } else {
8509 $value = '';
8510 }
8511 } elseif ($type == 'datetime' || $type == 'timestamp') {
8512 if (!empty($value)) {
8513 $value = dol_print_date($value, 'dayhour', 'tzuserrel');
8514 } else {
8515 $value = '';
8516 }
8517 } elseif ($type == 'duration') {
8518 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
8519 if (!is_null($value) && $value !== '') {
8520 $value = convertSecondToTime((int) $value, 'allhourmin');
8521 }
8522 } elseif ($type == 'double' || $type == 'real') {
8523 if (!is_null($value) && $value !== '') {
8524 $value = price($value);
8525 }
8526 } elseif ($type == 'boolean') {
8527 $checked = '';
8528 if (!empty($value)) {
8529 $checked = ' checked ';
8530 }
8531 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
8532 $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
8533 } else {
8534 $value = yn($value ? 1 : 0);
8535 }
8536 } elseif ($type == 'mail' || $type == 'email') {
8537 $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
8538 } elseif ($type == 'url') {
8539 $value = dol_print_url($value, '_blank', 32, 1);
8540 } elseif ($type == 'phone') {
8541 $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
8542 } elseif ($type == 'ip') {
8543 $value = dol_print_ip($value, 0);
8544 } elseif ($type == 'stars') {
8545 $value = '<input type="hidden" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.$this->id.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').'>';
8546 $value .= '<div class="star-selection" id="'.$keyprefix.$key.$keysuffix.$this->id.'_selection">';
8547 $i = 1;
8548 while ($i <= $size) {
8549 $value .= '<span class="star" data-value="'.$i.'">'.img_picto('', 'fontawesome_star_fas').'</span>';
8550 $i++;
8551 }
8552 $value .= '</div>';
8553 $value .= '<script>
8554 $(document).ready(function() {
8555 let container = $("#'.$keyprefix.$key.$keysuffix.$this->id.'_selection");
8556 let selectedStars = parseInt($("#'.$keyprefix.$key.$keysuffix.$this->id.'").val()) || 0;
8557 container.find(".star").each(function() {
8558 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
8559 });
8560 container.find(".star").on("mouseover", function() {
8561 let selectedStar = $(this).data("value");
8562 container.find(".star").each(function() {
8563 $(this).toggleClass("active", $(this).data("value") <= selectedStar);
8564 });
8565 });
8566 container.on("mouseout", function() {
8567 container.find(".star").each(function() {
8568 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
8569 });
8570 });
8571 container.find(".star").off("click").on("click", function() {
8572 selectedStars = $(this).data("value");
8573 if (selectedStars == 1 && $("#'.$keyprefix.$key.$keysuffix.$this->id.'").val() == 1) {
8574 selectedStars = 0;
8575 }
8576 container.find("#'.$keyprefix.$key.$keysuffix.$this->id.'").val(selectedStars);
8577 container.find(".star").each(function() {
8578 $(this).toggleClass("active", $(this).data("value") <= selectedStars);
8579 });
8580 $.ajax({
8581 url: "ajax/'.$this->element.'.php",
8582 method: "POST",
8583 data: {
8584 objectId: "'.$this->id.'",
8585 field: "'.$keyprefix.$key.$keysuffix.'",
8586 value: selectedStars,
8587 token: "'.newToken().'"
8588 },
8589 success: function(response) {
8590 var res = JSON.parse(response);
8591 console[res.status === "success" ? "log" : "error"](res.message);
8592 },
8593 error: function(xhr, status, error) {
8594 console.log("Ajax request failed while updating '.$keyprefix.$key.$keysuffix.':", error);
8595 }
8596 });
8597 });
8598 });
8599 </script>';
8600 } elseif ($type == 'price') {
8601 if (!is_null($value) && $value !== '') {
8602 $value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
8603 }
8604 } elseif ($type == 'select') {
8605 $value = isset($param['options'][(string) $value]) ? $param['options'][(string) $value] : '';
8606 if (strpos($value, "|") !== false) {
8607 $value = $langs->trans(explode('|', $value)[0]);
8608 } elseif (! is_numeric($value)) {
8609 $value = $langs->trans($value);
8610 }
8611 } elseif ($type == 'sellist') {
8612 $param_list = array_keys($param['options']);
8613 $InfoFieldList = explode(":", $param_list[0]);
8614
8615 $selectkey = "rowid";
8616 $keyList = 'rowid';
8617
8618 if (count($InfoFieldList) > 2 && !empty($InfoFieldList[2])) {
8619 $selectkey = $InfoFieldList[2];
8620 $keyList = $InfoFieldList[2].' as rowid';
8621 }
8622
8623 $fields_label = explode('|', $InfoFieldList[1]);
8624 if (is_array($fields_label)) {
8625 $keyList .= ', ';
8626 $keyList .= implode(', ', $fields_label);
8627 }
8628
8629 $filter_categorie = false;
8630 if (count($InfoFieldList) > 5) {
8631 if ($InfoFieldList[0] == 'categorie') {
8632 $filter_categorie = true;
8633 }
8634 }
8635
8636 $sql = "SELECT ".$keyList;
8637 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
8638 if (isset($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra') !== false) {
8639 $sql .= ' as main';
8640 }
8641 if ($selectkey == 'rowid' && empty($value)) {
8642 $sql .= " WHERE ".$selectkey." = 0";
8643 } elseif ($selectkey == 'rowid') {
8644 $sql .= " WHERE ".$selectkey." = ".((int) $value);
8645 } else {
8646 $sql .= " WHERE ".$selectkey." = '".$this->db->escape($value)."'";
8647 }
8648
8649 //$sql.= ' AND entity = '.$conf->entity;
8650
8651 dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
8652 $resql = $this->db->query($sql);
8653 if ($resql) {
8654 if (!$filter_categorie) {
8655 $value = ''; // value was used, so now we reset it to use it to build final output
8656 $numrows = $this->db->num_rows($resql);
8657 if ($numrows) {
8658 $obj = $this->db->fetch_object($resql);
8659
8660 // Several field into label (eq table:code|libelle:rowid)
8661 $fields_label = explode('|', $InfoFieldList[1]);
8662
8663 if (is_array($fields_label) && count($fields_label) > 1) {
8664 foreach ($fields_label as $field_toshow) {
8665 $translabel = '';
8666 if (!empty($obj->$field_toshow)) {
8667 $translabel = $langs->trans($obj->$field_toshow);
8668 }
8669 if ($translabel != $field_toshow) {
8670 $value .= dol_trunc($translabel, 18) . ' ';
8671 } else {
8672 $value .= $obj->$field_toshow . ' ';
8673 }
8674 }
8675 } else {
8676 $translabel = '';
8677 if (!empty($obj->{$InfoFieldList[1]})) {
8678 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
8679 }
8680 if ($translabel != $obj->{$InfoFieldList[1]}) {
8681 $value = dol_trunc($translabel, 18);
8682 } else {
8683 $value = $obj->{$InfoFieldList[1]};
8684 }
8685 }
8686 }
8687 } else {
8688 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
8689
8690 $toprint = array();
8691 $obj = $this->db->fetch_object($resql);
8692 $c = new Categorie($this->db);
8693 $c->fetch($obj->rowid);
8694 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
8695 foreach ($ways as $way) {
8696 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
8697 }
8698 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
8699 }
8700 } else {
8701 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
8702 }
8703 } elseif ($type == 'radio') {
8704 $value = $param['options'][(string) $value];
8705 } elseif ($type == 'checkbox') {
8706 $value_arr = explode(',', (string) $value);
8707 $value = '';
8708 if (is_array($value_arr) && count($value_arr) > 0) {
8709 $toprint = array();
8710 foreach ($value_arr as $keyval => $valueval) {
8711 if (!empty($valueval)) {
8712 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $param['options'][$valueval] . '</li>';
8713 }
8714 }
8715 if (!empty($toprint)) {
8716 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
8717 }
8718 }
8719 } elseif ($type == 'chkbxlst') {
8720 $value_arr = (isset($value) ? explode(',', $value) : array());
8721
8722 $param_list = array_keys($param['options']);
8723 $InfoFieldList = explode(":", $param_list[0]);
8724
8725 $selectkey = "rowid";
8726 $keyList = 'rowid';
8727
8728 if (count($InfoFieldList) >= 3) {
8729 $selectkey = $InfoFieldList[2];
8730 $keyList = $InfoFieldList[2].' as rowid';
8731 }
8732
8733 $fields_label = explode('|', $InfoFieldList[1]);
8734 if (is_array($fields_label)) {
8735 $keyList .= ', ';
8736 $keyList .= implode(', ', $fields_label);
8737 }
8738
8739 $filter_categorie = false;
8740 if (count($InfoFieldList) > 5) {
8741 if ($InfoFieldList[0] == 'categorie') {
8742 $filter_categorie = true;
8743 }
8744 }
8745
8746 $sql = "SELECT ".$keyList;
8747 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
8748 if (isset($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra') !== false) {
8749 $sql .= ' as main';
8750 }
8751 // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
8752 // $sql.= ' AND entity = '.$conf->entity;
8753
8754 dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG);
8755 $resql = $this->db->query($sql);
8756 if ($resql) {
8757 if (!$filter_categorie) {
8758 $value = ''; // value was used, so now we reset it to use it to build final output
8759 $toprint = array();
8760 while ($obj = $this->db->fetch_object($resql)) {
8761 // Several field into label (eq table:code|libelle:rowid)
8762 $fields_label = explode('|', $InfoFieldList[1]);
8763 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8764 if (is_array($fields_label) && count($fields_label) > 1) {
8765 foreach ($fields_label as $field_toshow) {
8766 $translabel = '';
8767 if (!empty($obj->$field_toshow)) {
8768 $translabel = $langs->trans($obj->$field_toshow);
8769 }
8770 if ($translabel != $field_toshow) {
8771 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
8772 } else {
8773 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->$field_toshow . '</li>';
8774 }
8775 }
8776 } else {
8777 $translabel = '';
8778 if (!empty($obj->{$InfoFieldList[1]})) {
8779 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
8780 }
8781 if ($translabel != $obj->{$InfoFieldList[1]}) {
8782 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
8783 } else {
8784 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->{$InfoFieldList[1]} . '</li>';
8785 }
8786 }
8787 }
8788 }
8789 } else {
8790 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
8791
8792 $toprint = array();
8793 while ($obj = $this->db->fetch_object($resql)) {
8794 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8795 $c = new Categorie($this->db);
8796 $c->fetch($obj->rowid);
8797 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
8798 foreach ($ways as $way) {
8799 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
8800 }
8801 }
8802 }
8803 }
8804 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
8805 } else {
8806 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
8807 }
8808 } elseif ($type == 'link') {
8809 $out = '';
8810
8811 // only if something to display (perf)
8812 if ($value) {
8813 $param_list = array_keys($param['options']);
8814 // Example: $param_list='ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
8815 // Example: $param_list='ObjectClass:PathToClass:#getnomurlparam1=-1#getnomurlparam2=customer'
8816
8817 $InfoFieldList = explode(":", $param_list[0]);
8818
8819 $classname = $InfoFieldList[0];
8820 $classpath = $InfoFieldList[1];
8821
8822 // Set $getnomurlparam1 et getnomurlparam2
8823 $getnomurlparam = 3;
8824 $getnomurlparam2 = '';
8825 $regtmp = array();
8826 if (preg_match('/#getnomurlparam1=([^#]*)/', $param_list[0], $regtmp)) {
8827 $getnomurlparam = $regtmp[1];
8828 }
8829 if (preg_match('/#getnomurlparam2=([^#]*)/', $param_list[0], $regtmp)) {
8830 $getnomurlparam2 = $regtmp[1];
8831 }
8832
8833 if (!empty($classpath)) {
8834 dol_include_once($InfoFieldList[1]);
8835
8836 if ($classname && !class_exists($classname)) {
8837 // from V19 of Dolibarr, In some cases link use element instead of class, example project_task
8838 // TODO use newObjectByElement() introduce in V20 by PR #30036 for better errors management
8839 $element_prop = getElementProperties($classname);
8840 if ($element_prop) {
8841 $classname = $element_prop['classname'];
8842 }
8843 }
8844
8845
8846 if ($classname && class_exists($classname)) {
8847 $object = new $classname($this->db);
8848 '@phan-var-force CommonObject $object';
8849 if ($object->element === 'product') { // Special case for product because default valut of fetch are wrong
8850 '@phan-var-force Product $object';
8851 $result = $object->fetch($value, '', '', '', 0, 1, 1);
8852 } else {
8853 $result = $object->fetch($value);
8854 }
8855 if ($result > 0) {
8856 if ($object->element === 'product') {
8857 '@phan-var-force Product $object';
8858 $get_name_url_param_arr = array($getnomurlparam, $getnomurlparam2, 0, -1, 0, '', 0);
8859 if (isset($val['get_name_url_params'])) {
8860 $get_name_url_params = explode(':', $val['get_name_url_params']);
8861 if (!empty($get_name_url_params)) {
8862 $param_num_max = count($get_name_url_param_arr) - 1;
8863 foreach ($get_name_url_params as $param_num => $param_value) {
8864 if ($param_num > $param_num_max) {
8865 break;
8866 }
8867 $get_name_url_param_arr[$param_num] = $param_value;
8868 }
8869 }
8870 }
8871
8875 $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]);
8876 } else {
8877 $value = $object->getNomUrl($getnomurlparam, $getnomurlparam2);
8878 }
8879 } else {
8880 $value = '';
8881 }
8882 }
8883 } else {
8884 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
8885 return 'Error bad setup of extrafield';
8886 }
8887 } else {
8888 $value = '';
8889 }
8890 } elseif ($type == 'password') {
8891 $value = '<span class="opacitymedium">'.$langs->trans("Encrypted").'</span>';
8892 //$value = preg_replace('/./i', '*', $value);
8893 } elseif ($type == 'array') {
8894 if (is_array($value)) {
8895 $value = implode('<br>', $value);
8896 } else {
8897 dol_syslog(__METHOD__.' Expected array from dol_eval, but got '.gettype($value), LOG_ERR);
8898 return 'Error unexpected result from code evaluation';
8899 }
8900 } else { // text|html|varchar
8901 if (!empty($value) && preg_match('/^text/', (string) $type) && !preg_match('/search_/', $keyprefix) && !empty($param['options'])) {
8902 $value = str_replace(',', "\n", $value);
8903 }
8904 $value = dol_htmlentitiesbr($value);
8905 }
8906
8907 //print $type.'-'.$size.'-'.$value;
8908 $out = $value;
8909
8910 return is_null($out) ? '' : $out;
8911 }
8912
8919 public function clearFieldError($fieldKey)
8920 {
8921 $this->error = '';
8922 unset($this->validateFieldsErrors[$fieldKey]);
8923 }
8924
8932 public function setFieldError($fieldKey, $msg = '')
8933 {
8934 global $langs;
8935 if (empty($msg)) {
8936 $msg = $langs->trans("UnknownError");
8937 }
8938
8939 $this->error = $this->validateFieldsErrors[$fieldKey] = $msg;
8940 }
8941
8948 public function getFieldError($fieldKey)
8949 {
8950 if (!empty($this->validateFieldsErrors[$fieldKey])) {
8951 return $this->validateFieldsErrors[$fieldKey];
8952 }
8953 return '';
8954 }
8955
8964 public function validateField($fields, $fieldKey, $fieldValue)
8965 {
8966 global $langs;
8967
8968 if (!class_exists('Validate')) {
8969 require_once DOL_DOCUMENT_ROOT . '/core/class/validate.class.php';
8970 }
8971
8972 $this->clearFieldError($fieldKey);
8973
8974 if (!array_key_exists($fieldKey, $fields) || !is_array($fields[$fieldKey])) {
8975 $this->setFieldError($fieldKey, $langs->trans('FieldNotFoundInObject'));
8976 return false;
8977 }
8978
8979 $val = $fields[$fieldKey];
8980
8981 $param = array();
8982 $param['options'] = array();
8983 $type = $val['type'];
8984
8985 $required = false;
8986 if (isset($val['notnull']) && $val['notnull'] === 1) {
8987 // 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
8988 $required = true;
8989 }
8990
8991 $maxSize = 0;
8992 $minSize = 0;
8993
8994 //
8995 // PREPARE Elements
8996 //
8997 $reg = array();
8998
8999 // Convert var to be able to share same code than showOutputField of extrafields
9000 if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
9001 $type = 'varchar'; // convert varchar(xx) int varchar
9002 $maxSize = $reg[1];
9003 } elseif (preg_match('/varchar/', $type)) {
9004 $type = 'varchar'; // convert varchar(xx) int varchar
9005 }
9006
9007 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
9008 $type = 'select';
9009 }
9010
9011 if (!empty($val['type']) && preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
9012 $type = 'link';
9013 }
9014
9015 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
9016 $param['options'] = $val['arrayofkeyval'];
9017 }
9018
9019 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
9020 $type = 'link';
9021 $param['options'] = array($reg[1].':'.$reg[2] => $reg[1].':'.$reg[2]);
9022 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
9023 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
9024 $type = 'sellist';
9025 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
9026 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
9027 $type = 'sellist';
9028 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
9029 $param['options'] = array($reg[1].':'.$reg[2] => 'N');
9030 $type = 'sellist';
9031 }
9032
9033 //
9034 // TEST Value
9035 //
9036
9037 // Use Validate class to allow external Modules to use data validation part instead of concentrate all test here (factoring) or just for reuse
9038 $validate = new Validate($this->db, $langs);
9039
9040
9041 // little trick : to perform tests with good performances sort tests by quick to low
9042
9043 //
9044 // COMMON TESTS
9045 //
9046
9047 // Required test and empty value
9048 if ($required && !$validate->isNotEmptyString($fieldValue)) {
9049 $this->setFieldError($fieldKey, $validate->error);
9050 return false;
9051 } elseif (!$required && !$validate->isNotEmptyString($fieldValue)) {
9052 // if no value sent and the field is not mandatory, no need to perform tests
9053 return true;
9054 }
9055
9056 // MAX Size test
9057 if (!empty($maxSize) && !$validate->isMaxLength($fieldValue, $maxSize)) {
9058 $this->setFieldError($fieldKey, $validate->error);
9059 return false;
9060 }
9061
9062 // MIN Size test
9063 if (!empty($minSize) && !$validate->isMinLength($fieldValue, $minSize)) {
9064 $this->setFieldError($fieldKey, $validate->error);
9065 return false;
9066 }
9067
9068 //
9069 // TESTS for TYPE
9070 //
9071
9072 if (in_array($type, array('date', 'datetime', 'timestamp'))) {
9073 if (!$validate->isTimestamp($fieldValue)) {
9074 $this->setFieldError($fieldKey, $validate->error);
9075 return false;
9076 } else {
9077 return true;
9078 }
9079 } elseif ($type == 'duration') {
9080 if (!$validate->isDuration($fieldValue)) {
9081 $this->setFieldError($fieldKey, $validate->error);
9082 return false;
9083 } else {
9084 return true;
9085 }
9086 } elseif (in_array($type, array('double', 'real', 'price'))) {
9087 // is numeric
9088 if (!$validate->isNumeric($fieldValue)) {
9089 $this->setFieldError($fieldKey, $validate->error);
9090 return false;
9091 } else {
9092 return true;
9093 }
9094 } elseif ($type == 'boolean') {
9095 if (!$validate->isBool($fieldValue)) {
9096 $this->setFieldError($fieldKey, $validate->error);
9097 return false;
9098 } else {
9099 return true;
9100 }
9101 } elseif ($type == 'mail') {
9102 if (!$validate->isEmail($fieldValue)) {
9103 $this->setFieldError($fieldKey, $validate->error);
9104 return false;
9105 }
9106 } elseif ($type == 'url') {
9107 if (!$validate->isUrl($fieldValue)) {
9108 $this->setFieldError($fieldKey, $validate->error);
9109 return false;
9110 } else {
9111 return true;
9112 }
9113 } elseif ($type == 'phone') {
9114 if (!$validate->isPhone($fieldValue)) {
9115 $this->setFieldError($fieldKey, $validate->error);
9116 return false;
9117 } else {
9118 return true;
9119 }
9120 } elseif ($type == 'select' || $type == 'radio') {
9121 if (!isset($param['options'][$fieldValue])) {
9122 $this->error = $langs->trans('RequireValidValue');
9123 return false;
9124 } else {
9125 return true;
9126 }
9127 } elseif ($type == 'sellist' || $type == 'chkbxlst') {
9128 $param_list = array_keys($param['options']);
9129 $InfoFieldList = explode(":", $param_list[0]);
9130 $value_arr = explode(',', $fieldValue);
9131 $value_arr = array_map(array($this->db, 'escape'), $value_arr);
9132
9133 $selectkey = "rowid";
9134 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
9135 $selectkey = $InfoFieldList[2];
9136 }
9137
9138 if (!$validate->isInDb($value_arr, $InfoFieldList[0], $selectkey)) {
9139 $this->setFieldError($fieldKey, $validate->error);
9140 return false;
9141 } else {
9142 return true;
9143 }
9144 } elseif ($type == 'link') {
9145 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
9146 $InfoFieldList = explode(":", $param_list[0]);
9147 $classname = $InfoFieldList[0];
9148 $classpath = $InfoFieldList[1];
9149 if (!$validate->isFetchable($fieldValue, $classname, $classpath)) {
9150 $lastIsFetchableError = $validate->error;
9151
9152 // from V19 of Dolibarr, In some cases link use element instead of class, example project_task
9153 if ($validate->isFetchableElement($fieldValue, $classname)) {
9154 return true;
9155 }
9156
9157 $this->setFieldError($fieldKey, $lastIsFetchableError);
9158 return false;
9159 } else {
9160 return true;
9161 }
9162 }
9163
9164 // if no test failed all is ok
9165 return true;
9166 }
9167
9181 public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = '', $display_type = 'card')
9182 {
9183 global $db, $conf, $langs, $action, $form, $hookmanager;
9184
9185 if (!is_object($form)) {
9186 $form = new Form($db);
9187 }
9188 if (!is_object($extrafields)) {
9189 dol_syslog('Bad parameter extrafields for showOptionals', LOG_ERR);
9190 return 'Bad parameter extrafields for showOptionals';
9191 }
9192 if (!is_array($extrafields->attributes[$this->table_element])) {
9193 dol_syslog("extrafields->attributes was not loaded with extrafields->fetch_name_optionals_label(table_element);", LOG_WARNING);
9194 }
9195
9196 $out = '';
9197
9198 $parameters = array('mode' => $mode, 'params' => $params, 'keysuffix' => $keysuffix, 'keyprefix' => $keyprefix, 'display_type' => $display_type);
9199 $reshook = $hookmanager->executeHooks('showOptionals', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
9200
9201 if (empty($reshook)) {
9202 if (is_array($extrafields->attributes[$this->table_element]) && array_key_exists('label', $extrafields->attributes[$this->table_element]) && is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label']) > 0) {
9203 $out .= "\n";
9204 $out .= '<!-- commonobject:showOptionals --> ';
9205 $out .= "\n";
9206
9207 $nbofextrafieldsshown = 0;
9208 $e = 0; // var to manage the modulo (odd/even)
9209
9210 $lastseparatorkeyfound = '';
9211 $extrafields_collapse_num = '';
9212 $extrafields_collapse_num_old = '';
9213 $i = 0;
9214
9215 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $label) {
9216 $i++;
9217
9218 // Show only the key field in params @phan-suppress-next-line PhanTypeArraySuspiciousNullable
9219 if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) {
9220 continue;
9221 }
9222
9223 // Test on 'enabled' ('enabled' is different than 'list' = 'visibility')
9224 $enabled = 1;
9225 if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
9226 $enabled = (int) dol_eval((string) $extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
9227 }
9228 if (empty($enabled)) {
9229 continue;
9230 }
9231
9232 $visibility = 1;
9233 if (isset($extrafields->attributes[$this->table_element]['list'][$key])) {
9234 $visibility = (int) dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
9235 }
9236
9237 $perms = 1;
9238 if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
9239 $perms = (int) dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
9240 }
9241
9242 if (($mode == 'create') && !in_array(abs($visibility), array(1, 3))) {
9243 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list
9244 } elseif (($mode == 'edit') && !in_array(abs($visibility), array(1, 3, 4))) {
9245 // We need to make sure, that the values of hidden extrafields are also part of $_POST. Otherwise, they would be empty after an update of the object. See also getOptionalsFromPost
9246 $ef_name = 'options_' . $key;
9247 $ef_value = $this->array_options[$ef_name];
9248 $out .= '<input type="hidden" name="' . $ef_name . '" id="' . $ef_name . '" value="' . $ef_value . '" />' . "\n";
9249 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list and <> 4 = not visible at the creation
9250 } elseif ($mode == 'view' && empty($visibility)) {
9251 continue;
9252 }
9253 if (empty($perms)) {
9254 continue;
9255 }
9256
9257 // Load language if required
9258 if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
9259 $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
9260 }
9261
9262 $colspan = 0;
9263 $value = null;
9264 if (is_array($params) && count($params) > 0 && $display_type == 'card') {
9265 if (array_key_exists('cols', $params)) {
9266 $colspan = $params['cols'];
9267 } elseif (array_key_exists('colspan', $params)) { // For backward compatibility. Use cols instead now.
9268 $reg = array();
9269 if (preg_match('/colspan="(\d+)"/', $params['colspan'], $reg)) {
9270 $colspan = $reg[1];
9271 } else {
9272 $colspan = $params['colspan'];
9273 }
9274 }
9275 }
9276 $colspan = intval($colspan);
9277
9278 switch ($mode) {
9279 case "view":
9280 $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 formatted later
9281 break;
9282 case "create":
9283 case "edit":
9284 // We get the value of property found with GETPOST so it takes into account:
9285 // default values overwrite, restore back to list link, ... (but not 'default value in database' of field)
9286 $check = 'alphanohtml';
9287 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text'))) {
9288 $check = 'restricthtml';
9289 }
9290 $getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, $check, 3); // GETPOST can get value from GET, POST or setup of default values overwrite.
9291 // GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
9292 if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)) {
9293 if (is_array($getposttemp)) {
9294 // $getposttemp is an array but following code expects a comma separated string
9295 $value = implode(",", $getposttemp);
9296 } else {
9297 $value = $getposttemp;
9298 }
9299 } elseif (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('int'))) {
9300 $value = (!empty($this->array_options["options_".$key]) || (isset($this->array_options["options_".$key]) && $this->array_options["options_".$key] === '0')) ? $this->array_options["options_".$key] : '';
9301 } else {
9302 $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.
9303 }
9304 //var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
9305 break;
9306 }
9307
9308 $nbofextrafieldsshown++;
9309
9310 // Output value of the current field
9311 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
9312 $extrafields_collapse_num = $key;
9313 /*
9314 $extrafield_param = $extrafields->attributes[$this->table_element]['param'][$key];
9315 if (!empty($extrafield_param) && is_array($extrafield_param)) {
9316 $extrafield_param_list = array_keys($extrafield_param['options']);
9317
9318 if (count($extrafield_param_list) > 0) {
9319 $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
9320
9321 if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
9322 //$extrafields_collapse_num = $extrafields->attributes[$this->table_element]['pos'][$key];
9323 $extrafields_collapse_num = $key;
9324 }
9325 }
9326 }
9327 */
9328
9329 // if colspan=0 or 1, the second column is not extended, so the separator must be on 2 columns
9330 $out .= $extrafields->showSeparator($key, $this, ($colspan ? $colspan + 1 : 2), $display_type, $mode);
9331
9332 $lastseparatorkeyfound = $key;
9333 } else {
9334 $collapse_group = $extrafields_collapse_num.(!empty($this->id) ? '_'.$this->id : '');
9335
9336 $class = (!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
9337 $csstyle = '';
9338 if (is_array($params) && count($params) > 0) {
9339 if (array_key_exists('class', $params)) {
9340 $class .= $params['class'].' ';
9341 }
9342 if (array_key_exists('style', $params)) {
9343 $csstyle = $params['style'];
9344 }
9345 }
9346
9347 // add html5 elements
9348 $domData = ' data-element="extrafield"';
9349 $domData .= ' data-targetelement="'.$this->element.'"';
9350 $domData .= ' data-targetid="'.$this->id.'"';
9351
9352 $html_id = (empty($this->id) ? '' : 'extrarow-'.$this->element.'_'.$key.'_'.$this->id);
9353 if ($display_type == 'card') {
9354 if (getDolGlobalString('MAIN_EXTRAFIELDS_USE_TWO_COLUMS') && ($e % 2) == 0) {
9355 $colspan = 0;
9356 }
9357
9358 if ($action == 'selectlines') {
9359 $colspan++;
9360 }
9361 }
9362
9363 // Convert date into timestamp format (value in memory must be a timestamp)
9364 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date'))) {
9365 $datenotinstring = null;
9366 if (array_key_exists('options_'.$key, $this->array_options)) {
9367 $datenotinstring = $this->array_options['options_'.$key];
9368 if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
9369 $datenotinstring = $this->db->jdate($datenotinstring);
9370 }
9371 }
9372 $datekey = $keyprefix.'options_'.$key.$keysuffix;
9373 $value = (GETPOSTISSET($datekey) && $this->id == GETPOST('elrowid', 'int')) ? dol_mktime(12, 0, 0, GETPOSTINT($datekey.'month', 3), GETPOSTINT($datekey.'day', 3), GETPOSTINT($datekey.'year', 3)) : $datenotinstring;
9374 }
9375 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('datetime'))) {
9376 $datenotinstring = null;
9377 if (array_key_exists('options_'.$key, $this->array_options)) {
9378 $datenotinstring = $this->array_options['options_'.$key];
9379 if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
9380 $datenotinstring = $this->db->jdate($datenotinstring);
9381 }
9382 }
9383 $timekey = $keyprefix.'options_'.$key.$keysuffix;
9384 $value = (GETPOSTISSET($timekey)) ? dol_mktime(GETPOSTINT($timekey.'hour', 3), GETPOSTINT($timekey.'min', 3), GETPOSTINT($timekey.'sec', 3), GETPOSTINT($timekey.'month', 3), GETPOSTINT($timekey.'day', 3), GETPOSTINT($timekey.'year', 3), 'tzuserrel') : $datenotinstring;
9385 }
9386 // Convert float submitted string into real php numeric (value in memory must be a php numeric)
9387 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price', 'double'))) {
9388 if (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) {
9389 $value = price2num($value);
9390 } elseif (isset($this->array_options['options_'.$key])) {
9391 $value = $this->array_options['options_'.$key];
9392 }
9393 }
9394
9395 // HTML, text, select, integer and varchar: take into account default value in database if in create mode
9396 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text', 'varchar', 'select', 'radio', 'int', 'boolean'))) {
9397 if ($action == 'create' || $mode == 'create') {
9398 $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) ? $value : $extrafields->attributes[$this->table_element]['default'][$key];
9399 }
9400 }
9401
9402 $labeltoshow = $langs->trans($label);
9403 $helptoshow = $langs->trans($extrafields->attributes[$this->table_element]['help'][$key]);
9404 if ($display_type == 'card') {
9405 $out .= '<tr '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="field_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
9406 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER') && ($action == 'view' || $action == 'valid' || $action == 'editline' || $action == 'confirm_valid' || $action == 'confirm_cancel')) {
9407 $out .= '<td></td>';
9408 }
9409 $out .= '<td class="'.(empty($params['tdclass']) ? 'titlefieldmax45' : $params['tdclass']).' wordbreak';
9410 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'text') {
9411 $out .= ' tdtop';
9412 }
9413 } elseif ($display_type == 'line') {
9414 $out .= '<div '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="fieldline_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
9415 $out .= '<div style="display: inline-block; padding-right:4px" class="wordbreak';
9416 }
9417 //$out .= "titlefield";
9418 //if (GETPOST('action', 'restricthtml') == 'create') $out.='create';
9419 // BUG #11554 : For public page, use red dot for required fields, instead of bold label
9420 $tpl_context = isset($params["tpl_context"]) ? $params["tpl_context"] : "none";
9421 if ($tpl_context != "public") { // Public page : red dot instead of fieldrequired characters
9422 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
9423 $out .= ' fieldrequired';
9424 }
9425 }
9426 $out .= '">';
9427 if ($tpl_context == "public") { // Public page : red dot instead of fieldrequired characters
9428 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
9429 $out .= $form->textwithpicto($labeltoshow, $helptoshow);
9430 } else {
9431 $out .= $labeltoshow;
9432 }
9433 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
9434 $out .= '&nbsp;<span style="color: red">*</span>';
9435 }
9436 } else {
9437 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
9438 $out .= $form->textwithpicto($labeltoshow, $helptoshow);
9439 } else {
9440 $out .= $labeltoshow;
9441 }
9442 }
9443
9444 $out .= ($display_type == 'card' ? '</td>' : '</div>');
9445
9446 // Second column
9447 $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
9448 if ($display_type == 'card') {
9449 // 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
9450 $out .= '<td '.($html_id ? 'id="'.$html_id.'" ' : '').' class="valuefieldcreate '.$this->element.'_extras_'.$key;
9451 $out .= '" '.($colspan ? ' colspan="'.$colspan.'"' : '');
9452 $out .= '>';
9453 } elseif ($display_type == 'line') {
9454 $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].'">';
9455 }
9456
9457 switch ($mode) {
9458 case "view":
9459 $out .= $extrafields->showOutputField($key, $value, '', $this->table_element);
9460 break;
9461 case "create":
9462 $listoftypestoshowpicto = explode(',', getDolGlobalString('MAIN_TYPES_TO_SHOW_PICTO', 'email,phone,ip,password'));
9463 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], $listoftypestoshowpicto)) {
9464 $out .= getPictoForType($extrafields->attributes[$this->table_element]['type'][$key], ($extrafields->attributes[$this->table_element]['type'][$key] == 'text' ? 'tdtop' : ''));
9465 }
9466 //$out .= '<!-- type = '.$extrafields->attributes[$this->table_element]['type'][$key].' -->';
9467 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this, $this->table_element);
9468 break;
9469 case "edit":
9470 $listoftypestoshowpicto = explode(',', getDolGlobalString('MAIN_TYPES_TO_SHOW_PICTO', 'email,phone,ip,password'));
9471 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], $listoftypestoshowpicto)) {
9472 $out .= getPictoForType($extrafields->attributes[$this->table_element]['type'][$key], ($extrafields->attributes[$this->table_element]['type'][$key] == 'text' ? 'tdtop' : ''));
9473 }
9474 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', '', $this, $this->table_element);
9475 break;
9476 }
9477
9478 $out .= ($display_type == 'card' ? '</td>' : '</div>');
9479 $out .= ($display_type == 'card' ? '</tr>'."\n" : '</div>');
9480 $e++;
9481 }
9482 }
9483 $out .= "\n";
9484 // Add code to manage list depending on others
9485 if (!empty($conf->use_javascript_ajax)) {
9486 $out .= $this->getJSListDependancies();
9487 }
9488
9489 $out .= '<!-- commonobject:showOptionals end --> '."\n";
9490
9491 if (empty($nbofextrafieldsshown)) {
9492 $out = '';
9493 }
9494 }
9495 }
9496
9497 $out .= $hookmanager->resPrint;
9498
9499 return $out;
9500 }
9501
9506 public function getJSListDependancies($type = '_extra')
9507 {
9508 $out = '
9509 <script nonce="'.getNonce().'">
9510 jQuery(document).ready(function() {
9511 function showOptions'.$type.'(child_list, parent_list, orig_select)
9512 {
9513 var val = $("select[name=\""+parent_list+"\"]").val();
9514 var parentVal = parent_list + ":" + val;
9515 if(typeof val == "string"){
9516 if(val != "") {
9517 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
9518 $("select[name=\""+child_list+"\"] option[parent]").remove();
9519 $("select[name=\""+child_list+"\"]").append(options);
9520 } else {
9521 var options = orig_select.find("option[parent]").clone();
9522 $("select[name=\""+child_list+"\"] option[parent]").remove();
9523 $("select[name=\""+child_list+"\"]").append(options);
9524 }
9525 } else if(val > 0) {
9526 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
9527 $("select[name=\""+child_list+"\"] option[parent]").remove();
9528 $("select[name=\""+child_list+"\"]").append(options);
9529 } else {
9530 var options = orig_select.find("option[parent]").clone();
9531 $("select[name=\""+child_list+"\"] option[parent]").remove();
9532 $("select[name=\""+child_list+"\"]").append(options);
9533 }
9534 }
9535 function setListDependencies'.$type.'() {
9536 jQuery("select option[parent]").parent().each(function() {
9537 var orig_select = {};
9538 var child_list = $(this).attr("name");
9539 orig_select[child_list] = $(this).clone();
9540 var parent = $(this).find("option[parent]:first").attr("parent");
9541 var infos = parent.split(":");
9542 var parent_list = infos[0];
9543
9544 //Hide daughters lists
9545 if ($("#"+child_list).val() == 0 && $("#"+parent_list).val() == 0){
9546 $("#"+child_list).hide();
9547 //Show mother lists
9548 } else if ($("#"+parent_list).val() != 0){
9549 $("#"+parent_list).show();
9550 }
9551 //Show the child list if the parent list value is selected
9552 $("select[name=\""+parent_list+"\"]").click(function() {
9553 if ($(this).val() != 0){
9554 $("#"+child_list).show()
9555 }
9556 });
9557
9558 //When we change parent list
9559 $("select[name=\""+parent_list+"\"]").change(function() {
9560 showOptions'.$type.'(child_list, parent_list, orig_select[child_list]);
9561 //Select the value 0 on child list after a change on the parent list
9562 $("#"+child_list).val(0).trigger("change");
9563 //Hide child lists if the parent value is set to 0
9564 if ($(this).val() == 0){
9565 $("#"+child_list).hide();
9566 }
9567 });
9568 });
9569 }
9570
9571 setListDependencies'.$type.'();
9572 });
9573 </script>'."\n";
9574 return $out;
9575 }
9576
9582 public function getRights()
9583 {
9584 global $user;
9585
9586 $module = empty($this->module) ? '' : $this->module;
9587 $element = $this->element;
9588
9589 if ($element == 'facturerec') {
9590 $element = 'facture';
9591 } elseif ($element == 'invoice_supplier_rec') {
9592 return !$user->hasRight('fournisseur', 'facture') ? null : $user->hasRight('fournisseur', 'facture');
9593 } elseif ($module && $user->hasRight($module, $element)) {
9594 // for modules built with ModuleBuilder
9595 return $user->hasRight($module, $element);
9596 }
9597
9598 return $user->rights->$element;
9599 }
9600
9613 public static function commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
9614 {
9615 global $hookmanager;
9616
9617 $parameters = array(
9618 'origin_id' => $origin_id,
9619 'dest_id' => $dest_id,
9620 'tables' => $tables,
9621 );
9622 $reshook = $hookmanager->executeHooks('commonReplaceThirdparty', $parameters);
9623 if ($reshook) {
9624 return true; // replacement code
9625 } elseif ($reshook < 0) {
9626 return $ignoreerrors === 1; // failure
9627 } // reshook = 0 => execute normal code
9628
9629 foreach ($tables as $table) {
9630 $sql = 'UPDATE '.$dbs->prefix().$table.' SET fk_soc = '.((int) $dest_id).' WHERE fk_soc = '.((int) $origin_id);
9631
9632 if (!$dbs->query($sql)) {
9633 if ($ignoreerrors) {
9634 return true; // FIXME Not enough. If there is A-B on the kept thirdparty and B-C on the old one, we must get A-B-C after merge. Not A-B.
9635 }
9636 //$this->errors = $db->lasterror();
9637 return false;
9638 }
9639 }
9640
9641 return true;
9642 }
9643
9656 public static function commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
9657 {
9658 foreach ($tables as $table) {
9659 $sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_product = '.((int) $dest_id).' WHERE fk_product = '.((int) $origin_id);
9660
9661 if (!$dbs->query($sql)) {
9662 if ($ignoreerrors) {
9663 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.
9664 }
9665 //$this->errors = $db->lasterror();
9666 return false;
9667 }
9668 }
9669
9670 return true;
9671 }
9672
9685 public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
9686 {
9687 global $conf;
9688
9689 $buyPrice = 0;
9690
9691 if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && getDolGlobalInt('ForceBuyingPriceIfNull') > 0)) {
9692 // When ForceBuyingPriceIfNull is set
9693 $buyPrice = $unitPrice * (1 - $discountPercent / 100);
9694 } else {
9695 // Get cost price for margin calculation
9696 if (!empty($fk_product) && $fk_product > 0) {
9697 $result = 0;
9698 if (getDolGlobalString('MARGIN_TYPE') == 'costprice') {
9699 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
9700 $product = new Product($this->db);
9701 $result = $product->fetch($fk_product);
9702 if ($result <= 0) {
9703 $this->errors[] = 'ErrorProductIdDoesNotExists';
9704 return -1;
9705 }
9706 if ($product->cost_price > 0) {
9707 $buyPrice = $product->cost_price;
9708 } elseif ($product->pmp > 0) {
9709 $buyPrice = $product->pmp;
9710 }
9711 } elseif (getDolGlobalString('MARGIN_TYPE') == 'pmp') {
9712 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
9713 $product = new Product($this->db);
9714 $result = $product->fetch($fk_product);
9715 if ($result <= 0) {
9716 $this->errors[] = 'ErrorProductIdDoesNotExists';
9717 return -1;
9718 }
9719 if ($product->pmp > 0) {
9720 $buyPrice = $product->pmp;
9721 }
9722 }
9723
9724 if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1', 'pmp', 'costprice'))) {
9725 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
9726 $productFournisseur = new ProductFournisseur($this->db);
9727 if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0) {
9728 $buyPrice = $productFournisseur->fourn_unitprice;
9729 } elseif ($result < 0) {
9730 $this->errors[] = $productFournisseur->error;
9731 return -2;
9732 }
9733 }
9734 }
9735 }
9736
9737 return (float) $buyPrice;
9738 }
9739
9747 public function getDataToShowPhoto($modulepart, $imagesize)
9748 {
9749 // See getDataToShowPhoto() implemented by Product for example.
9750 return array('dir' => '', 'file' => '', 'originalfile' => '', 'altfile' => '', 'email' => '', 'capture' => '');
9751 }
9752
9753
9754 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
9774 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')
9775 {
9776 // phpcs:enable
9777 global $user, $langs;
9778
9779 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
9780 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
9781
9782 $sortfield = 'position_name';
9783 $sortorder = 'asc';
9784
9785 $dir = $sdir.'/';
9786 $pdir = '/';
9787
9788 $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
9789 $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
9790
9791 // For backward compatibility
9792 if ($modulepart == 'product') {
9793 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
9794 $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9795 $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9796 }
9797 }
9798 if ($modulepart == 'category') {
9799 $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9800 $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9801 }
9802
9803 // Defined relative dir to DOL_DATA_ROOT
9804 $relativedir = '';
9805 if ($dir) {
9806 $relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
9807 $relativedir = preg_replace('/^[\\/]/', '', $relativedir);
9808 $relativedir = preg_replace('/[\\/]$/', '', $relativedir);
9809 }
9810
9811 $dirthumb = $dir.'thumbs/';
9812 $pdirthumb = $pdir.'thumbs/';
9813
9814 $return = '<!-- Photo -->'."\n";
9815 $nbphoto = 0;
9816
9817 $filearray = dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ? SORT_DESC : SORT_ASC), 1);
9818
9819 /*if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) // For backward compatibility, we scan also old dirs
9820 {
9821 $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
9822 $filearray=array_merge($filearray, $filearrayold);
9823 }*/
9824
9825 completeFileArrayWithDatabaseInfo($filearray, $relativedir, $this);
9826 '@phan-var-force array<array{name:string,path:string,level1name:string,relativename:string,fullname:string,date:string,size:int,perm:int,type:string,position_name:string,cover:string,keywords:string,acl:string,rowid:int,label:string,share:string}> $filearray';
9827
9828 if (count($filearray)) {
9829 if ($sortfield && $sortorder) {
9830 $filearray = dol_sort_array($filearray, $sortfield, $sortorder);
9831 }
9832
9833 foreach ($filearray as $key => $val) {
9834 $photo = '';
9835 $file = $val['name'];
9836
9837 //if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
9838 if (image_format_supported($file) >= 0) {
9839 $nbphoto++;
9840 $photo = $file;
9841 $viewfilename = $file;
9842
9843 if ($size == 1 || $size == 'small') { // Format vignette
9844 // Find name of thumb file
9845 $photo_vignette = basename(getImageFileNameForSize($dir.$file, '_small'));
9846 if (!dol_is_file($dirthumb.$photo_vignette)) {
9847 // The thumb does not exists, so we will use the original file
9848 $dirthumb = $dir;
9849 $pdirthumb = $pdir;
9850 $photo_vignette = basename($file);
9851 }
9852
9853 // Get filesize of original file
9854 $imgarray = dol_getImageSize($dir.$photo);
9855
9856 if ($nbbyrow > 0) {
9857 if ($nbphoto == 1) {
9858 $return .= '<table class="valigntop center centpercent" style="border: 0; padding: 2px; border-spacing: 2px; border-collapse: separate;">';
9859 }
9860
9861 if ($nbphoto % $nbbyrow == 1) {
9862 $return .= '<tr class="center valignmiddle" style="border: 1px">';
9863 }
9864 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%" class="photo">'."\n";
9865 } elseif ($nbbyrow < 0) {
9866 $return .= '<div class="inline-block">'."\n";
9867 }
9868
9869 $relativefile = preg_replace('/^\//', '', $pdir.$photo);
9870 if (empty($nolink)) {
9871 $urladvanced = getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
9872 if ($urladvanced) {
9873 $return .= '<a href="'.$urladvanced.'">';
9874 } else {
9875 $return .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank" rel="noopener noreferrer">';
9876 }
9877 }
9878
9879 // Show image (width height=$maxHeight)
9880 // Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
9881 $alt = $langs->transnoentitiesnoconv('File').': '.$relativefile;
9882 $alt .= ' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
9883 if ($overwritetitle) {
9884 if (is_numeric($overwritetitle)) {
9885 $alt = '';
9886 } else {
9887 $alt = $overwritetitle;
9888 }
9889 }
9890 if (empty($cache) && !empty($val['label'])) {
9891 // label is md5 of file
9892 // use it in url to say we want to cache this version of the file
9893 $cache = $val['label'];
9894 }
9895 if ($usesharelink) {
9896 if (array_key_exists('share', $val) && $val['share']) {
9897 if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
9898 $return .= '<!-- Show original file (thumb not yet available with shared links) -->';
9899 $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).'">';
9900 } else {
9901 $return .= '<!-- Show original file -->';
9902 $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).'">';
9903 }
9904 } else {
9905 $return .= '<!-- Show nophoto file (because file is not shared) -->';
9906 $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
9907 }
9908 } else {
9909 if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
9910 $return .= '<!-- Show thumb -->';
9911 $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).'">';
9912 } else {
9913 $return .= '<!-- Show original file -->';
9914 $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).'">';
9915 }
9916 }
9917
9918 if (empty($nolink)) {
9919 $return .= '</a>';
9920 }
9921
9922 if ($showfilename) {
9923 $return .= '<br>'.$viewfilename;
9924 }
9925 if ($showaction) {
9926 $return .= '<br>';
9927 // If $photo_vignette set, we add a link to generate thumbs if file is an image and width or height higher than limits
9928 if ($photo_vignette && (image_format_supported($photo) > 0) && ((isset($imgarray['width']) && $imgarray['width'] > $maxWidth) || (isset($imgarray['width']) && $imgarray['width'] > $maxHeight))) {
9929 $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>';
9930 }
9931 // Special case for product
9932 if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
9933 // Link to resize
9934 $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; ';
9935
9936 // Link to delete
9937 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
9938 $return .= img_delete().'</a>';
9939 }
9940 }
9941 $return .= "\n";
9942
9943 if ($nbbyrow > 0) {
9944 $return .= '</td>';
9945 if (($nbphoto % $nbbyrow) == 0) {
9946 $return .= '</tr>';
9947 }
9948 } elseif ($nbbyrow < 0) {
9949 $return .= '</div>'."\n";
9950 }
9951 }
9952
9953 if (empty($size)) { // Format origine
9954 $return .= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
9955
9956 if ($showfilename) {
9957 $return .= '<br>'.$viewfilename;
9958 }
9959 if ($showaction) {
9960 // Special case for product
9961 if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
9962 // Link to resize
9963 $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; ';
9964
9965 // Link to delete
9966 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
9967 $return .= img_delete().'</a>';
9968 }
9969 }
9970 }
9971
9972 // On continue ou on arrete de boucler ?
9973 if ($nbmax && $nbphoto >= $nbmax) {
9974 break;
9975 }
9976 }
9977 }
9978
9979 if ($size == 1 || $size == 'small') {
9980 if ($nbbyrow > 0) {
9981 // Ferme tableau
9982 while ($nbphoto % $nbbyrow) {
9983 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%">&nbsp;</td>';
9984 $nbphoto++;
9985 }
9986
9987 if ($nbphoto) {
9988 $return .= '</table>';
9989 }
9990 }
9991 }
9992 }
9993
9994 $this->nbphoto = $nbphoto;
9995
9996 return $return;
9997 }
9998
9999
10006 protected function isArray($info)
10007 {
10008 if (is_array($info)) {
10009 if (isset($info['type']) && $info['type'] == 'array') {
10010 return true;
10011 } else {
10012 return false;
10013 }
10014 }
10015 return false;
10016 }
10017
10024 public function isDate($info)
10025 {
10026 if (isset($info['type']) && ($info['type'] == 'date' || $info['type'] == 'datetime' || $info['type'] == 'timestamp')) {
10027 return true;
10028 }
10029 return false;
10030 }
10031
10038 public function isDuration($info)
10039 {
10040 if (is_array($info)) {
10041 if (isset($info['type']) && ($info['type'] == 'duration')) {
10042 return true;
10043 } else {
10044 return false;
10045 }
10046 } else {
10047 return false;
10048 }
10049 }
10050
10057 public function isInt($info)
10058 {
10059 if (is_array($info)) {
10060 if (isset($info['type']) && (preg_match('/(^int|int$)/i', $info['type']))) {
10061 return true;
10062 } else {
10063 return false;
10064 }
10065 } else {
10066 return false;
10067 }
10068 }
10069
10076 public function isFloat($info)
10077 {
10078 if (is_array($info)) {
10079 if (isset($info['type']) && (preg_match('/^(double|real|price)/i', $info['type']))) {
10080 return true;
10081 } else {
10082 return false;
10083 }
10084 }
10085 return false;
10086 }
10087
10094 public function isText($info)
10095 {
10096 if (is_array($info)) {
10097 if (isset($info['type']) && $info['type'] == 'text') {
10098 return true;
10099 } else {
10100 return false;
10101 }
10102 }
10103 return false;
10104 }
10105
10112 protected function canBeNull($info)
10113 {
10114 if (is_array($info)) {
10115 if (array_key_exists('notnull', $info) && $info['notnull'] != '1') {
10116 return true;
10117 } else {
10118 return false;
10119 }
10120 }
10121 return true;
10122 }
10123
10130 protected function isForcedToNullIfZero($info)
10131 {
10132 if (is_array($info)) {
10133 if (array_key_exists('notnull', $info) && $info['notnull'] == '-1') {
10134 return true;
10135 } else {
10136 return false;
10137 }
10138 }
10139 return false;
10140 }
10141
10148 protected function isIndex($info)
10149 {
10150 if (is_array($info)) {
10151 if (array_key_exists('index', $info) && $info['index'] == true) {
10152 return true;
10153 } else {
10154 return false;
10155 }
10156 }
10157 return false;
10158 }
10159
10160
10169 protected function setSaveQuery()
10170 {
10171 global $conf;
10172
10173 $queryarray = array();
10174 foreach ($this->fields as $field => $info) { // Loop on definition of fields
10175 // Depending on field type ('datetime', ...)
10176 if ($this->isDate($info)) {
10177 if (empty($this->{$field})) {
10178 $queryarray[$field] = null;
10179 } else {
10180 $queryarray[$field] = $this->db->idate($this->{$field});
10181 }
10182 } elseif ($this->isDuration($info)) {
10183 // $this->{$field} may be null, '', 0, '0', 123, '123'
10184 if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
10185 if (!isset($this->{$field})) {
10186 if (!empty($info['default'])) {
10187 $queryarray[$field] = $info['default'];
10188 } else {
10189 $queryarray[$field] = 0;
10190 }
10191 } else {
10192 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
10193 }
10194 } else {
10195 $queryarray[$field] = null;
10196 }
10197 } elseif ($this->isInt($info) || $this->isFloat($info)) {
10198 if ($field == 'entity' && is_null($this->{$field})) {
10199 $queryarray[$field] = ((int) $conf->entity);
10200 } else {
10201 // $this->{$field} may be null, '', 0, '0', 123, '123'
10202 if ((isset($this->{$field}) && ((string) $this->{$field}) != '') || !empty($info['notnull'])) {
10203 if (!isset($this->{$field})) {
10204 $queryarray[$field] = 0;
10205 } elseif ($this->isInt($info)) {
10206 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
10207 } elseif ($this->isFloat($info)) {
10208 $queryarray[$field] = (float) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
10209 }
10210 } else {
10211 $queryarray[$field] = null;
10212 }
10213 }
10214 } else {
10215 // Note: If $this->{$field} is not defined, it means there is a bug into definition of ->fields or a missing declaration of property
10216 // We should keep the warning generated by this because it is a bug somewhere else in code, not here.
10217 $queryarray[$field] = $this->{$field};
10218 }
10219
10220 if (array_key_exists('type', $info) && $info['type'] == 'timestamp' && empty($queryarray[$field])) {
10221 unset($queryarray[$field]);
10222 }
10223 if (!empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) {
10224 $queryarray[$field] = null; // May force 0 to null
10225 }
10226 }
10227
10228 return $queryarray;
10229 }
10230
10237 public function setVarsFromFetchObj(&$obj)
10238 {
10239 global $db;
10240
10241 foreach ($this->fields as $field => $info) {
10242 if ($this->isDate($info)) {
10243 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') {
10244 $this->$field = '';
10245 } else {
10246 $this->$field = $db->jdate($obj->$field);
10247 }
10248 } elseif ($this->isInt($info)) {
10249 if ($field == 'rowid') {
10250 $this->id = (int) $obj->$field;
10251 } else {
10252 if ($this->isForcedToNullIfZero($info)) {
10253 if (empty($obj->$field)) {
10254 $this->$field = null;
10255 } else {
10256 $this->$field = (int) $obj->$field;
10257 }
10258 } else {
10259 if (isset($obj->$field) && (!is_null($obj->$field) || (array_key_exists('notnull', $info) && $info['notnull'] == 1))) {
10260 $this->$field = (int) $obj->$field;
10261 } else {
10262 $this->$field = null;
10263 }
10264 }
10265 }
10266 } elseif ($this->isFloat($info)) {
10267 if ($this->isForcedToNullIfZero($info)) {
10268 if (empty($obj->$field)) {
10269 $this->$field = null;
10270 } else {
10271 $this->$field = (float) $obj->$field;
10272 }
10273 } else {
10274 if (isset($obj->$field) && (!is_null($obj->$field) || (array_key_exists('notnull', $info) && $info['notnull'] == 1))) {
10275 $this->$field = (float) $obj->$field;
10276 } else {
10277 $this->$field = null;
10278 }
10279 }
10280 } else {
10281 $this->$field = isset($obj->$field) ? $obj->$field : null;
10282 }
10283 }
10284
10285 // If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
10286 if (!isset($this->fields['ref']) && isset($this->id)) {
10287 $this->ref = (string) $this->id;
10288 }
10289 }
10290
10295 public function emtpyObjectVars()
10296 {
10297 foreach ($this->fields as $field => $arr) {
10298 $this->$field = null;
10299 }
10300 }
10301
10309 public function getFieldList($alias = '', $excludefields = array())
10310 {
10311 $keys = array_keys($this->fields);
10312 if (!empty($alias)) {
10313 $keys_with_alias = array();
10314 foreach ($keys as $fieldname) {
10315 if (!empty($excludefields)) {
10316 if (in_array($fieldname, $excludefields)) { // The field is excluded and must not be in output
10317 continue;
10318 }
10319 }
10320 $keys_with_alias[] = $alias . '.' . $fieldname;
10321 }
10322 return implode(',', $keys_with_alias);
10323 } else {
10324 return implode(',', $keys);
10325 }
10326 }
10327
10335 protected function quote($value, $fieldsentry)
10336 {
10337 if (is_null($value)) {
10338 return 'NULL';
10339 } elseif (preg_match('/^(int|double|real|price)/i', $fieldsentry['type'])) {
10340 return price2num((string) $value);
10341 } elseif (preg_match('/int$/i', $fieldsentry['type'])) {
10342 return (int) $value;
10343 } elseif ($fieldsentry['type'] == 'boolean') {
10344 if ($value) {
10345 return 'true';
10346 } else {
10347 return 'false';
10348 }
10349 } else {
10350 return "'".$this->db->escape($value)."'";
10351 }
10352 }
10353
10354
10362 public function createCommon(User $user, $notrigger = 0)
10363 {
10364 global $langs;
10365
10366 dol_syslog(get_class($this)."::createCommon create", LOG_DEBUG);
10367
10368 $error = 0;
10369
10370 $now = dol_now();
10371
10372 $fieldvalues = $this->setSaveQuery();
10373
10374 // Note: Here, $fieldvalues contains same keys (or less) that are inside ->fields
10375
10376 if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) {
10377 $fieldvalues['date_creation'] = $this->db->idate($now);
10378 $this->date_creation = $this->db->idate($now);
10379 }
10380 // For backward compatibility, if a property ->fk_user_creat exists and not filled.
10381 if (array_key_exists('fk_user_creat', $fieldvalues) && !($fieldvalues['fk_user_creat'] > 0)) {
10382 $fieldvalues['fk_user_creat'] = $user->id;
10383 $this->fk_user_creat = $user->id;
10384 }
10385 if (array_key_exists('user_creation_id', $fieldvalues) && !($fieldvalues['user_creation_id'] > 0)) {
10386 $fieldvalues['user_creation_id'] = $user->id;
10387 $this->user_creation_id = $user->id;
10388 }
10389 if (array_key_exists('pass_crypted', $fieldvalues) && property_exists($this, 'pass')) {
10390 // @phan-suppress-next-line PhanUndeclaredProperty
10391 $tmparray = dol_hash($this->pass, '0', 0, 1);
10392 $fieldvalues['pass_crypted'] = $tmparray['pass_encrypted'];
10393 if (array_key_exists('pass_encoding', $fieldvalues) && property_exists($this, 'pass_encoding')) {
10394 $fieldvalues['pass_encoding'] = $tmparray['pass_encoding'];
10395 }
10396 }
10397 if (array_key_exists('ref', $fieldvalues)) {
10398 $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
10399 }
10400
10401 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
10402
10403 $keys = array();
10404 $values = array(); // Array to store string forged for SQL syntax
10405 foreach ($fieldvalues as $k => $v) {
10406 $keys[$k] = $k;
10407 $value = $this->fields[$k];
10408 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
10409 $values[$k] = $this->quote($v, $value); // May return string 'NULL' if $value is null
10410 }
10411
10412 // Clean and check mandatory
10413 foreach ($keys as $key) {
10414 if (!isset($this->fields[$key])) {
10415 continue;
10416 }
10417 $key_fields = $this->fields[$key];
10418
10419 // If field is an implicit foreign key field (so type = 'integer:...')
10420 if (preg_match('/^integer:/i', $key_fields['type']) && $values[$key] == '-1') {
10421 $values[$key] = '';
10422 }
10423 if (!empty($key_fields['foreignkey']) && $values[$key] == '-1') {
10424 $values[$key] = '';
10425 }
10426
10427 if (isset($key_fields['notnull']) && $key_fields['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && (!isset($key_fields['default']) || is_null($key_fields['default']))) {
10428 $error++;
10429 $langs->load("errors");
10430 dol_syslog("Mandatory field '".$key."' is empty and required into ->fields definition of class");
10431 $this->errors[] = $langs->trans("ErrorFieldRequired", isset($key_fields['label']) ? $key_fields['label'] : $key);
10432 }
10433
10434 // If value is null and there is a default value for field @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
10435 if (isset($key_fields['notnull']) && $key_fields['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && !is_null($key_fields['default'])) {
10436 $values[$key] = $this->quote($key_fields['default'], $key_fields);
10437 }
10438
10439 // If field is an implicit foreign key field (so type = 'integer:...')
10440 if (isset($key_fields['type']) && preg_match('/^integer:/i', $key_fields['type']) && empty($values[$key])) {
10441 if (isset($key_fields['default'])) {
10442 $values[$key] = ((int) $key_fields['default']);
10443 } else {
10444 $values[$key] = 'null';
10445 }
10446 }
10447 if (!empty($key_fields['foreignkey']) && empty($values[$key])) {
10448 $values[$key] = 'null';
10449 }
10450 }
10451
10452 if ($error) {
10453 return -1;
10454 }
10455
10456 $this->db->begin();
10457
10458 if (!$error) {
10459 $sql = "INSERT INTO ".$this->db->prefix().$this->table_element;
10460 $sql .= " (".implode(", ", $keys).')';
10461 $sql .= " VALUES (".implode(", ", $values).")"; // $values can contains 'abc' or 123
10462
10463 $res = $this->db->query($sql);
10464 if (!$res) {
10465 $error++;
10466 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
10467 $this->errors[] = "ErrorRefAlreadyExists";
10468 } else {
10469 $this->errors[] = $this->db->lasterror();
10470 }
10471 }
10472 }
10473
10474 if (!$error) {
10475 $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
10476 }
10477
10478 // If we have a field ref with a default value of (PROV)
10479 if (!$error) {
10480 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
10481 if (array_key_exists('ref', $this->fields) && array_key_exists('notnull', $this->fields['ref']) && $this->fields['ref']['notnull'] > 0 && array_key_exists('default', $this->fields['ref']) && $this->fields['ref']['default'] == '(PROV)') {
10482 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ref = '(PROV".((int) $this->id).")' WHERE (ref = '(PROV)' OR ref = '') AND rowid = ".((int) $this->id);
10483 $resqlupdate = $this->db->query($sql);
10484
10485 if ($resqlupdate === false) {
10486 $error++;
10487 $this->errors[] = $this->db->lasterror();
10488 } else {
10489 $this->ref = '(PROV'.$this->id.')';
10490 }
10491 }
10492 }
10493
10494 // Create extrafields
10495 if (!$error) {
10496 $result = $this->insertExtraFields();
10497 if ($result < 0) {
10498 $error++;
10499 }
10500 }
10501
10502 // Create lines
10503 if (!empty($this->table_element_line) && !empty($this->fk_element) && !empty($this->lines)) {
10504 foreach ($this->lines as $line) {
10505 $keyforparent = $this->fk_element;
10506 $line->$keyforparent = $this->id;
10507
10508 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
10509 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
10510 if (!is_object($line)) {
10511 $line = (object) $line;
10512 }
10513
10514 $result = 0;
10515 if (method_exists($line, 'insert')) {
10516 $result = $line->insert($user, 1);
10517 } elseif (method_exists($line, 'create')) {
10518 $result = $line->create($user, 1);
10519 }
10520 if ($result < 0) {
10521 $this->error = $line->error;
10522 $this->db->rollback();
10523 return -1;
10524 }
10525 }
10526 }
10527
10528 // Triggers
10529 if (!$error && !$notrigger) {
10530 // Call triggers
10531 $result = $this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user);
10532 if ($result < 0) {
10533 $error++;
10534 }
10535 // End call triggers
10536 }
10537
10538 // Commit or rollback
10539 if ($error) {
10540 $this->db->rollback();
10541 return -1;
10542 } else {
10543 $this->db->commit();
10544 return $this->id;
10545 }
10546 }
10547
10548
10558 public function fetchCommon($id, $ref = null, $morewhere = '', $noextrafields = 0)
10559 {
10560 if (empty($id) && empty($ref) && empty($morewhere)) {
10561 return -1;
10562 }
10563
10564 $fieldlist = $this->getFieldList('t');
10565 if (empty($fieldlist)) {
10566 return 0;
10567 }
10568
10569 $sql = "SELECT ".$fieldlist;
10570 $sql .= " FROM ".$this->db->prefix().$this->table_element.' as t';
10571
10572 if (!empty($id)) {
10573 $sql .= ' WHERE t.rowid = '.((int) $id);
10574 } elseif (!empty($ref)) {
10575 $sql .= " WHERE t.ref = '".$this->db->escape($ref)."'";
10576 } else {
10577 $sql .= ' WHERE 1 = 1'; // usage with empty id and empty ref is very rare
10578 }
10579 if (empty($id) && isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
10580 $sql .= ' AND t.entity IN ('.getEntity($this->element).')';
10581 }
10582 if ($morewhere) {
10583 $sql .= $morewhere;
10584 }
10585 $sql .= ' LIMIT 1'; // This is a fetch, to be certain to get only one record
10586
10587 $res = $this->db->query($sql);
10588 if ($res) {
10589 $obj = $this->db->fetch_object($res);
10590 if ($obj) {
10591 $this->setVarsFromFetchObj($obj);
10592
10593 // Retrieve all extrafield
10594 // fetch optionals attributes and labels
10595 if (empty($noextrafields)) {
10596 $result = $this->fetch_optionals();
10597 if ($result < 0) {
10598 $this->error = $this->db->lasterror();
10599 $this->errors[] = $this->error;
10600 return -4;
10601 }
10602 }
10603
10604 return $this->id;
10605 } else {
10606 return 0;
10607 }
10608 } else {
10609 $this->error = $this->db->lasterror();
10610 $this->errors[] = $this->error;
10611 return -1;
10612 }
10613 }
10614
10622 public function fetchLinesCommon($morewhere = '', $noextrafields = 0)
10623 {
10624 $objectlineclassname = get_class($this).'Line';
10625 if (!class_exists($objectlineclassname)) {
10626 $this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
10627 return -1;
10628 }
10629
10630 $objectline = new $objectlineclassname($this->db);
10631 '@phan-var-force CommonObjectLine $objectline';
10632
10633 $sql = "SELECT ".$objectline->getFieldList('l');
10634 $sql .= " FROM ".$this->db->prefix().$objectline->table_element." as l";
10635 $sql .= " WHERE l.fk_".$this->db->escape($this->element)." = ".((int) $this->id);
10636 if ($morewhere) {
10637 $sql .= $morewhere;
10638 }
10639 if (isset($objectline->fields['position'])) {
10640 $sql .= $this->db->order('position', 'ASC');
10641 }
10642
10643 $resql = $this->db->query($sql);
10644 if ($resql) {
10645 $num_rows = $this->db->num_rows($resql);
10646 $i = 0;
10647 $this->lines = array();
10648 while ($i < $num_rows) {
10649 $obj = $this->db->fetch_object($resql);
10650 if ($obj) {
10651 $newline = new $objectlineclassname($this->db);
10652 '@phan-var-force CommonObjectLine $newline';
10653 $newline->setVarsFromFetchObj($obj);
10654
10655 // Note: extrafields load of line not yet supported
10656 /*
10657 if (empty($noextrafields)) {
10658 // Load extrafields of line
10659 }*/
10660
10661 $this->lines[] = $newline;
10662 }
10663 $i++;
10664 }
10665
10666 return 1;
10667 } else {
10668 $this->error = $this->db->lasterror();
10669 $this->errors[] = $this->error;
10670 return -1;
10671 }
10672 }
10673
10681 public function updateCommon(User $user, $notrigger = 0)
10682 {
10683 dol_syslog(get_class($this)."::updateCommon update", LOG_DEBUG);
10684
10685 $error = 0;
10686
10687 $now = dol_now();
10688
10689 // $this->oldcopy should have been set by the caller of update
10690 //if (empty($this->oldcopy)) {
10691 // dol_syslog("this->oldcopy should have been set by the caller of update (here properties were already modified)", LOG_WARNING);
10692 // $this->oldcopy = dol_clone($this, 2);
10693 //}
10694
10695 $fieldvalues = $this->setSaveQuery();
10696
10697 // Note: Here, $fieldvalues contains same keys (or less) that are inside ->fields
10698
10699 if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) {
10700 $fieldvalues['date_modification'] = $this->db->idate($now);
10701 }
10702 if (array_key_exists('fk_user_modif', $fieldvalues) && !($fieldvalues['fk_user_modif'] > 0)) {
10703 $fieldvalues['fk_user_modif'] = $user->id;
10704 }
10705 if (array_key_exists('user_modification_id', $fieldvalues) && !($fieldvalues['user_modification_id'] > 0)) {
10706 $fieldvalues['user_modification_id'] = $user->id;
10707 }
10708 if (array_key_exists('pass_crypted', $fieldvalues) && property_exists($this, 'pass') && !empty($this->pass)) {
10709 // @phan-suppress-next-line PhanUndeclaredProperty
10710 $tmparray = dol_hash($this->pass, '0', 0, 1);
10711 $fieldvalues['pass_crypted'] = $tmparray['pass_encrypted'];
10712 if (array_key_exists('pass_encoding', $fieldvalues) && property_exists($this, 'pass_encoding')) {
10713 $fieldvalues['pass_encoding'] = $tmparray['pass_encoding'];
10714 }
10715 }
10716 if (array_key_exists('ref', $fieldvalues)) {
10717 $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
10718 }
10719
10720 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
10721
10722 // Add quotes and escape on fields with type string
10723 $keys = array();
10724 $values = array();
10725 $tmp = array();
10726 foreach ($fieldvalues as $k => $v) {
10727 $keys[$k] = $k;
10728 $value = $this->fields[$k];
10729 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
10730 $values[$k] = $this->quote($v, $value);
10731 if (($value["type"] == "text") && !empty($value['arrayofkeyval']) && is_array($value['arrayofkeyval'])) {
10732 // Clean values for text with selectbox
10733 $v = preg_replace('/\s/', ',', $v);
10734 $v = preg_replace('/,+/', ',', $v);
10735 }
10736 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
10737 $tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
10738 }
10739
10740 // Clean and check mandatory fields
10741 foreach ($keys as $key) {
10742 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
10743 $values[$key] = ''; // This is an implicit foreign key field
10744 }
10745 if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
10746 $values[$key] = ''; // This is an explicit foreign key field
10747 }
10748
10749 //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
10750 /*
10751 if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
10752 {
10753 $error++;
10754 $this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
10755 }*/
10756 }
10757
10758 $sql = 'UPDATE '.$this->db->prefix().$this->table_element.' SET '.implode(', ', $tmp).' WHERE rowid='.((int) $this->id);
10759
10760 $this->db->begin();
10761
10762 if (!$error) {
10763 $res = $this->db->query($sql);
10764 if (!$res) {
10765 $error++;
10766 $this->errors[] = $this->db->lasterror();
10767 }
10768 }
10769
10770 // Update extrafield
10771 if (!$error) {
10772 $result = $this->insertExtraFields(); // This delete and reinsert extrafields
10773 if ($result < 0) {
10774 $error++;
10775 }
10776 }
10777
10778 // Triggers
10779 if (!$error && !$notrigger) {
10780 // Call triggers
10781 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
10782 if ($result < 0) {
10783 $error++;
10784 } //Do also here what you must do to rollback action if trigger fail
10785 // End call triggers
10786 }
10787
10788 // Commit or rollback
10789 if ($error) {
10790 $this->db->rollback();
10791 return -1;
10792 } else {
10793 $this->db->commit();
10794 return $this->id;
10795 }
10796 }
10797
10806 public function deleteCommon(User $user, $notrigger = 0, $forcechilddeletion = 0)
10807 {
10808 dol_syslog(get_class($this)."::deleteCommon delete", LOG_DEBUG);
10809
10810 $error = 0;
10811
10812 $this->db->begin();
10813
10814 if ($forcechilddeletion) { // Force also delete of childtables that should lock deletion in standard case when option force is off
10815 foreach ($this->childtables as $table) {
10816 $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
10817 $resql = $this->db->query($sql);
10818 if (!$resql) {
10819 $this->error = $this->db->lasterror();
10820 $this->errors[] = $this->error;
10821 $this->db->rollback();
10822 return -1;
10823 }
10824 }
10825 } elseif (!empty($this->childtables)) { // If object has children linked with a foreign key field, we check all child tables.
10826 $objectisused = $this->isObjectUsed($this->id);
10827 if (!empty($objectisused)) {
10828 dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
10829 $this->error = 'ErrorRecordHasChildren';
10830 $this->errors[] = $this->error;
10831 $this->db->rollback();
10832 return 0;
10833 }
10834 }
10835
10836 // Delete cascade first
10837 if (is_array($this->childtablesoncascade) && !empty($this->childtablesoncascade)) {
10838 foreach ($this->childtablesoncascade as $tabletodelete) {
10839 $deleteFromObject = explode(':', $tabletodelete, 4);
10840 if (count($deleteFromObject) >= 2) {
10841 $className = str_replace('@', '', $deleteFromObject[0]);
10842 $filePath = $deleteFromObject[1];
10843 $columnName = $deleteFromObject[2];
10844 $filter = '';
10845 if (!empty($deleteFromObject[3])) {
10846 $filter = $deleteFromObject[3];
10847 }
10848 if (dol_include_once($filePath)) {
10849 $childObject = new $className($this->db);
10850 if (method_exists($childObject, 'deleteByParentField')) {
10851 '@phan-var-force CommonObject $childObject';
10852 $result = $childObject->deleteByParentField($this->id, $columnName, $filter);
10853 if ($result < 0) {
10854 $error++;
10855 $this->errors[] = $childObject->error;
10856 break;
10857 }
10858 } else {
10859 $error++;
10860 $this->errors[] = "You defined a cascade delete on an object $className/$this->id but there is no method deleteByParentField for it";
10861 break;
10862 }
10863 } else {
10864 $error++;
10865 $this->errors[] = 'Cannot include child class file '.$filePath;
10866 break;
10867 }
10868 } else {
10869 // Delete record in child table
10870 $sql = "DELETE FROM ".$this->db->prefix().$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
10871
10872 $resql = $this->db->query($sql);
10873 if (!$resql) {
10874 $error++;
10875 $this->error = $this->db->lasterror();
10876 $this->errors[] = $this->error;
10877 break;
10878 }
10879 }
10880 }
10881 }
10882
10883 if (!$error) {
10884 if (!$notrigger) {
10885 // Call triggers
10886 $result = $this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
10887 if ($result < 0) {
10888 $error++;
10889 } // Do also here what you must do to rollback action if trigger fail
10890 // End call triggers
10891 }
10892 }
10893
10894 // Delete llx_ecm_files
10895 if (!$error) {
10896 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
10897 if (!$res) {
10898 $error++;
10899 }
10900 }
10901
10902 // Delete linked object
10903 $res = $this->deleteObjectLinked();
10904 if ($res < 0) {
10905 $error++;
10906 }
10907
10908 if (!$error && !empty($this->isextrafieldmanaged)) {
10909 $result = $this->deleteExtraFields();
10910 if ($result < 0) {
10911 $error++;
10912 }
10913 }
10914
10915 if (!$error) {
10916 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.' WHERE rowid='.((int) $this->id);
10917
10918 $resql = $this->db->query($sql);
10919 if (!$resql) {
10920 $error++;
10921 $this->errors[] = $this->db->lasterror();
10922 }
10923 }
10924
10925 // Commit or rollback
10926 if ($error) {
10927 $this->db->rollback();
10928 return -1;
10929 } else {
10930 $this->db->commit();
10931 return 1;
10932 }
10933 }
10934
10946 public function deleteByParentField($parentId = 0, $parentField = '', $filter = '', $filtermode = "AND")
10947 {
10948 global $user;
10949
10950 $error = 0;
10951 $deleted = 0;
10952
10953 if (!empty($parentId) && !empty($parentField)) {
10954 $this->db->begin();
10955
10956 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
10957 $sql .= " WHERE ".$this->db->sanitize($parentField)." = ".(int) $parentId;
10958
10959 // Manage filter
10960 $errormessage = '';
10961 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
10962 if ($errormessage) {
10963 $this->errors[] = $errormessage;
10964 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
10965 return -1;
10966 }
10967
10968 $resql = $this->db->query($sql);
10969 if (!$resql) {
10970 $this->errors[] = $this->db->lasterror();
10971 $error++;
10972 } else {
10973 while ($obj = $this->db->fetch_object($resql)) {
10974 $result = $this->fetch($obj->rowid); // @phpstan-ignore-line
10975 if ($result < 0) {
10976 $error++;
10977 $this->errors[] = $this->error;
10978 } else {
10979 $result = $this->delete($user); // @phpstan-ignore-line
10980 if ($result < 0) {
10981 $error++;
10982 $this->errors[] = $this->error;
10983 } else {
10984 $deleted++;
10985 }
10986 }
10987 }
10988 }
10989
10990 if (empty($error)) {
10991 $this->db->commit();
10992 return $deleted;
10993 } else {
10994 $this->error = implode(', ', $this->errors);
10995 $this->db->rollback();
10996 return $error * -1;
10997 }
10998 }
10999
11000 return $deleted;
11001 }
11002
11011 public function deleteLineCommon(User $user, $idline, $notrigger = 0)
11012 {
11013 $error = 0;
11014
11015 $tmpforobjectclass = get_class($this);
11016 $tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line';
11017
11018 $this->db->begin();
11019
11020 // Call trigger
11021 $result = $this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user);
11022 if ($result < 0) {
11023 $error++;
11024 }
11025 // End call triggers
11026
11027 if (empty($error)) {
11028 $sql = "DELETE FROM ".$this->db->prefix().$this->table_element_line;
11029 $sql .= " WHERE rowid = ".((int) $idline);
11030
11031 $resql = $this->db->query($sql);
11032 if (!$resql) {
11033 $this->error = "Error ".$this->db->lasterror();
11034 $error++;
11035 }
11036 }
11037
11038 if (empty($error)) {
11039 // Remove extrafields
11040 $tmpobjectline = new $tmpforobjectlineclass($this->db);
11041 '@phan-var-force CommonObjectLine $tmpobjectline';
11042 if (!isset($tmpobjectline->isextrafieldmanaged) || !empty($tmpobjectline->isextrafieldmanaged)) {
11043 $tmpobjectline->id = $idline;
11044 $result = $tmpobjectline->deleteExtraFields();
11045 if ($result < 0) {
11046 $error++;
11047 $this->error = "Error ".get_class($this)."::deleteLineCommon deleteExtraFields error -4 ".$tmpobjectline->error;
11048 }
11049 }
11050 }
11051
11052 if (empty($error)) {
11053 $this->db->commit();
11054 return 1;
11055 } else {
11056 dol_syslog(get_class($this)."::deleteLineCommon ERROR:".$this->error, LOG_ERR);
11057 $this->db->rollback();
11058 return -1;
11059 }
11060 }
11061
11062
11072 public function setStatusCommon($user, $status, $notrigger = 0, $triggercode = '')
11073 {
11074 $error = 0;
11075
11076 $this->db->begin();
11077
11078 $statusfield = 'status';
11079 if (in_array($this->element, array('don', 'donation', 'shipping'))) {
11080 $statusfield = 'fk_statut';
11081 }
11082
11083 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
11084 $sql .= " SET ".$statusfield." = ".((int) $status);
11085 $sql .= " WHERE rowid = ".((int) $this->id);
11086
11087 if ($this->db->query($sql)) {
11088 if (!$error) {
11089 $this->oldcopy = clone $this;
11090 }
11091
11092 if (!$error && !$notrigger) {
11093 // Call trigger
11094 $result = $this->call_trigger($triggercode, $user);
11095 if ($result < 0) {
11096 $error++;
11097 }
11098 }
11099
11100 if (!$error) {
11101 $this->status = $status;
11102 if (property_exists($this, 'statut')) { // For backward compatibility
11103 $this->statut = $status;
11104 }
11105 $this->db->commit();
11106 return 1;
11107 } else {
11108 $this->db->rollback();
11109 return -1;
11110 }
11111 } else {
11112 $this->error = $this->db->error();
11113 $this->db->rollback();
11114 return -1;
11115 }
11116 }
11117
11124 public function initAsSpecimenCommon()
11125 {
11126 global $user;
11127
11128 $this->id = 0;
11129 $this->specimen = 1;
11130 $fields = array(
11131 'label' => 'This is label',
11132 'ref' => 'ABCD1234',
11133 'description' => 'This is a description',
11134 'qty' => 123.12,
11135 'note_public' => 'Public note',
11136 'note_private' => 'Private note',
11137 'date_creation' => (dol_now() - 3600 * 48),
11138 'date_modification' => (dol_now() - 3600 * 24),
11139 'fk_user_creat' => $user->id,
11140 'fk_user_modif' => $user->id,
11141 'date' => dol_now(),
11142 );
11143 foreach ($fields as $key => $value) {
11144 if (array_key_exists($key, $this->fields)) {
11145 $this->{$key} = $value; // @phpstan-ignore-line
11146 }
11147 }
11148
11149 // Force values to default values when known
11150 if (property_exists($this, 'fields')) {
11151 foreach ($this->fields as $key => $value) {
11152 // If fields are already set, do nothing
11153 if (array_key_exists($key, $fields)) {
11154 continue;
11155 }
11156
11157 if (!empty($value['default'])) {
11158 $this->$key = $value['default'];
11159 }
11160 }
11161 }
11162
11163 return 1;
11164 }
11165
11166
11167 /* Part for comments */
11168
11174 public function fetchComments()
11175 {
11176 require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
11177
11178 $comment = new Comment($this->db);
11179 $result = $comment->fetchAllFor($this->element, $this->id);
11180 if ($result < 0) {
11181 $this->errors = array_merge($this->errors, $comment->errors);
11182 return -1;
11183 } else {
11184 $this->comments = $comment->comments;
11185 }
11186 return count($this->comments);
11187 }
11188
11194 public function getNbComments()
11195 {
11196 return count($this->comments);
11197 }
11198
11205 public function trimParameters($parameters)
11206 {
11207 if (!is_array($parameters)) {
11208 return;
11209 }
11210 foreach ($parameters as $parameter) {
11211 if (isset($this->$parameter)) {
11212 $this->$parameter = trim($this->$parameter);
11213 }
11214 }
11215 }
11216
11217 /* Part for categories/tags */
11218
11229 public function getCategoriesCommon($type_categ)
11230 {
11231 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
11232
11233 // Get current categories
11234 $c = new Categorie($this->db);
11235 $existing = $c->containing($this->id, $type_categ, 'id');
11236
11237 return $existing;
11238 }
11239
11252 public function setCategoriesCommon($categories, $type_categ = '', $remove_existing = true)
11253 {
11254 // Handle single category
11255 if (!is_array($categories)) {
11256 $categories = array($categories);
11257 }
11258
11259 dol_syslog(get_class($this)."::setCategoriesCommon Object Id:".$this->id.' type_categ:'.$type_categ.' nb tag add:'.count($categories), LOG_DEBUG);
11260
11261 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
11262
11263 if (empty($type_categ)) {
11264 dol_syslog(__METHOD__.': Type '.$type_categ.'is an unknown category type. Done nothing.', LOG_ERR);
11265 return -1;
11266 }
11267
11268 // Get current categories
11269 $c = new Categorie($this->db);
11270 $existing = $c->containing($this->id, $type_categ, 'id');
11271 if ($remove_existing) {
11272 // Diff
11273 if (is_array($existing)) {
11274 $to_del = array_diff($existing, $categories);
11275 $to_add = array_diff($categories, $existing);
11276 } else {
11277 $to_del = array(); // Nothing to delete
11278 $to_add = $categories;
11279 }
11280 } else {
11281 $to_del = array(); // Nothing to delete
11282 $to_add = array_diff($categories, $existing);
11283 }
11284
11285 $error = 0;
11286 $ok = 0;
11287
11288 // Process
11289 foreach ($to_del as $del) {
11290 if ($c->fetch($del) > 0) {
11291 $result = $c->del_type($this, $type_categ);
11292 if ($result < 0) {
11293 $error++;
11294 $this->error = $c->error;
11295 $this->errors = $c->errors;
11296 break;
11297 } else {
11298 $ok += $result;
11299 }
11300 }
11301 }
11302 foreach ($to_add as $add) {
11303 if ($c->fetch($add) > 0) {
11304 $result = $c->add_type($this, $type_categ);
11305 if ($result < 0) {
11306 $error++;
11307 $this->error = $c->error;
11308 $this->errors = $c->errors;
11309 break;
11310 } else {
11311 $ok += $result;
11312 }
11313 }
11314 }
11315
11316 return $error ? (-1 * $error) : $ok;
11317 }
11318
11327 public function cloneCategories($fromId, $toId, $type = '')
11328 {
11329 $this->db->begin();
11330
11331 if (empty($type)) {
11332 $type = $this->table_element;
11333 }
11334
11335 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
11336 $categorystatic = new Categorie($this->db);
11337
11338 $sql = "INSERT INTO ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type])." (fk_categorie, fk_product)";
11339 $sql .= " SELECT fk_categorie, $toId FROM ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type]);
11340 $sql .= " WHERE fk_product = ".((int) $fromId);
11341
11342 if (!$this->db->query($sql)) {
11343 $this->error = $this->db->lasterror();
11344 $this->db->rollback();
11345 return -1;
11346 }
11347
11348 $this->db->commit();
11349 return 1;
11350 }
11351
11358 public function deleteEcmFiles($mode = 0)
11359 {
11360 global $conf;
11361
11362 $this->db->begin();
11363
11364 // Delete in database with mode 0
11365 if ($mode == 0) {
11366 switch ($this->element) {
11367 case 'propal':
11368 $element = 'propale';
11369 break;
11370 case 'product':
11371 $element = 'produit';
11372 break;
11373 case 'order_supplier':
11374 $element = 'fournisseur/commande';
11375 break;
11376 case 'invoice_supplier':
11377 // Special cases that need to use get_exdir to get real dir of object
11378 // In future, all object should use this to define path of documents.
11379 $element = 'fournisseur/facture/'.get_exdir($this->id, 2, 0, 1, $this, 'invoice_supplier');
11380 break;
11381 case 'shipping':
11382 $element = 'expedition/sending';
11383 break;
11384 case 'task':
11385 case 'project_task':
11386 require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
11387
11388 $project_result = $this->fetchProject();
11389 if ($project_result >= 0) {
11390 $element = 'projet/'.dol_sanitizeFileName($this->project->ref).'/';
11391 }
11392 // no break
11393 default:
11394 $element = $this->element;
11395 }
11396 '@phan-var-force string $element';
11397
11398 // Delete ecm_files_extrafields with mode 0 (using name)
11399 $sql = "DELETE FROM ".$this->db->prefix()."ecm_files_extrafields WHERE fk_object IN (";
11400 $sql .= " SELECT rowid FROM ".$this->db->prefix()."ecm_files WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
11401 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
11402 $sql .= ")";
11403
11404 if (!$this->db->query($sql)) {
11405 $this->error = $this->db->lasterror();
11406 $this->db->rollback();
11407 return false;
11408 }
11409
11410 // Delete ecm_files with mode 0 (using name)
11411 $sql = "DELETE FROM ".$this->db->prefix()."ecm_files";
11412 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
11413 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
11414
11415 if (!$this->db->query($sql)) {
11416 $this->error = $this->db->lasterror();
11417 $this->db->rollback();
11418 return false;
11419 }
11420 }
11421
11422 // Delete in database with mode 1
11423 if ($mode == 1) {
11424 $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files_extrafields";
11425 $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).")";
11426 $resql = $this->db->query($sql);
11427 if (!$resql) {
11428 $this->error = $this->db->lasterror();
11429 $this->db->rollback();
11430 return false;
11431 }
11432
11433 $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files";
11434 $sql .= " WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? "" : "@".$this->module))."' AND src_object_id = ".((int) $this->id);
11435 $resql = $this->db->query($sql);
11436 if (!$resql) {
11437 $this->error = $this->db->lasterror();
11438 $this->db->rollback();
11439 return false;
11440 }
11441 }
11442
11443 $this->db->commit();
11444 return true;
11445 }
11446}
$id
Definition account.php:48
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:66
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:463
$c
Definition line.php:327
$object ref
Definition info.php:89
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.
deleteLineCommon(User $user, $idline, $notrigger=0)
Delete a line of object in database.
isEmpty()
isEmpty We consider CommonObject isEmpty if this->id is empty
clearFieldError($fieldKey)
clear validation message result for a field
static getCountOfItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
Count items linked to an object id in association table.
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
update_price($exclspec=0, $roundingadjust='auto', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
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.
liste_contact($statusoflink=-1, $source='external', $list=0, $code='', $status=-1, $arrayoftcids=array())
Get array of all contacts for an object.
fetchObjectFrom($table, $field, $key, $element=null)
Load object from specific field.
fetchProject()
Load the project with id $this->fk_project into this->project.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
static deleteAllItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
Function used to remove all items linked to an object id in association table.
setFieldError($fieldKey, $msg='')
set validation error message a field
validateField($fields, $fieldKey, $fieldValue)
Return validation test result for a field.
getDataToShowPhoto($modulepart, $imagesize)
Function used to get the logos or photos of an object.
setMulticurrencyCode($code)
Change the multicurrency code.
add_element_resource($resource_id, $resource_type, $busy=0, $mandatory=0)
Add resources to the current object : add entry into llx_element_resources Need $this->element & $thi...
emtpyObjectVars()
Sets all object fields to null.
fetch_projet()
Load the project with id $this->fk_project into this->project.
getIdContact($source, $code, $status=0)
Return id of contacts for a source and a contact code.
setExtraField($key, $value)
Convenience method for setting the value of an extrafield without actually updating it in the databas...
setDocModel($user, $modelpdf)
Set last model used by doc generator.
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid=0, $f_user=null, $notrigger=0)
Delete all links between an object $this.
getExtraField($key)
Convenience method for retrieving the value of an extrafield without actually fetching it from the da...
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.
setBankAccount($fk_account, $notrigger=0, $userused=null)
Change the bank account.
setExtraParameters()
Set extra parameters.
setErrorsFromObject($object)
setErrorsFromObject
setShippingMethod($shipping_method_id, $notrigger=0, $userused=null)
Change the shipping method.
update_note_public($note)
Update public note (kept for backward compatibility)
getSpecialCode($lineid)
Get special code of a line.
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...
hasProductsOrServices($predefined=-1)
Function to say how many lines object contains.
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check if an object id or ref exists If you don't need or want to instantiate the object and just need...
isObjectUsed($id=0, $entity=0)
Function to check if an object is used by others (by children).
updateRangOfLine($rowid, $rang)
Update position of line (rang)
getDefaultCreateValueFor($fieldname, $alternatevalue=null, $type='alphanohtml')
Return the default value to use for a field when showing the create form of object.
createCommon(User $user, $notrigger=0)
Create object in the database.
getTotalWeightVolume()
Return into unit=0, the calculated total of weight and volume of all lines * qty Calculate by adding ...
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.
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.
updateCommon(User $user, $notrigger=0)
Update object into database.
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.
deprecatedProperties()
Provide list of deprecated properties and replacements.
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.
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).
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.
getListContactId($source='external')
Return list of id of contacts of object.
setWarehouse($warehouse_id)
Change the warehouse.
setDeliveryAddress($id)
Define delivery address.
fetchBarCode()
Load data for barcode into properties ->barcode_type* Properties ->barcode_type that is id of barcode...
getTotalDiscount()
Function that returns the total amount HT of discounts applied for all lines.
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.
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.
fetchLinesCommon($morewhere='', $noextrafields=0)
Load object in memory from the 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.
getElementType()
Return an element type string formatted like element_element target_type and source_type.
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.
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 ...
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.
deleteByParentField($parentId=0, $parentField='', $filter='', $filtermode="AND")
Delete all child object from a parent ID.
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.
fetchCommon($id, $ref=null, $morewhere='', $noextrafields=0)
Load object in memory from the database.
deleteCommon(User $user, $notrigger=0, $forcechilddeletion=0)
Delete object in database.
getFormatedCustomerRef($objref)
Return customer ref for screen output.
getTooltipContentArray($params)
Return array of data 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...
showOptionals($extrafields, $mode='view', $params=null, $keysuffix='', $keyprefix='', $onetrtd='', $display_type='card')
Function to show lines of extrafields with output data.
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.
addThumbs($file, $quality=50)
Build thumb.
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.
static isEmptyValue($v, string $type)
Return if a value is "empty" for a mandatory vision.
const TYPE_CREDIT_NOTE
Credit note invoice.
Class to manage generation of HTML components Only common components must be here.
Class to manage triggers.
Parent class to manage intervention document templates.
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.
Class toolbox to validate values.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:171
getCountry($searchkey, $withcode='', $dbtouse=null, $outputlangs=null, $entconv=1, $searchlabel='')
Return country label, code or id from an id, code or label.
getState($id, $withcode='0', $dbtouse=null, $withregion=0, $outputlangs=null, $entconv=1)
Return state translated from an id.
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:244
dol_meta_create($object)
Create a meta file with document file into same directory.
completeFileArrayWithDatabaseInfo(&$filearray, $relativedir, $object=null)
Complete $filearray with data from database.
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.
dol_is_file($pathoffile)
Return if path is a file.
dol_dir_list($utf8_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:63
dol_delete_preview($object)
Delete all preview files linked to object instance.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formatted for view output Used into pdf and HTML pages.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
dol_print_ip($ip, $mode=0)
Return an IP formatted to be shown on screen.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_format_address($object, $withcountry=0, $sep="\n", $outputlangs=null, $mode=0, $extralangcode='')
Return a formatted address (part address/zip/town/state) according to country rules.
dol_print_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0, $morecss='paddingright')
Format phone numbers according to country.
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 '.
getCallerInfoString()
Get caller info as a string that can be appended to a log message.
dol_string_nospecial($str, $newstr='_', $badcharstoreplace='', $badcharstoremove='', $keepspaces=0)
Clean a string from all punctuation characters to use it as a ref or login.
dol_eval($s, $returnvalue=1, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $allowothertags=array())
Show a picto called object_picto (generic function)
dol_print_url($url, $target='_blank', $max=32, $withpicto=0, $morecss='')
Show Url link.
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.
getPictoForType($key, $morecss='')
Return the picto for a data type.
forgeSQLFromUniversalSearchCriteria($filter, &$errorstr='', $noand=0, $nopar=0, $noerror=0)
forgeSQLFromUniversalSearchCriteria
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getElementProperties($elementType)
Get an array with properties of an element.
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs=null, $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by the value of a given key, which produces ascending (default) or descending out...
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
dolPrintHTMLForAttribute($s, $escapeonlyhtmltags=0, $allowothertags=array())
Return a string ready to be output into an HTML attribute (alt, title, data-html, ....
dol_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0, $morecss='paddingrightonly')
Show EMail link formatted for HTML output.
getImageFileNameForSize($file, $extName, $extImgTarget='')
Return the filename of file to get the thumbs.
yn($yesno, $format=1, $color=0)
Return yes or no in current language.
get_date_range($date_start, $date_end, $format='', $outputlangs=null, $withparenthesis=1)
Format output for start and end date.
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.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_clone($object, $native=2)
Create a clone of instance of object (new instance with same value for each properties) With native =...
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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.
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...
dol_sanitizeFileName($str, $newstr='_', $unaccent=1, $includequotes=0)
Clean a string to use it as a file name.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
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.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
dol_sanitizePathName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a path name.
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).
if(!defined( 'IMAGETYPE_WEBP')) getDefaultImageSizes()
Return default values for image sizes.
dol_getImageSize($file, $url=false)
Return size of image file on disk (Supported extensions are gif, jpg, png, bmp and webp)
image_format_supported($file, $acceptsvg=0)
Return if a filename is file name of a supported image format.
query($query, $usesavepoint=0, $type='auto', $result_mode=0)
Execute a SQL request and return the resultset.
global $conf
The following vars must be defined: $type2label $form $conf, $lang, The following vars may also be de...
Definition member.php:79
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:90
$conf db user
Active Directory does not allow anonymous connections.
Definition repair.php:154
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:150
getRandomPassword($generic=false, $replaceambiguouschars=null, $length=32)
Return a generated password using default module.
dol_hash($chain, $type='0', $nosalt=0, $mode=0)
Returns a hash (non reversible encryption) of a string.
dolEncrypt($chain, $key='', $ciphering='', $forceseed='')
Encode a string with a symmetric encryption.
dolDecrypt($chain, $key='')
Decode a string with a symmetric encryption.