dolibarr 21.0.0-alpha
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 *
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 3 of the License, or
25 * (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program. If not, see <https://www.gnu.org/licenses/>.
34 */
35
42require_once DOL_DOCUMENT_ROOT.'/core/class/doldeprecationhandler.class.php';
43
49abstract class CommonObject
50{
51 use DolDeprecationHandler;
52
53 const TRIGGER_PREFIX = ''; // to be overridden in child class implementations, i.e. 'BILL', 'TASK', 'PROPAL', etc.
54
58 public $module;
59
63 public $db;
64
68 public $id;
69
73 public $entity;
74
79 public $error;
80
84 public $errorhidden;
85
89 public $errors = array();
90
94 private $validateFieldsErrors = array();
95
99 public $element;
100
105 public $fk_element;
106
112 public $element_for_permission;
113
117 public $table_element;
118
122 public $table_element_line = '';
123
128 public $ismultientitymanaged;
129
133 public $import_key;
134
138 public $array_options = array();
139
140
144 public $fields = array();
145
150 public $array_languages = null; // Value is array() when load already tried
151
155 public $contacts_ids;
156
160 public $linked_objects;
161
165 public $linkedObjectsIds;
166
170 public $linkedObjects;
171
175 private $linkedObjectsFullLoaded = array();
176
180 public $oldcopy;
181
185 public $oldref;
186
190 protected $table_ref_field = '';
191
195 public $restrictiononfksoc = 0;
196
197
198 // The following vars are used by some objects only.
199 // We keep these properties in CommonObject in order to provide common methods using them.
200
204 public $context = array();
205
209 public $actionmsg;
213 public $actionmsg2;
214
218 public $canvas;
219
224 public $project;
225
230 public $fk_project;
231
237 private $projet;
238
244 public $fk_projet;
245
250 public $contact;
251
256 public $contact_id;
257
262 public $thirdparty;
263
268 public $user;
269
274 public $origin_type;
275
280 public $origin_id;
281
286
292 public $origin;
293
302 private $expedition;
303
309 private $livraison;
310
316 private $commandeFournisseur;
317
318
322 public $ref;
323
327 public $ref_ext;
328
332 public $ref_previous;
333
337 public $ref_next;
338
342 public $newref;
343
350 public $statut;
351
359 public $status;
360
361
366 public $country;
367
372 public $country_id;
373
378 public $country_code;
379
384 public $state;
385
390 public $state_id;
391
396 public $fk_departement;
397
402 public $state_code;
403
408 public $region_id;
409
414 public $region_code;
415
420 public $region;
421
422
427 public $barcode_type;
428
433 public $barcode_type_code;
434
439 public $barcode_type_label;
440
445 public $barcode_type_coder;
446
451 public $mode_reglement_id;
452
457 public $cond_reglement_id;
458
462 public $demand_reason_id;
463
468 public $transport_mode_id;
469
477 private $cond_reglement; // Private to call DolDeprecationHandler
481 protected $depr_cond_reglement; // Internal value for deprecation
482
488 public $fk_delivery_address;
489
494 public $shipping_method_id;
495
500 public $shipping_method;
501
502 // Multicurrency
506 public $fk_multicurrency;
507
512 public $multicurrency_code;
513
518 public $multicurrency_tx;
519
523 public $multicurrency_total_ht;
524
528 public $multicurrency_total_tva;
529
533 public $multicurrency_total_ttc;
534
538 public $multicurrency_total_localtax1; // not in database
539
543 public $multicurrency_total_localtax2; // not in database
544
549 public $model_pdf;
550
555 public $last_main_doc;
556
562 public $fk_bank;
563
568 public $fk_account;
569
574 public $note_public;
575
580 public $note_private;
581
587 public $note;
588
593 public $total_ht;
594
599 public $total_tva;
600
605 public $total_localtax1;
606
611 public $total_localtax2;
612
617 public $total_ttc;
618
619
623 public $lines;
624
628 public $actiontypecode;
629
634 public $comments = array();
635
639 public $name;
640
644 public $lastname;
645
649 public $firstname;
650
654 public $civility_id;
655
656 // Dates
660 public $date_creation;
661
665 public $date_validation;
666
670 public $date_modification;
671
677 public $tms;
678
684 private $date_update;
685
689 public $date_cloture;
690
695 public $user_author;
696
701 public $user_creation;
702
706 public $user_creation_id;
707
712 public $user_valid;
713
718 public $user_validation;
719
723 public $user_validation_id;
724
728 public $user_closing_id;
729
734 public $user_modification;
735
739 public $user_modification_id;
740
745 public $fk_user_creat;
746
751 public $fk_user_modif;
752
753
757 public $next_prev_filter;
758
762 public $specimen = 0;
763
767 public $sendtoid;
768
774 private $alreadypaid;
775
779 public $totalpaid;
780
784 public $labelStatus = array();
785
789 public $labelStatusShort = array();
790
794 public $tpl;
795
796
800 public $showphoto_on_popup;
801
805 public $nb = array();
806
810 public $nbphoto;
811
815 public $output;
816
820 public $extraparams = array();
821
825 protected $childtables = array();
826
832 protected $childtablesoncascade = array();
833
837 public $product;
838
842 public $cond_reglement_supplier_id;
843
849 public $deposit_percent;
850
851
855 public $retained_warranty_fk_cond_reglement;
856
860 public $warehouse_id;
861
865 public $isextrafieldmanaged = 0;
866
867
868 // No constructor as it is an abstract class
869
875 protected function deprecatedProperties()
876 {
877 return array(
878 'alreadypaid' => 'totalpaid',
879 'cond_reglement' => 'depr_cond_reglement',
880 //'note' => 'note_private', // Some classes needs ->note and others need ->note_public/private so we can't manage deprecation for this field with dolDeprecationHandler
881 'commandeFournisseur' => 'origin_object',
882 'expedition' => 'origin_object',
883 'fk_project' => 'fk_project',
884 'livraison' => 'origin_object',
885 'projet' => 'project',
886 'statut' => 'status',
887 );
888 }
889
890
901 public static function isExistingObject($element, $id, $ref = '', $ref_ext = '')
902 {
903 global $db, $conf;
904
905 $sql = "SELECT rowid, ref, ref_ext";
906 $sql .= " FROM ".$db->prefix().$element;
907 $sql .= " WHERE entity IN (".getEntity($element).")";
908
909 if ($id > 0) {
910 $sql .= " AND rowid = ".((int) $id);
911 } elseif ($ref) {
912 $sql .= " AND ref = '".$db->escape($ref)."'";
913 } elseif ($ref_ext) {
914 $sql .= " AND ref_ext = '".$db->escape($ref_ext)."'";
915 } else {
916 $error = 'ErrorWrongParameters';
917 dol_syslog(get_class()."::isExistingObject ".$error, LOG_ERR);
918 return -1;
919 }
920 if ($ref || $ref_ext) { // Because the same ref can exists in 2 different entities, we force the current one in priority
921 $sql .= " AND entity = ".((int) $conf->entity);
922 }
923
924 dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
925 $resql = $db->query($sql);
926 if ($resql) {
927 $num = $db->num_rows($resql);
928 if ($num > 0) {
929 return 1;
930 } else {
931 return 0;
932 }
933 }
934 return -1;
935 }
936
942 public function isEmpty()
943 {
944 return (empty($this->id));
945 }
946
954 {
955 if (!empty($object->error)) {
956 $this->error = $object->error;
957 }
958 if (!empty($object->errors)) {
959 $this->errors = array_merge($this->errors, $object->errors);
960 }
961 }
962
970 public function getTooltipContentArray($params)
971 {
972 return [];
973 }
974
982 public function getTooltipContent($params)
983 {
984 global $action, $extrafields, $langs, $hookmanager;
985
986 // If there is too much extrafields, we do not include them into tooltip
987 $MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP = getDolGlobalInt('MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP', 3);
988
989 $data = $this->getTooltipContentArray($params);
990 $count = 0;
991
992 // Add extrafields
993 if (!empty($extrafields->attributes[$this->table_element]['label'])) {
994 $data['opendivextra'] = '<div class="centpercent wordbreak divtooltipextra">';
995 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
996 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
997 continue;
998 }
999 if ($count >= abs($MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP)) {
1000 $data['more_extrafields'] = '<br>...';
1001 break;
1002 }
1003 $enabled = 1;
1004 if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
1005 $enabled = (int) dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
1006 }
1007 if ($enabled && isset($extrafields->attributes[$this->table_element]['list'][$key])) {
1008 $enabled = (int) dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
1009 }
1010 $perms = 1;
1011 if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
1012 $perms = (int) dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
1013 }
1014 if (empty($enabled)) {
1015 continue; // 0 = Never visible field
1016 }
1017 if (abs($enabled) != 1 && abs($enabled) != 3 && abs($enabled) != 5 && abs($enabled) != 4) {
1018 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list <> 4 = not visible at the creation
1019 }
1020 if (empty($perms)) {
1021 continue; // 0 = Not visible
1022 }
1023 if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
1024 $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
1025 }
1026 $labelextra = $langs->trans((string) $extrafields->attributes[$this->table_element]['label'][$key]);
1027 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
1028 $data[$key] = '<br><b><u>'. $labelextra . '</u></b>';
1029 } else {
1030 $value = (empty($this->array_options['options_' . $key]) ? '' : $this->array_options['options_' . $key]);
1031 $data[$key] = '<br><b>'. $labelextra . ':</b> ' . $extrafields->showOutputField($key, $value, '', $this->table_element);
1032 $count++;
1033 }
1034 }
1035 $data['closedivextra'] = '</div>';
1036 }
1037
1038 $hookmanager->initHooks(array($this->element . 'dao'));
1039 $parameters = array(
1040 'tooltipcontentarray' => &$data,
1041 'params' => $params,
1042 );
1043 // Note that $action and $object may have been modified by some hooks
1044 $hookmanager->executeHooks('getTooltipContent', $parameters, $this, $action);
1045
1046 //var_dump($data);
1047 $label = implode($data);
1048
1049 return $label;
1050 }
1051
1052
1058 public function errorsToString()
1059 {
1060 return $this->error.(is_array($this->errors) ? (($this->error != '' ? ', ' : '').implode(', ', $this->errors)) : '');
1061 }
1062
1063
1070 public function getFormatedCustomerRef($objref)
1071 {
1072 global $hookmanager;
1073
1074 $parameters = array('objref' => $objref);
1075 $action = '';
1076 $reshook = $hookmanager->executeHooks('getFormatedCustomerRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1077 if ($reshook > 0) {
1078 return $hookmanager->resArray['objref'];
1079 }
1080 return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
1081 }
1082
1089 public function getFormatedSupplierRef($objref)
1090 {
1091 global $hookmanager;
1092
1093 $parameters = array('objref' => $objref);
1094 $action = '';
1095 $reshook = $hookmanager->executeHooks('getFormatedSupplierRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
1096 if ($reshook > 0) {
1097 return $hookmanager->resArray['objref'];
1098 }
1099 return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
1100 }
1101
1111 public function getFullAddress($withcountry = 0, $sep = "\n", $withregion = 0, $extralangcode = '')
1112 {
1113 if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country))) {
1114 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
1115 $tmparray = getCountry($this->country_id, 'all');
1116 $this->country_code = $tmparray['code'];
1117 $this->country = $tmparray['label'];
1118 }
1119
1120 if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_code))) {
1121 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
1122 $tmparray = getState($this->state_id, 'all', null, 1);
1123 $this->state_code = $tmparray['code'];
1124 $this->state = $tmparray['label'];
1125 $this->region_code = $tmparray['region_code'];
1126 $this->region = $tmparray['region'];
1127 }
1128
1129 return dol_format_address($this, $withcountry, $sep, null, 0, $extralangcode);
1130 }
1131
1132
1141 public function getLastMainDocLink($modulepart, $initsharekey = 0, $relativelink = 0)
1142 {
1143 global $user, $dolibarr_main_url_root;
1144
1145 if (empty($this->last_main_doc)) {
1146 return ''; // No way to known which document name to use
1147 }
1148
1149 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
1150 $ecmfile = new EcmFiles($this->db);
1151 $result = $ecmfile->fetch(0, '', $this->last_main_doc);
1152 if ($result < 0) {
1153 $this->error = $ecmfile->error;
1154 $this->errors = $ecmfile->errors;
1155 return -1;
1156 }
1157
1158 if (empty($ecmfile->id)) { // No entry into file index already exists, we should initialize the shared key manually.
1159 // Add entry into index
1160 if ($initsharekey) {
1161 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
1162
1163 // 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
1164 /*
1165 $ecmfile->filepath = $rel_dir;
1166 $ecmfile->filename = $filename;
1167 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
1168 $ecmfile->fullpath_orig = '';
1169 $ecmfile->gen_or_uploaded = 'generated';
1170 $ecmfile->description = ''; // indexed content
1171 $ecmfile->keywords = ''; // keyword content
1172 $ecmfile->share = getRandomPassword(true);
1173 $result = $ecmfile->create($user);
1174 if ($result < 0)
1175 {
1176 $this->error = $ecmfile->error;
1177 $this->errors = $ecmfile->errors;
1178 }
1179 */
1180 } else {
1181 return '';
1182 }
1183 } elseif (empty($ecmfile->share)) { // Entry into file index already exists but no share key is defined.
1184 // Add entry into index
1185 if ($initsharekey) {
1186 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
1187 $ecmfile->share = getRandomPassword(true);
1188 $ecmfile->update($user);
1189 } else {
1190 return '';
1191 }
1192 }
1193 // Define $urlwithroot
1194 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1195 // This is to use external domain name found into config file
1196 //if (DOL_URL_ROOT && ! preg_match('/\/$/', $urlwithouturlroot) && ! preg_match('/^\//', DOL_URL_ROOT)) $urlwithroot=$urlwithouturlroot.'/'.DOL_URL_ROOT;
1197 //else
1198 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT;
1199 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1200
1201 $forcedownload = 0;
1202
1203 $paramlink = '';
1204 //if (!empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart; // For sharing with hash (so public files), modulepart is not required.
1205 //if (!empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; // For sharing with hash (so public files), entity is not required.
1206 //$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath); // No need of name of file for public link, we will use the hash
1207 if (!empty($ecmfile->share)) {
1208 $paramlink .= ($paramlink ? '&' : '').'hashp='.$ecmfile->share; // Hash for public share
1209 }
1210 if ($forcedownload) {
1211 $paramlink .= ($paramlink ? '&' : '').'attachment=1';
1212 }
1213
1214 if ($relativelink) {
1215 $linktoreturn = 'document.php'.($paramlink ? '?'.$paramlink : '');
1216 } else {
1217 $linktoreturn = $urlwithroot.'/document.php'.($paramlink ? '?'.$paramlink : '');
1218 }
1219
1220 // Here $ecmfile->share is defined
1221 return $linktoreturn;
1222 }
1223
1224
1225 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1235 public function add_contact($fk_socpeople, $type_contact, $source = 'external', $notrigger = 0)
1236 {
1237 // phpcs:enable
1238 global $user, $langs;
1239
1240
1241 dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
1242
1243 // Check parameters
1244 if ($fk_socpeople <= 0) {
1245 $langs->load("errors");
1246 $this->error = $langs->trans("ErrorWrongValueForParameterX", "1");
1247 dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
1248 return -1;
1249 }
1250 if (!$type_contact) {
1251 $langs->load("errors");
1252 $this->error = $langs->trans("ErrorWrongValueForParameterX", "2");
1253 dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
1254 return -2;
1255 }
1256
1257 $id_type_contact = 0;
1258 if (is_numeric($type_contact)) {
1259 $id_type_contact = $type_contact;
1260 } else {
1261 // We look for id type_contact
1262 $sql = "SELECT tc.rowid";
1263 $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1264 $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1265 $sql .= " AND tc.source='".$this->db->escape($source)."'";
1266 $sql .= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
1267 //print $sql;
1268 $resql = $this->db->query($sql);
1269 if ($resql) {
1270 $obj = $this->db->fetch_object($resql);
1271 if ($obj) {
1272 $id_type_contact = $obj->rowid;
1273 }
1274 }
1275 }
1276
1277 if ($id_type_contact == 0) {
1278 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");
1279 return 0;
1280 }
1281
1282 $datecreate = dol_now();
1283
1284 // Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
1285 $TListeContacts = $this->liste_contact(-1, $source);
1286 $already_added = false;
1287 if (is_array($TListeContacts) && !empty($TListeContacts)) {
1288 foreach ($TListeContacts as $array_contact) {
1289 if ($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
1290 $already_added = true;
1291 break;
1292 }
1293 }
1294 }
1295
1296 if (!$already_added) {
1297 $this->db->begin();
1298
1299 // Insert into database
1300 $sql = "INSERT INTO ".$this->db->prefix()."element_contact";
1301 $sql .= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
1302 $sql .= " VALUES (".$this->id.", ".((int) $fk_socpeople)." , ";
1303 $sql .= "'".$this->db->idate($datecreate)."'";
1304 $sql .= ", 4, ".((int) $id_type_contact);
1305 $sql .= ")";
1306
1307 $resql = $this->db->query($sql);
1308 if ($resql) {
1309 if (!$notrigger) {
1310 $result = $this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
1311 if ($result < 0) {
1312 $this->db->rollback();
1313 return -1;
1314 }
1315 }
1316
1317 $this->db->commit();
1318 return 1;
1319 } else {
1320 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1321 $this->error = $this->db->errno();
1322 $this->db->rollback();
1323 return -2;
1324 } else {
1325 $this->error = $this->db->lasterror();
1326 $this->db->rollback();
1327 return -1;
1328 }
1329 }
1330 } else {
1331 return 0;
1332 }
1333 }
1334
1335 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1343 public function copy_linked_contact($objFrom, $source = 'internal')
1344 {
1345 // phpcs:enable
1346 $contacts = $objFrom->liste_contact(-1, $source);
1347 foreach ($contacts as $contact) {
1348 if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0) {
1349 return -1;
1350 }
1351 }
1352 return 1;
1353 }
1354
1355 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1365 public function update_contact($rowid, $statut, $type_contact_id = 0, $fk_socpeople = 0)
1366 {
1367 // phpcs:enable
1368 // Insert into database
1369 $sql = "UPDATE ".$this->db->prefix()."element_contact set";
1370 $sql .= " statut = ".$statut;
1371 if ($type_contact_id) {
1372 $sql .= ", fk_c_type_contact = ".((int) $type_contact_id);
1373 }
1374 if ($fk_socpeople) {
1375 $sql .= ", fk_socpeople = ".((int) $fk_socpeople);
1376 }
1377 $sql .= " where rowid = ".((int) $rowid);
1378 $resql = $this->db->query($sql);
1379 if ($resql) {
1380 return 0;
1381 } else {
1382 $this->error = $this->db->lasterror();
1383 return -1;
1384 }
1385 }
1386
1387 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1395 public function delete_contact($rowid, $notrigger = 0)
1396 {
1397 // phpcs:enable
1398 global $user;
1399
1400 $error = 0;
1401
1402 $this->db->begin();
1403
1404 if (!$error && empty($notrigger)) {
1405 // Call trigger
1406 $this->context['contact_id'] = ((int) $rowid);
1407 $result = $this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
1408 if ($result < 0) {
1409 $error++;
1410 }
1411 // End call triggers
1412 }
1413
1414 if (!$error) {
1415 dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
1416
1417 $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
1418 $sql .= " WHERE rowid = ".((int) $rowid);
1419
1420 $result = $this->db->query($sql);
1421 if (!$result) {
1422 $error++;
1423 $this->errors[] = $this->db->lasterror();
1424 }
1425 }
1426
1427 if (!$error) {
1428 $this->db->commit();
1429 return 1;
1430 } else {
1431 $this->error = $this->db->lasterror();
1432 $this->db->rollback();
1433 return -1;
1434 }
1435 }
1436
1437 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1445 public function delete_linked_contact($source = '', $code = '')
1446 {
1447 // phpcs:enable
1448 $listId = '';
1449 $temp = array();
1450 $typeContact = $this->liste_type_contact($source, '', 0, 0, $code);
1451
1452 if (!empty($typeContact)) {
1453 foreach ($typeContact as $key => $value) {
1454 array_push($temp, $key);
1455 }
1456 $listId = implode(",", $temp);
1457 }
1458
1459 // If $listId is empty, we have not criteria on fk_c_type_contact so we will delete record on element_id for
1460 // any type or record instead of only the ones of the current object. So we do nothing in such a case.
1461 if (empty($listId)) {
1462 return 0;
1463 }
1464
1465 $sql = "DELETE FROM ".$this->db->prefix()."element_contact";
1466 $sql .= " WHERE element_id = ".((int) $this->id);
1467 $sql .= " AND fk_c_type_contact IN (".$this->db->sanitize($listId).")";
1468
1469 dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
1470 if ($this->db->query($sql)) {
1471 return 1;
1472 } else {
1473 $this->error = $this->db->lasterror();
1474 return -1;
1475 }
1476 }
1477
1478 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1490 public function liste_contact($statusoflink = -1, $source = 'external', $list = 0, $code = '', $status = -1, $arrayoftcids = array())
1491 {
1492 // phpcs:enable
1493 global $langs;
1494
1495 $tab = array();
1496
1497 $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
1498 if ($source == 'internal') {
1499 $sql .= ", '-1' as socid, t.statut as statuscontact, t.login, t.photo, t.gender";
1500 }
1501 if ($source == 'external' || $source == 'thirdparty') {
1502 $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
1503 }
1504 $sql .= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
1505 $sql .= ", tc.source, tc.element, tc.code, tc.libelle as type_label";
1506 $sql .= " FROM ".$this->db->prefix()."c_type_contact tc,";
1507 $sql .= " ".$this->db->prefix()."element_contact ec";
1508 if ($source == 'internal') { // internal contact (user)
1509 $sql .= " LEFT JOIN ".$this->db->prefix()."user t on ec.fk_socpeople = t.rowid";
1510 }
1511 if ($source == 'external' || $source == 'thirdparty') { // external contact (socpeople)
1512 $sql .= " LEFT JOIN ".$this->db->prefix()."socpeople t on ec.fk_socpeople = t.rowid";
1513 }
1514 $sql .= " WHERE ec.element_id = ".((int) $this->id);
1515 $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1516 $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1517 if ($code) {
1518 $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1519 }
1520 if ($source == 'internal') {
1521 $sql .= " AND tc.source = 'internal'";
1522 if ($status >= 0) {
1523 $sql .= " AND t.statut = ".((int) $status);
1524 }
1525 }
1526 if ($source == 'external' || $source == 'thirdparty') {
1527 $sql .= " AND tc.source = 'external'";
1528 if ($status >= 0) {
1529 $sql .= " AND t.statut = ".((int) $status); // t is llx_socpeople
1530 }
1531 }
1532 $sql .= " AND tc.active = 1";
1533 if ($statusoflink >= 0) {
1534 $sql .= " AND ec.statut = ".((int) $statusoflink);
1535 }
1536 $sql .= " ORDER BY t.lastname ASC";
1537
1538 dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
1539 $resql = $this->db->query($sql);
1540 if ($resql) {
1541 $num = $this->db->num_rows($resql);
1542 $i = 0;
1543 while ($i < $num) {
1544 $obj = $this->db->fetch_object($resql);
1545
1546 if (!$list) {
1547 $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
1548 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_label);
1549 $tab[$i] = array(
1550 'parentId' => $this->id,
1551 'source' => $obj->source,
1552 'socid' => $obj->socid,
1553 'id' => $obj->id,
1554 'nom' => $obj->lastname, // For backward compatibility
1555 'civility' => $obj->civility,
1556 'lastname' => $obj->lastname,
1557 'firstname' => $obj->firstname,
1558 'email' => $obj->email,
1559 'login' => (empty($obj->login) ? '' : $obj->login),
1560 'photo' => (empty($obj->photo) ? '' : $obj->photo),
1561 'gender' => (empty($obj->gender) ? '' : $obj->gender),
1562 'statuscontact' => $obj->statuscontact,
1563 'rowid' => $obj->rowid,
1564 'code' => $obj->code,
1565 'libelle' => $libelle_type,
1566 'status' => $obj->statuslink,
1567 'fk_c_type_contact' => $obj->fk_c_type_contact
1568 );
1569 } else {
1570 $tab[$i] = $obj->id;
1571 }
1572
1573 $i++;
1574 }
1575
1576 return $tab;
1577 } else {
1578 $this->error = $this->db->lasterror();
1579 dol_print_error($this->db);
1580 return -1;
1581 }
1582 }
1583
1584
1591 public function swapContactStatus($rowid)
1592 {
1593 $sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1594 $sql .= " tc.code, tc.libelle as type_label";
1595 $sql .= " FROM (".$this->db->prefix()."element_contact as ec, ".$this->db->prefix()."c_type_contact as tc)";
1596 $sql .= " WHERE ec.rowid =".((int) $rowid);
1597 $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1598 $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1599
1600 dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1601 $resql = $this->db->query($sql);
1602 if ($resql) {
1603 $obj = $this->db->fetch_object($resql);
1604 $newstatut = ($obj->statut == 4) ? 5 : 4;
1605 $result = $this->update_contact($rowid, $newstatut);
1606 $this->db->free($resql);
1607 return $result;
1608 } else {
1609 $this->error = $this->db->error();
1610 dol_print_error($this->db);
1611 return -1;
1612 }
1613 }
1614
1615 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1626 public function liste_type_contact($source = 'internal', $order = 'position', $option = 0, $activeonly = 0, $code = '')
1627 {
1628 // phpcs:enable
1629 global $langs;
1630
1631 if (empty($order)) {
1632 $order = 'position';
1633 }
1634 if ($order == 'position') {
1635 $order .= ',code';
1636 }
1637
1638 $tab = array();
1639 $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle as type_label, tc.position";
1640 $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1641 $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1642 if ($activeonly == 1) {
1643 $sql .= " AND tc.active=1"; // only the active types
1644 }
1645 if (!empty($source) && $source != 'all') {
1646 $sql .= " AND tc.source='".$this->db->escape($source)."'";
1647 }
1648 if (!empty($code)) {
1649 $sql .= " AND tc.code='".$this->db->escape($code)."'";
1650 }
1651 $sql .= $this->db->order($order, 'ASC');
1652
1653 //print "sql=".$sql;
1654 $resql = $this->db->query($sql);
1655 if ($resql) {
1656 $num = $this->db->num_rows($resql);
1657 $i = 0;
1658 while ($i < $num) {
1659 $obj = $this->db->fetch_object($resql);
1660
1661 $transkey = "TypeContact_".$this->element."_".$source."_".$obj->code;
1662 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_label);
1663 if (empty($option)) {
1664 $tab[$obj->rowid] = $libelle_type;
1665 } else {
1666 $tab[$obj->code] = $libelle_type;
1667 }
1668 $i++;
1669 }
1670 return $tab;
1671 } else {
1672 $this->error = $this->db->lasterror();
1673 //dol_print_error($this->db);
1674 return null;
1675 }
1676 }
1677
1689 public function listeTypeContacts($source = 'internal', $option = 0, $activeonly = 0, $code = '', $element = '', $excludeelement = '')
1690 {
1691 global $langs, $conf;
1692
1693 $langs->loadLangs(array('bills', 'contracts', 'interventions', 'orders', 'projects', 'propal', 'ticket', 'agenda'));
1694
1695 $tab = array();
1696
1697 $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle as type_label, tc.position, tc.element";
1698 $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1699
1700 $sqlWhere = array();
1701 if (!empty($element)) {
1702 $sqlWhere[] = " tc.element='".$this->db->escape($element)."'";
1703 }
1704 if (!empty($excludeelement)) {
1705 $sqlWhere[] = " tc.element <> '".$this->db->escape($excludeelement)."'";
1706 }
1707
1708 if ($activeonly == 1) {
1709 $sqlWhere[] = " tc.active=1"; // only the active types
1710 }
1711
1712 if (!empty($source) && $source != 'all') {
1713 $sqlWhere[] = " tc.source='".$this->db->escape($source)."'";
1714 }
1715
1716 if (!empty($code)) {
1717 $sqlWhere[] = " tc.code='".$this->db->escape($code)."'";
1718 }
1719
1720 if (count($sqlWhere) > 0) {
1721 $sql .= " WHERE ".implode(' AND ', $sqlWhere);
1722 }
1723
1724 $sql .= $this->db->order('tc.element, tc.position', 'ASC');
1725
1726 dol_syslog(__METHOD__, LOG_DEBUG);
1727 $resql = $this->db->query($sql);
1728 if ($resql) {
1729 $num = $this->db->num_rows($resql);
1730 if ($num > 0) {
1731 $langs->loadLangs(array("propal", "orders", "bills", "suppliers", "contracts", "supplier_proposal"));
1732
1733 while ($obj = $this->db->fetch_object($resql)) {
1734 $modulename = $obj->element;
1735 if (strpos($obj->element, 'project') !== false) {
1736 $modulename = 'projet';
1737 } elseif ($obj->element == 'contrat') {
1738 $element = 'contract';
1739 } elseif ($obj->element == 'action') {
1740 $modulename = 'agenda';
1741 } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1742 $modulename = 'fournisseur';
1743 }
1744 if (!empty($conf->{$modulename}->enabled)) {
1745 $libelle_element = $langs->trans('ContactDefault_'.$obj->element);
1746 $tmpelement = $obj->element;
1747 $transkey = "TypeContact_".$tmpelement."_".$source."_".$obj->code;
1748 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_label);
1749 $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1750 }
1751 }
1752 }
1753 return $tab;
1754 } else {
1755 $this->error = $this->db->lasterror();
1756 return null;
1757 }
1758 }
1759
1771 public function getIdContact($source, $code, $status = 0)
1772 {
1773 global $conf;
1774
1775 $result = array();
1776 $i = 0;
1777 // Particular case for shipping
1778 if ($this->element == 'shipping' && $this->origin_id != 0) {
1779 $id = $this->origin_id;
1780 $element = 'commande';
1781 } elseif ($this->element == 'reception' && $this->origin_id != 0) {
1782 $id = $this->origin_id;
1783 $element = 'order_supplier';
1784 } else {
1785 $id = $this->id;
1786 $element = $this->element;
1787 }
1788
1789 $sql = "SELECT ec.fk_socpeople";
1790 $sql .= " FROM ".$this->db->prefix()."element_contact as ec,";
1791 if ($source == 'internal') {
1792 $sql .= " ".$this->db->prefix()."user as c,";
1793 }
1794 if ($source == 'external') {
1795 $sql .= " ".$this->db->prefix()."socpeople as c,";
1796 }
1797 $sql .= " ".$this->db->prefix()."c_type_contact as tc";
1798 $sql .= " WHERE ec.element_id = ".((int) $id);
1799 $sql .= " AND ec.fk_socpeople = c.rowid";
1800 if ($source == 'internal') {
1801 $sql .= " AND c.entity IN (".getEntity('user').")";
1802 }
1803 if ($source == 'external') {
1804 $sql .= " AND c.entity IN (".getEntity('societe').")";
1805 }
1806 $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1807 $sql .= " AND tc.element = '".$this->db->escape($element)."'";
1808 $sql .= " AND tc.source = '".$this->db->escape($source)."'";
1809 if ($code) {
1810 $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1811 }
1812 $sql .= " AND tc.active = 1";
1813 if ($status) {
1814 $sql .= " AND ec.statut = ".((int) $status);
1815 }
1816
1817 dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1818 $resql = $this->db->query($sql);
1819 if ($resql) {
1820 while ($obj = $this->db->fetch_object($resql)) {
1821 $result[$i] = $obj->fk_socpeople;
1822 $i++;
1823 }
1824 } else {
1825 $this->error = $this->db->error();
1826 return null;
1827 }
1828
1829 return $result;
1830 }
1831
1832 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1839 public function fetch_contact($contactid = null)
1840 {
1841 // phpcs:enable
1842 if (empty($contactid)) {
1843 $contactid = $this->contact_id;
1844 }
1845
1846 if (empty($contactid)) {
1847 return 0;
1848 }
1849
1850 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1851 $contact = new Contact($this->db);
1852 $result = $contact->fetch($contactid);
1853 $this->contact = $contact;
1854 return $result;
1855 }
1856
1857 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1865 public function fetch_thirdparty($force_thirdparty_id = 0)
1866 {
1867 // phpcs:enable
1868 if (empty($this->socid) && empty($this->fk_soc) && empty($force_thirdparty_id)) {
1869 return 0;
1870 }
1871
1872 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1873
1874 $idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : 0);
1875 if ($force_thirdparty_id) {
1876 $idtofetch = $force_thirdparty_id;
1877 }
1878
1879 if ($idtofetch) {
1880 $thirdparty = new Societe($this->db);
1881 $result = $thirdparty->fetch($idtofetch);
1882 if ($result < 0) {
1883 $this->errors = array_merge($this->errors, $thirdparty->errors);
1884 }
1885 $this->thirdparty = $thirdparty;
1886
1887 // Use first price level if level not defined for third party
1888 if (getDolGlobalString('PRODUIT_MULTIPRICES') && empty($this->thirdparty->price_level)) {
1889 $this->thirdparty->price_level = 1;
1890 }
1891
1892 return $result;
1893 } else {
1894 return -1;
1895 }
1896 }
1897
1898
1906 public function fetchOneLike($ref)
1907 {
1908 if (!$this->table_ref_field) {
1909 return 0;
1910 }
1911
1912 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
1913 $sql .= " WHERE ".$this->table_ref_field." LIKE '".$this->db->escape($ref)."'"; // no escapeforlike here
1914 $sql .= " LIMIT 1";
1915
1916 $query = $this->db->query($sql);
1917
1918 if (!$this->db->num_rows($query)) {
1919 return 0;
1920 }
1921
1922 $result = $this->db->fetch_object($query);
1923
1924 if (method_exists($this, 'fetch')) {
1925 return $this->fetch($result->rowid);
1926 } else {
1927 $this->error = 'Fetch method not implemented on '.get_class($this);
1928 dol_syslog(get_class($this).'::fetchOneLike Error='.$this->error, LOG_ERR);
1929 array_push($this->errors, $this->error);
1930 return -1;
1931 }
1932 }
1933
1934 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1942 public function fetch_barcode()
1943 {
1944 // phpcs:enable
1945 global $conf;
1946
1947 dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1948
1949 $idtype = $this->barcode_type;
1950 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
1951 if ($this->element == 'product' && getDolGlobalString('PRODUIT_DEFAULT_BARCODE_TYPE')) {
1952 $idtype = getDolGlobalString('PRODUIT_DEFAULT_BARCODE_TYPE');
1953 } elseif ($this->element == 'societe') {
1954 $idtype = getDolGlobalString('GENBARCODE_BARCODETYPE_THIRDPARTY');
1955 } else {
1956 dol_syslog('Call fetch_barcode with barcode_type not defined and cannot be guessed', LOG_WARNING);
1957 }
1958 }
1959
1960 if ($idtype > 0) {
1961 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
1962 $sql = "SELECT rowid, code, libelle as label, coder";
1963 $sql .= " FROM ".$this->db->prefix()."c_barcode_type";
1964 $sql .= " WHERE rowid = ".((int) $idtype);
1965 dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1966 $resql = $this->db->query($sql);
1967 if ($resql) {
1968 $obj = $this->db->fetch_object($resql);
1969 $this->barcode_type = $obj->rowid;
1970 $this->barcode_type_code = $obj->code;
1971 $this->barcode_type_label = $obj->label;
1972 $this->barcode_type_coder = $obj->coder;
1973 return 1;
1974 } else {
1975 dol_print_error($this->db);
1976 return -1;
1977 }
1978 }
1979 }
1980 return 0;
1981 }
1982
1983 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1989 public function fetch_project()
1990 {
1991 // phpcs:enable
1992 return $this->fetch_projet();
1993 }
1994
1995 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2001 public function fetch_projet()
2002 {
2003 // phpcs:enable
2004 include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
2005
2006 if (empty($this->fk_project) && !empty($this->fk_projet)) {
2007 $this->fk_project = $this->fk_projet; // For backward compatibility
2008 }
2009 if (empty($this->fk_project)) {
2010 return 0;
2011 }
2012
2013 $project = new Project($this->db);
2014 $result = $project->fetch($this->fk_project);
2015
2016 $this->projet = $project; // deprecated
2017 $this->project = $project;
2018 return $result;
2019 }
2020
2021 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2027 public function fetch_product()
2028 {
2029 // phpcs:enable
2030 include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
2031
2032 // @phan-suppress-next-line PhanUndeclaredProperty
2033 if (empty($this->fk_product)) {
2034 return 0;
2035 }
2036
2037 $product = new Product($this->db);
2038 // @phan-suppress-next-line PhanUndeclaredProperty
2039 $result = $product->fetch($this->fk_product);
2040
2041 $this->product = $product;
2042 return $result;
2043 }
2044
2045 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2052 public function fetch_user($userid)
2053 {
2054 // phpcs:enable
2055 $user = new User($this->db);
2056 $result = $user->fetch($userid);
2057 $this->user = $user;
2058 return $result;
2059 }
2060
2061 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2068 public function fetch_origin()
2069 {
2070 // phpcs:enable
2071 $origin = $this->origin ? $this->origin : $this->origin_type;
2072
2073 // Manage classes with non standard name
2074 if ($origin == 'shipping') {
2075 $origin = 'expedition';
2076 }
2077 if ($origin == 'delivery') {
2078 $origin = 'livraison';
2079 }
2080 if ($origin == 'order_supplier' || $origin == 'supplier_order') {
2081 $origin = 'commandeFournisseur';
2082 }
2083
2084 $classname = ucfirst($origin);
2085 $this->origin_object = new $classname($this->db);
2086 // @phan-suppress-next-line PhanPluginUnknownObjectMethodCall
2087 $this->origin_object->fetch($this->origin_id);
2088 }
2089
2099 public function fetchObjectFrom($table, $field, $key, $element = null)
2100 {
2101 global $conf;
2102
2103 $result = false;
2104
2105 $sql = "SELECT rowid FROM ".$this->db->prefix().$table;
2106 $sql .= " WHERE ".$field." = '".$this->db->escape($key)."'";
2107 if (!empty($element)) {
2108 $sql .= " AND entity IN (".getEntity($element).")";
2109 } else {
2110 $sql .= " AND entity = ".((int) $conf->entity);
2111 }
2112
2113 dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
2114 $resql = $this->db->query($sql);
2115 if ($resql) {
2116 $obj = $this->db->fetch_object($resql);
2117 // Test for avoid error -1
2118 if ($obj) {
2119 if (method_exists($this, 'fetch')) {
2120 $result = $this->fetch($obj->rowid);
2121 } else {
2122 $this->error = 'fetch() method not implemented on '.get_class($this);
2123 dol_syslog(get_class($this).'::fetchOneLike Error='.$this->error, LOG_ERR);
2124 array_push($this->errors, $this->error);
2125 $result = -1;
2126 }
2127 }
2128 }
2129
2130 return $result;
2131 }
2132
2141 public function getValueFrom($table, $id, $field)
2142 {
2143 $result = false;
2144 if (!empty($id) && !empty($field) && !empty($table)) {
2145 $sql = "SELECT ".$field." FROM ".$this->db->prefix().$table;
2146 $sql .= " WHERE rowid = ".((int) $id);
2147
2148 dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
2149 $resql = $this->db->query($sql);
2150 if ($resql) {
2151 $row = $this->db->fetch_row($resql);
2152 $result = $row[0];
2153 }
2154 }
2155 return $result;
2156 }
2157
2174 public function setValueFrom($field, $value, $table = '', $id = null, $format = '', $id_field = '', $fuser = null, $trigkey = '', $fk_user_field = 'fk_user_modif')
2175 {
2176 global $user;
2177
2178 if (empty($table)) {
2179 $table = $this->table_element;
2180 }
2181 if (empty($id)) {
2182 $id = $this->id;
2183 }
2184 if (empty($format)) {
2185 $format = 'text';
2186 }
2187 if (empty($id_field)) {
2188 $id_field = 'rowid';
2189 }
2190
2191 // Special case
2192 if ($table == 'product' && $field == 'note_private') {
2193 $field = 'note';
2194 }
2195
2196 if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
2197 $fk_user_field = 'fk_user_mod';
2198 }
2199 if (in_array($table, array('prelevement_bons'))) { // TODO Add a field fk_user_modif into llx_prelevement_bons
2200 $fk_user_field = '';
2201 }
2202
2203 $oldvalue = null;
2204 if ($trigkey) {
2205 $sql = "SELECT " . $field;
2206 $sql .= " FROM " . MAIN_DB_PREFIX . $table;
2207 $sql .= " WHERE " . $id_field . " = " . ((int) $id);
2208
2209 $resql = $this->db->query($sql);
2210 if ($resql) {
2211 if ($obj = $this->db->fetch_object($resql)) {
2212 if ($format == 'date') {
2213 $oldvalue = $this->db->jdate($obj->$field);
2214 } else {
2215 $oldvalue = $obj->$field;
2216 }
2217 }
2218 } else {
2219 $this->error = $this->db->lasterror();
2220 return -1;
2221 }
2222 }
2223
2224 $error = 0;
2225
2226 dol_syslog(__METHOD__, LOG_DEBUG);
2227
2228 $this->db->begin();
2229
2230 $sql = "UPDATE ".$this->db->prefix().$table." SET ";
2231
2232 if ($format == 'text') {
2233 $sql .= $field." = '".$this->db->escape($value)."'";
2234 } elseif ($format == 'int') {
2235 $sql .= $field." = ".((int) $value);
2236 } elseif ($format == 'date') {
2237 $sql .= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
2238 } elseif ($format == 'dategmt') {
2239 $sql .= $field." = ".($value ? "'".$this->db->idate($value, 'gmt')."'" : "null");
2240 }
2241
2242 if ($fk_user_field) {
2243 if (!empty($fuser) && is_object($fuser)) {
2244 $sql .= ", ".$fk_user_field." = ".((int) $fuser->id);
2245 } elseif (empty($fuser) || $fuser != 'none') {
2246 $sql .= ", ".$fk_user_field." = ".((int) $user->id);
2247 }
2248 }
2249
2250 $sql .= " WHERE ".$id_field." = ".((int) $id);
2251
2252 $resql = $this->db->query($sql);
2253 if ($resql) {
2254 if ($trigkey) {
2255 // call trigger with updated object values
2256 if (method_exists($this, 'fetch')) {
2257 $result = $this->fetch($id);
2258 } else {
2259 $result = $this->fetchCommon($id);
2260 }
2261 $this->oldcopy = clone $this;
2262 if (property_exists($this->oldcopy, $field)) {
2263 $this->oldcopy->$field = $oldvalue;
2264 }
2265
2266 if ($result >= 0) {
2267 $result = $this->call_trigger($trigkey, (!empty($fuser) && is_object($fuser)) ? $fuser : $user); // This may set this->errors
2268 }
2269 if ($result < 0) {
2270 $error++;
2271 }
2272 }
2273
2274 if (!$error) {
2275 if (property_exists($this, $field)) {
2276 $this->$field = $value;
2277 }
2278 $this->db->commit();
2279 return 1;
2280 } else {
2281 $this->db->rollback();
2282 return -2;
2283 }
2284 } else {
2285 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2286 $this->error = 'DB_ERROR_RECORD_ALREADY_EXISTS';
2287 } else {
2288 $this->error = $this->db->lasterror();
2289 }
2290 $this->db->rollback();
2291 return -1;
2292 }
2293 }
2294
2295 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2306 public function load_previous_next_ref($filter, $fieldid, $nodbprefix = 0)
2307 {
2308 // phpcs:enable
2309 global $conf, $user;
2310
2311 if (!$this->table_element) {
2312 dol_print_error(null, get_class($this)."::load_previous_next_ref was called on object with property table_element not defined");
2313 return -1;
2314 }
2315 if ($fieldid == 'none') {
2316 return 1;
2317 }
2318
2319 // For backward compatibility
2320 if (in_array($this->table_element, array('facture_rec', 'facture_fourn_rec')) && $fieldid == 'title') {
2321 $fieldid = 'titre';
2322 }
2323
2324 // Security on socid
2325 $socid = 0;
2326 if ($user->socid > 0) {
2327 $socid = $user->socid;
2328 }
2329
2330 // this->ismultientitymanaged contains
2331 // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
2332 $aliastablesociete = 's';
2333 if ($this->element == 'societe') {
2334 $aliastablesociete = 'te'; // te as table_element
2335 }
2336 $restrictiononfksoc = empty($this->restrictiononfksoc) ? 0 : $this->restrictiononfksoc;
2337 $sql = "SELECT MAX(te.".$fieldid.")";
2338 $sql .= " FROM ".(empty($nodbprefix) ? $this->db->prefix() : '').$this->table_element." as te";
2339 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2340 $tmparray = explode('@', $this->ismultientitymanaged);
2341 $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
2342 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2343 $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
2344 } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2345 $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
2346 }
2347 if ($restrictiononfksoc && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2348 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
2349 }
2350 if ($fieldid == 'rowid') {
2351 $sql .= " WHERE te.".$fieldid." < ".((int) $this->id);
2352 } else {
2353 $sql .= " WHERE te.".$fieldid." < '".$this->db->escape($this->ref)."'"; // ->ref must always be defined (set to id if field does not exists)
2354 }
2355 if ($restrictiononfksoc == 1 && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2356 $sql .= " AND sc.fk_user = ".((int) $user->id);
2357 }
2358 if ($restrictiononfksoc == 2 && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2359 $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
2360 }
2361
2362 $filtermax = $filter;
2363
2364 // Manage filter
2365 $errormessage = '';
2366 $tmpsql = forgeSQLFromUniversalSearchCriteria($filtermax, $errormessage);
2367 if ($errormessage) {
2368 if (!preg_match('/^\s*AND/i', $filtermax)) {
2369 $sql .= " AND ";
2370 }
2371 $sql .= $filtermax;
2372 } else {
2373 $sql .= $tmpsql;
2374 }
2375
2376 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2377 $tmparray = explode('@', $this->ismultientitymanaged);
2378 $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
2379 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2380 $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
2381 }
2382 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2383 if ($this->element == 'user' && getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE')) {
2384 if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
2385 $sql .= " AND te.entity IS NOT NULL"; // Show all users
2386 } else {
2387 $sql .= " AND te.rowid IN (SELECT ug.fk_user FROM ".$this->db->prefix()."usergroup_user as ug WHERE ug.entity IN (".getEntity('usergroup')."))";
2388 }
2389 } else {
2390 $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
2391 }
2392 }
2393 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
2394 $tmparray = explode('@', $this->ismultientitymanaged);
2395 $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
2396 }
2397 if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
2398 $sql .= ' AND te.fk_soc = '.((int) $socid);
2399 }
2400 if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
2401 $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
2402 }
2403 if ($restrictiononfksoc && $socid && $this->element == 'societe') {
2404 $sql .= ' AND te.rowid = '.((int) $socid);
2405 }
2406 //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
2407
2408 $result = $this->db->query($sql);
2409 if (!$result) {
2410 $this->error = $this->db->lasterror();
2411 return -1;
2412 }
2413 $row = $this->db->fetch_row($result);
2414 $this->ref_previous = $row[0];
2415
2416 $sql = "SELECT MIN(te.".$fieldid.")";
2417 $sql .= " FROM ".(empty($nodbprefix) ? $this->db->prefix() : '').$this->table_element." as te";
2418 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2419 $tmparray = explode('@', $this->ismultientitymanaged);
2420 $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
2421 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2422 $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
2423 } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2424 $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
2425 }
2426 if ($restrictiononfksoc && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2427 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
2428 }
2429 if ($fieldid == 'rowid') {
2430 $sql .= " WHERE te.".$fieldid." > ".((int) $this->id);
2431 } else {
2432 $sql .= " WHERE te.".$fieldid." > '".$this->db->escape($this->ref)."'"; // ->ref must always be defined (set to id if field does not exists)
2433 }
2434 if ($restrictiononfksoc == 1 && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2435 $sql .= " AND (sc.fk_user = ".((int) $user->id);
2436 if (getDolGlobalInt('MAIN_SEE_SUBORDINATES')) {
2437 $userschilds = $user->getAllChildIds();
2438 $sql .= " OR sc.fk_user IN (".$this->db->sanitize(implode(',', $userschilds)).")";
2439 }
2440 $sql .= ')';
2441 }
2442 if ($restrictiononfksoc == 2 && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2443 $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
2444 }
2445
2446 $filtermin = $filter;
2447
2448 // Manage filter
2449 $errormessage = '';
2450 $tmpsql = forgeSQLFromUniversalSearchCriteria($filtermin, $errormessage);
2451 if ($errormessage) {
2452 if (!preg_match('/^\s*AND/i', $filtermin)) {
2453 $sql .= " AND ";
2454 }
2455 $sql .= $filtermin;
2456
2457 $filtermin = '';
2458 } else {
2459 $sql .= $tmpsql;
2460 }
2461
2462 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2463 $tmparray = explode('@', $this->ismultientitymanaged);
2464 $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
2465 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2466 $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
2467 }
2468 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2469 if ($this->element == 'user' && getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE')) {
2470 if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
2471 $sql .= " AND te.entity IS NOT NULL"; // Show all users
2472 } else {
2473 $sql .= " AND te.rowid IN (SELECT ug.fk_user FROM ".$this->db->prefix()."usergroup_user as ug WHERE ug.entity IN (".getEntity('usergroup')."))";
2474 }
2475 } else {
2476 $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
2477 }
2478 }
2479 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
2480 $tmparray = explode('@', $this->ismultientitymanaged);
2481 $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
2482 }
2483 if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
2484 $sql .= ' AND te.fk_soc = '.((int) $socid);
2485 }
2486 if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
2487 $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
2488 }
2489 if ($restrictiononfksoc && $socid && $this->element == 'societe') {
2490 $sql .= ' AND te.rowid = '.((int) $socid);
2491 }
2492 //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
2493 // 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
2494
2495 $result = $this->db->query($sql);
2496 if (!$result) {
2497 $this->error = $this->db->lasterror();
2498 return -2;
2499 }
2500 $row = $this->db->fetch_row($result);
2501 $this->ref_next = $row[0];
2502
2503 return 1;
2504 }
2505
2506
2514 public function getListContactId($source = 'external')
2515 {
2516 $contactAlreadySelected = array();
2517 $tab = $this->liste_contact(-1, $source);
2518 $num = count($tab);
2519 $i = 0;
2520 while ($i < $num) {
2521 if ($source == 'thirdparty') {
2522 $contactAlreadySelected[$i] = $tab[$i]['socid'];
2523 } else {
2524 $contactAlreadySelected[$i] = $tab[$i]['id'];
2525 }
2526 $i++;
2527 }
2528 return $contactAlreadySelected;
2529 }
2530
2531
2539 public function setProject($projectid, $notrigger = 0)
2540 {
2541 global $user;
2542 $error = 0;
2543
2544 if (!$this->table_element) {
2545 dol_syslog(get_class($this)."::setProject was called on object with property table_element not defined", LOG_ERR);
2546 return -1;
2547 }
2548
2549 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2550 if (!empty($this->fields['fk_project'])) { // Common case
2551 if ($projectid) {
2552 $sql .= " SET fk_project = ".((int) $projectid);
2553 } else {
2554 $sql .= " SET fk_project = NULL";
2555 }
2556 $sql .= ' WHERE rowid = '.((int) $this->id);
2557 } elseif ($this->table_element == 'actioncomm') { // Special case for actioncomm
2558 if ($projectid) {
2559 $sql .= " SET fk_project = ".((int) $projectid);
2560 } else {
2561 $sql .= " SET fk_project = NULL";
2562 }
2563 $sql .= ' WHERE id = '.((int) $this->id);
2564 } else { // Special case for old architecture objects
2565 if ($projectid) {
2566 $sql .= ' SET fk_projet = '.((int) $projectid);
2567 } else {
2568 $sql .= ' SET fk_projet = NULL';
2569 }
2570 $sql .= " WHERE rowid = ".((int) $this->id);
2571 }
2572
2573 $this->db->begin();
2574
2575 dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
2576 if ($this->db->query($sql)) {
2577 $this->fk_project = ((int) $projectid);
2578 } else {
2579 dol_print_error($this->db);
2580 $error++;
2581 }
2582
2583 // Triggers
2584 if (!$error && !$notrigger) {
2585 // Call triggers
2586 $result = $this->call_trigger(strtoupper($this->element) . '_MODIFY', $user);
2587 if ($result < 0) {
2588 $error++;
2589 } //Do also here what you must do to rollback action if trigger fail
2590 // End call triggers
2591 }
2592
2593 // Commit or rollback
2594 if ($error) {
2595 $this->db->rollback();
2596 return -1;
2597 } else {
2598 $this->db->commit();
2599 return 1;
2600 }
2601 }
2602
2609 public function setPaymentMethods($id)
2610 {
2611 global $user;
2612
2613 $error = 0;
2614 $notrigger = 0;
2615
2616 dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
2617
2618 if ($this->status >= 0 || $this->element == 'societe') {
2619 // TODO uniformize field name
2620 $fieldname = 'fk_mode_reglement';
2621 if ($this->element == 'societe') {
2622 $fieldname = 'mode_reglement';
2623 }
2624 if (get_class($this) == 'Fournisseur') {
2625 $fieldname = 'mode_reglement_supplier';
2626 }
2627 if (get_class($this) == 'Tva') {
2628 $fieldname = 'fk_typepayment';
2629 }
2630 if (get_class($this) == 'Salary') {
2631 $fieldname = 'fk_typepayment';
2632 }
2633
2634 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2635 $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2636 $sql .= ' WHERE rowid='.((int) $this->id);
2637
2638 if ($this->db->query($sql)) {
2639 $this->mode_reglement_id = $id;
2640 // for supplier
2641 if (get_class($this) == 'Fournisseur') {
2642 $this->mode_reglement_supplier_id = $id;
2643 }
2644 // Triggers
2645 if (!$error && !$notrigger) {
2646 // Call triggers
2647 if (get_class($this) == 'Commande') {
2648 $result = $this->call_trigger('ORDER_MODIFY', $user);
2649 } else {
2650 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
2651 }
2652 if ($result < 0) {
2653 $error++;
2654 }
2655 // End call triggers
2656 }
2657 return 1;
2658 } else {
2659 dol_syslog(get_class($this).'::setPaymentMethods Error '.$this->db->error());
2660 $this->error = $this->db->error();
2661 return -1;
2662 }
2663 } else {
2664 dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
2665 $this->error = 'Status of the object is incompatible '.$this->status;
2666 return -2;
2667 }
2668 }
2669
2676 public function setMulticurrencyCode($code)
2677 {
2678 dol_syslog(get_class($this).'::setMulticurrencyCode('.$code.')');
2679 if ($this->status >= 0 || $this->element == 'societe') {
2680 $fieldname = 'multicurrency_code';
2681
2682 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2683 $sql .= " SET ".$fieldname." = '".$this->db->escape($code)."'";
2684 $sql .= ' WHERE rowid='.((int) $this->id);
2685
2686 if ($this->db->query($sql)) {
2687 $this->multicurrency_code = $code;
2688
2689 list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
2690 if ($rate) {
2691 $this->setMulticurrencyRate($rate, 2);
2692 }
2693
2694 return 1;
2695 } else {
2696 dol_syslog(get_class($this).'::setMulticurrencyCode Error '.$sql.' - '.$this->db->error());
2697 $this->error = $this->db->error();
2698 return -1;
2699 }
2700 } else {
2701 dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
2702 $this->error = 'Status of the object is incompatible '.$this->status;
2703 return -2;
2704 }
2705 }
2706
2714 public function setMulticurrencyRate($rate, $mode = 1)
2715 {
2716 dol_syslog(get_class($this).'::setMulticurrencyRate('.$rate.', '.$mode.')');
2717 if ($this->status >= 0 || $this->element == 'societe') {
2718 $fieldname = 'multicurrency_tx';
2719
2720 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2721 $sql .= " SET ".$fieldname." = ".((float) $rate);
2722 $sql .= ' WHERE rowid='.((int) $this->id);
2723
2724 if ($this->db->query($sql)) {
2725 $this->multicurrency_tx = $rate;
2726
2727 // Update line price
2728 if (!empty($this->lines)) {
2729 foreach ($this->lines as &$line) {
2730 // Amounts in company currency will be recalculated
2731 if ($mode == 1) {
2732 $line->subprice = 0;
2733 }
2734
2735 // Amounts in foreign currency will be recalculated
2736 if ($mode == 2) {
2737 $line->multicurrency_subprice = 0;
2738 }
2739
2740 switch ($this->element) {
2741 case 'propal':
2744 $this->updateline(
2745 $line->id,
2746 $line->subprice,
2747 $line->qty,
2748 $line->remise_percent,
2749 $line->tva_tx,
2750 $line->localtax1_tx,
2751 $line->localtax2_tx,
2752 ($line->description ? $line->description : $line->desc),
2753 'HT',
2754 $line->info_bits,
2755 $line->special_code,
2756 $line->fk_parent_line,
2757 $line->skip_update_total,
2758 $line->fk_fournprice,
2759 $line->pa_ht,
2760 $line->label,
2761 $line->product_type,
2762 $line->date_start,
2763 $line->date_end,
2764 $line->array_options,
2765 $line->fk_unit,
2766 $line->multicurrency_subprice
2767 );
2768 break;
2769 case 'commande':
2772 $this->updateline(
2773 $line->id,
2774 ($line->description ? $line->description : $line->desc),
2775 $line->subprice,
2776 $line->qty,
2777 $line->remise_percent,
2778 $line->tva_tx,
2779 $line->localtax1_tx,
2780 $line->localtax2_tx,
2781 'HT',
2782 $line->info_bits,
2783 $line->date_start,
2784 $line->date_end,
2785 $line->product_type,
2786 $line->fk_parent_line,
2787 $line->skip_update_total,
2788 $line->fk_fournprice,
2789 $line->pa_ht,
2790 $line->label,
2791 $line->special_code,
2792 $line->array_options,
2793 $line->fk_unit,
2794 $line->multicurrency_subprice
2795 );
2796 break;
2797 case 'facture':
2800 $this->updateline(
2801 $line->id,
2802 ($line->description ? $line->description : $line->desc),
2803 $line->subprice,
2804 $line->qty,
2805 $line->remise_percent,
2806 $line->date_start,
2807 $line->date_end,
2808 $line->tva_tx,
2809 $line->localtax1_tx,
2810 $line->localtax2_tx,
2811 'HT',
2812 $line->info_bits,
2813 $line->product_type,
2814 $line->fk_parent_line,
2815 $line->skip_update_total,
2816 $line->fk_fournprice,
2817 $line->pa_ht,
2818 $line->label,
2819 $line->special_code,
2820 $line->array_options,
2821 $line->situation_percent,
2822 $line->fk_unit,
2823 $line->multicurrency_subprice
2824 );
2825 break;
2826 case 'supplier_proposal':
2829 $this->updateline(
2830 $line->id,
2831 $line->subprice,
2832 $line->qty,
2833 $line->remise_percent,
2834 $line->tva_tx,
2835 $line->localtax1_tx,
2836 $line->localtax2_tx,
2837 ($line->description ? $line->description : $line->desc),
2838 'HT',
2839 $line->info_bits,
2840 $line->special_code,
2841 $line->fk_parent_line,
2842 $line->skip_update_total,
2843 $line->fk_fournprice,
2844 $line->pa_ht,
2845 $line->label,
2846 $line->product_type,
2847 $line->array_options,
2848 $line->ref_fourn,
2849 $line->multicurrency_subprice
2850 );
2851 break;
2852 case 'order_supplier':
2855 $this->updateline(
2856 $line->id,
2857 ($line->description ? $line->description : $line->desc),
2858 $line->subprice,
2859 $line->qty,
2860 $line->remise_percent,
2861 $line->tva_tx,
2862 $line->localtax1_tx,
2863 $line->localtax2_tx,
2864 'HT',
2865 $line->info_bits,
2866 $line->product_type,
2867 0,
2868 $line->date_start,
2869 $line->date_end,
2870 $line->array_options,
2871 $line->fk_unit,
2872 $line->multicurrency_subprice,
2873 $line->ref_supplier
2874 );
2875 break;
2876 case 'invoice_supplier':
2879 $this->updateline(
2880 $line->id,
2881 ($line->description ? $line->description : $line->desc),
2882 $line->subprice,
2883 $line->tva_tx,
2884 $line->localtax1_tx,
2885 $line->localtax2_tx,
2886 $line->qty,
2887 0,
2888 'HT',
2889 $line->info_bits,
2890 $line->product_type,
2891 $line->remise_percent,
2892 0,
2893 $line->date_start,
2894 $line->date_end,
2895 $line->array_options,
2896 $line->fk_unit,
2897 $line->multicurrency_subprice,
2898 $line->ref_supplier
2899 );
2900 break;
2901 default:
2902 dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
2903 break;
2904 }
2905 }
2906 }
2907
2908 return 1;
2909 } else {
2910 dol_syslog(get_class($this).'::setMulticurrencyRate Error '.$sql.' - '.$this->db->error());
2911 $this->error = $this->db->error();
2912 return -1;
2913 }
2914 } else {
2915 dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
2916 $this->error = 'Status of the object is incompatible '.$this->status;
2917 return -2;
2918 }
2919 }
2920
2928 public function setPaymentTerms($id, $deposit_percent = null)
2929 {
2930 dol_syslog(get_class($this).'::setPaymentTerms('.$id.', '.var_export($deposit_percent, true).')');
2931 if ($this->status >= 0 || $this->element == 'societe') {
2932 // TODO uniformize field name
2933 $fieldname = 'fk_cond_reglement';
2934 if ($this->element == 'societe') {
2935 $fieldname = 'cond_reglement';
2936 }
2937 if (get_class($this) == 'Fournisseur') {
2938 $fieldname = 'cond_reglement_supplier';
2939 }
2940
2941 if (empty($deposit_percent) || $deposit_percent < 0) {
2942 $deposit_percent = (float) getDictionaryValue('c_payment_term', 'deposit_percent', $id);
2943 }
2944
2945 if ($deposit_percent > 100) {
2946 $deposit_percent = 100;
2947 }
2948
2949 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2950 $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2951 if (in_array($this->table_element, array('propal', 'commande', 'societe'))) {
2952 $sql .= " , deposit_percent = " . (empty($deposit_percent) ? 'NULL' : "'".$this->db->escape($deposit_percent)."'");
2953 }
2954 $sql .= ' WHERE rowid='.((int) $this->id);
2955
2956 if ($this->db->query($sql)) {
2957 $this->cond_reglement_id = $id;
2958 // for supplier
2959 if (get_class($this) == 'Fournisseur') {
2960 $this->cond_reglement_supplier_id = $id;
2961 }
2962 $this->cond_reglement = $id; // for compatibility
2963 $this->deposit_percent = $deposit_percent;
2964 return 1;
2965 } else {
2966 dol_syslog(get_class($this).'::setPaymentTerms Error '.$sql.' - '.$this->db->error());
2967 $this->error = $this->db->error();
2968 return -1;
2969 }
2970 } else {
2971 dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
2972 $this->error = 'Status of the object is incompatible '.$this->status;
2973 return -2;
2974 }
2975 }
2976
2983 public function setTransportMode($id)
2984 {
2985 dol_syslog(get_class($this).'::setTransportMode('.$id.')');
2986 if ($this->status >= 0 || $this->element == 'societe') {
2987 $fieldname = 'fk_transport_mode';
2988 if ($this->element == 'societe') {
2989 $fieldname = 'transport_mode';
2990 }
2991 if (get_class($this) == 'Fournisseur') {
2992 $fieldname = 'transport_mode_supplier';
2993 }
2994
2995 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2996 $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2997 $sql .= ' WHERE rowid='.((int) $this->id);
2998
2999 if ($this->db->query($sql)) {
3000 $this->transport_mode_id = $id;
3001 // for supplier
3002 if (get_class($this) == 'Fournisseur') {
3003 $this->transport_mode_supplier_id = $id;
3004 }
3005 return 1;
3006 } else {
3007 dol_syslog(get_class($this).'::setTransportMode Error '.$sql.' - '.$this->db->error());
3008 $this->error = $this->db->error();
3009 return -1;
3010 }
3011 } else {
3012 dol_syslog(get_class($this).'::setTransportMode, status of the object is incompatible');
3013 $this->error = 'Status of the object is incompatible '.$this->status;
3014 return -2;
3015 }
3016 }
3017
3025 {
3026 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms('.$id.')');
3027 if ($this->status >= 0 || $this->element == 'societe') {
3028 $fieldname = 'retained_warranty_fk_cond_reglement';
3029
3030 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
3031 $sql .= " SET ".$fieldname." = ".((int) $id);
3032 $sql .= ' WHERE rowid='.((int) $this->id);
3033
3034 if ($this->db->query($sql)) {
3035 $this->retained_warranty_fk_cond_reglement = $id;
3036 return 1;
3037 } else {
3038 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms Error '.$sql.' - '.$this->db->error());
3039 $this->error = $this->db->error();
3040 return -1;
3041 }
3042 } else {
3043 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms, status of the object is incompatible');
3044 $this->error = 'Status of the object is incompatible '.$this->status;
3045 return -2;
3046 }
3047 }
3048
3056 public function setDeliveryAddress($id)
3057 {
3058 $fieldname = 'fk_delivery_address';
3059 if ($this->element == 'delivery' || $this->element == 'shipping') {
3060 $fieldname = 'fk_address';
3061 }
3062
3063 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ".$fieldname." = ".((int) $id);
3064 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
3065
3066 if ($this->db->query($sql)) {
3067 $this->fk_delivery_address = $id;
3068 return 1;
3069 } else {
3070 $this->error = $this->db->error();
3071 dol_syslog(get_class($this).'::setDeliveryAddress Error '.$this->error);
3072 return -1;
3073 }
3074 }
3075
3076
3085 public function setShippingMethod($shipping_method_id, $notrigger = 0, $userused = null)
3086 {
3087 global $user;
3088
3089 if (empty($userused)) {
3090 $userused = $user;
3091 }
3092
3093 $error = 0;
3094
3095 if (!$this->table_element) {
3096 dol_syslog(get_class($this)."::setShippingMethod was called on object with property table_element not defined", LOG_ERR);
3097 return -1;
3098 }
3099
3100 $this->db->begin();
3101
3102 if ($shipping_method_id < 0) {
3103 $shipping_method_id = 'NULL';
3104 }
3105 dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
3106
3107 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3108 $sql .= " SET fk_shipping_method = ".((int) $shipping_method_id);
3109 $sql .= " WHERE rowid=".((int) $this->id);
3110 $resql = $this->db->query($sql);
3111 if (!$resql) {
3112 dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
3113 $this->error = $this->db->lasterror();
3114 $error++;
3115 } else {
3116 if (!$notrigger) {
3117 // Call trigger
3118 $this->context = array('shippingmethodupdate' => 1);
3119 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused);
3120 if ($result < 0) {
3121 $error++;
3122 }
3123 // End call trigger
3124 }
3125 }
3126 if ($error) {
3127 $this->db->rollback();
3128 return -1;
3129 } else {
3130 $this->shipping_method_id = ($shipping_method_id == 'NULL') ? null : $shipping_method_id;
3131 $this->db->commit();
3132 return 1;
3133 }
3134 }
3135
3136
3143 public function setWarehouse($warehouse_id)
3144 {
3145 if (!$this->table_element) {
3146 dol_syslog(get_class($this)."::setWarehouse was called on object with property table_element not defined", LOG_ERR);
3147 return -1;
3148 }
3149 if ($warehouse_id < 0) {
3150 $warehouse_id = 'NULL';
3151 }
3152 dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
3153
3154 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3155 $sql .= " SET fk_warehouse = ".((int) $warehouse_id);
3156 $sql .= " WHERE rowid=".((int) $this->id);
3157
3158 if ($this->db->query($sql)) {
3159 $this->warehouse_id = ($warehouse_id == 'NULL') ? null : $warehouse_id;
3160 return 1;
3161 } else {
3162 dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
3163 $this->error = $this->db->error();
3164 return 0;
3165 }
3166 }
3167
3168
3176 public function setDocModel($user, $modelpdf)
3177 {
3178 if (!$this->table_element) {
3179 dol_syslog(get_class($this)."::setDocModel was called on object with property table_element not defined", LOG_ERR);
3180 return -1;
3181 }
3182
3183 $newmodelpdf = dol_trunc($modelpdf, 255);
3184
3185 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3186 $sql .= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
3187 $sql .= " WHERE rowid = ".((int) $this->id);
3188
3189 dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
3190 $resql = $this->db->query($sql);
3191 if ($resql) {
3192 $this->model_pdf = $modelpdf;
3193 return 1;
3194 } else {
3195 dol_print_error($this->db);
3196 return 0;
3197 }
3198 }
3199
3200
3209 public function setBankAccount($fk_account, $notrigger = 0, $userused = null)
3210 {
3211 global $user;
3212
3213 if (empty($userused)) {
3214 $userused = $user;
3215 }
3216
3217 $error = 0;
3218
3219 if (!$this->table_element) {
3220 dol_syslog(get_class($this)."::setBankAccount was called on object with property table_element not defined", LOG_ERR);
3221 return -1;
3222 }
3223 $this->db->begin();
3224
3225 if ($fk_account < 0) {
3226 $fk_account = 'NULL';
3227 }
3228 dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
3229
3230 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3231 $sql .= " SET fk_account = ".((int) $fk_account);
3232 $sql .= " WHERE rowid=".((int) $this->id);
3233
3234 $resql = $this->db->query($sql);
3235 if (!$resql) {
3236 dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
3237 $this->error = $this->db->lasterror();
3238 $error++;
3239 } else {
3240 if (!$notrigger) {
3241 // Call trigger
3242 $this->context['bankaccountupdate'] = 1;
3243 $triggerName = strtoupper(get_class($this)).'_MODIFY';
3244 // Special cases
3245 if ($triggerName == 'FACTUREREC_MODIFY') {
3246 $triggerName = 'BILLREC_MODIFY';
3247 }
3248 $result = $this->call_trigger($triggerName, $userused);
3249 if ($result < 0) {
3250 $error++;
3251 }
3252 // End call trigger
3253 }
3254 }
3255 if ($error) {
3256 $this->db->rollback();
3257 return -1;
3258 } else {
3259 $this->fk_account = ($fk_account == 'NULL') ? null : $fk_account;
3260 $this->db->commit();
3261 return 1;
3262 }
3263 }
3264
3265
3266 // TODO: Move line related operations to CommonObjectLine?
3267
3268 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3278 public function line_order($renum = false, $rowidorder = 'ASC', $fk_parent_line = true)
3279 {
3280 // phpcs:enable
3281 if (!$this->table_element_line) {
3282 dol_syslog(get_class($this)."::line_order was called on object with property table_element_line not defined", LOG_ERR);
3283 return -1;
3284 }
3285 if (!$this->fk_element) {
3286 dol_syslog(get_class($this)."::line_order was called on object with property fk_element not defined", LOG_ERR);
3287 return -1;
3288 }
3289
3290 $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3291 if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3292 $fieldposition = 'position';
3293 }
3294
3295 // Count number of lines to reorder (according to choice $renum)
3296 $nl = 0;
3297 $sql = "SELECT count(rowid) FROM ".$this->db->prefix().$this->table_element_line;
3298 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3299 if (!$renum) {
3300 $sql .= " AND " . $fieldposition . " = 0";
3301 }
3302 if ($renum) {
3303 $sql .= " AND " . $fieldposition . " <> 0";
3304 }
3305
3306 dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
3307 $resql = $this->db->query($sql);
3308 if ($resql) {
3309 $row = $this->db->fetch_row($resql);
3310 $nl = $row[0];
3311 } else {
3312 dol_print_error($this->db);
3313 }
3314 if ($nl > 0) {
3315 // The goal of this part is to reorder all lines, with all children lines sharing the same counter that parents.
3316 $rows = array();
3317
3318 // We first search all lines that are parent lines (for multilevel details lines)
3319 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3320 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3321 if ($fk_parent_line) {
3322 $sql .= ' AND fk_parent_line IS NULL';
3323 }
3324 $sql .= " ORDER BY " . $fieldposition . " ASC, rowid " . $rowidorder;
3325
3326 dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
3327 $resql = $this->db->query($sql);
3328 if ($resql) {
3329 $i = 0;
3330 $num = $this->db->num_rows($resql);
3331 while ($i < $num) {
3332 $row = $this->db->fetch_row($resql);
3333 $rows[] = $row[0]; // Add parent line into array rows
3334 $children = $this->getChildrenOfLine($row[0]);
3335 if (!empty($children)) {
3336 foreach ($children as $child) {
3337 array_push($rows, $child);
3338 }
3339 }
3340 $i++;
3341 }
3342
3343 // Now we set a new number for each lines (parent and children with children included into parent tree)
3344 if (!empty($rows)) {
3345 foreach ($rows as $key => $row) {
3346 $this->updateRangOfLine($row, ($key + 1));
3347 }
3348 }
3349 } else {
3350 dol_print_error($this->db);
3351 }
3352 }
3353 return 1;
3354 }
3355
3363 public function getChildrenOfLine($id, $includealltree = 0)
3364 {
3365 $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3366 if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3367 $fieldposition = 'position';
3368 }
3369
3370 $rows = array();
3371
3372 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3373 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3374 $sql .= ' AND fk_parent_line = '.((int) $id);
3375 $sql .= " ORDER BY " . $fieldposition . " ASC";
3376
3377 dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id, LOG_DEBUG);
3378 $resql = $this->db->query($sql);
3379 if ($resql) {
3380 if ($this->db->num_rows($resql) > 0) {
3381 while ($row = $this->db->fetch_row($resql)) {
3382 $rows[] = $row[0];
3383 if ($includealltree) {
3384 $rows = array_merge($rows, $this->getChildrenOfLine($row[0], $includealltree));
3385 }
3386 }
3387 }
3388 }
3389 return $rows;
3390 }
3391
3392 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3400 public function line_up($rowid, $fk_parent_line = true)
3401 {
3402 // phpcs:enable
3403 $this->line_order(false, 'ASC', $fk_parent_line);
3404
3405 // Get rang of line
3406 $rang = $this->getRangOfLine($rowid);
3407
3408 // Update position of line
3409 $this->updateLineUp($rowid, $rang);
3410 }
3411
3412 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3420 public function line_down($rowid, $fk_parent_line = true)
3421 {
3422 // phpcs:enable
3423 $this->line_order(false, 'ASC', $fk_parent_line);
3424
3425 // Get rang of line
3426 $rang = $this->getRangOfLine($rowid);
3427
3428 // Get max value for rang
3429 $max = $this->line_max();
3430
3431 // Update position of line
3432 $this->updateLineDown($rowid, $rang, $max);
3433 }
3434
3442 public function updateRangOfLine($rowid, $rang)
3443 {
3444 global $hookmanager;
3445 $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3446 if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3447 $fieldposition = 'position';
3448 }
3449
3450 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3451 $sql .= ' WHERE rowid = '.((int) $rowid);
3452
3453 dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
3454 if (!$this->db->query($sql)) {
3455 dol_print_error($this->db);
3456 return -1;
3457 } else {
3458 $parameters = array('rowid' => $rowid, 'rang' => $rang, 'fieldposition' => $fieldposition);
3459 $action = '';
3460 $reshook = $hookmanager->executeHooks('afterRankOfLineUpdate', $parameters, $this, $action);
3461 return 1;
3462 }
3463 }
3464
3465 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3472 public function line_ajaxorder($rows)
3473 {
3474 // phpcs:enable
3475 $num = count($rows);
3476 for ($i = 0; $i < $num; $i++) {
3477 $this->updateRangOfLine($rows[$i], ($i + 1));
3478 }
3479 }
3480
3488 public function updateLineUp($rowid, $rang)
3489 {
3490 if ($rang > 1) {
3491 $fieldposition = 'rang';
3492 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3493 $fieldposition = 'position';
3494 }
3495
3496 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3497 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3498 $sql .= " AND " . $fieldposition . " = " . ((int) ($rang - 1));
3499 if ($this->db->query($sql)) {
3500 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang - 1));
3501 $sql .= ' WHERE rowid = '.((int) $rowid);
3502 if (!$this->db->query($sql)) {
3503 dol_print_error($this->db);
3504 }
3505 } else {
3506 dol_print_error($this->db);
3507 }
3508 }
3509 }
3510
3519 public function updateLineDown($rowid, $rang, $max)
3520 {
3521 if ($rang < $max) {
3522 $fieldposition = 'rang';
3523 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3524 $fieldposition = 'position';
3525 }
3526
3527 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3528 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3529 $sql .= " AND " . $fieldposition . " = " . ((int) ($rang + 1));
3530 if ($this->db->query($sql)) {
3531 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang + 1));
3532 $sql .= ' WHERE rowid = '.((int) $rowid);
3533 if (!$this->db->query($sql)) {
3534 dol_print_error($this->db);
3535 }
3536 } else {
3537 dol_print_error($this->db);
3538 }
3539 }
3540 }
3541
3548 public function getRangOfLine($rowid)
3549 {
3550 $fieldposition = 'rang';
3551 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3552 $fieldposition = 'position';
3553 }
3554
3555 $sql = "SELECT " . $fieldposition . " FROM ".$this->db->prefix().$this->table_element_line;
3556 $sql .= " WHERE rowid = ".((int) $rowid);
3557
3558 dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
3559 $resql = $this->db->query($sql);
3560 if ($resql) {
3561 $row = $this->db->fetch_row($resql);
3562 return $row[0];
3563 }
3564
3565 return 0;
3566 }
3567
3574 public function getIdOfLine($rang)
3575 {
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 = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3582 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3583 $sql .= " AND " . $fieldposition . " = ".((int) $rang);
3584 $resql = $this->db->query($sql);
3585 if ($resql) {
3586 $row = $this->db->fetch_row($resql);
3587 return $row[0];
3588 }
3589
3590 return 0;
3591 }
3592
3593 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3600 public function line_max($fk_parent_line = 0)
3601 {
3602 // phpcs:enable
3603 $positionfield = 'rang';
3604 if (in_array($this->table_element, array('bom_bom', 'product_attribute'))) {
3605 $positionfield = 'position';
3606 }
3607
3608 // Search the last rang with fk_parent_line
3609 if ($fk_parent_line) {
3610 $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
3611 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3612 $sql .= " AND fk_parent_line = ".((int) $fk_parent_line);
3613
3614 dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
3615 $resql = $this->db->query($sql);
3616 if ($resql) {
3617 $row = $this->db->fetch_row($resql);
3618 if (!empty($row[0])) {
3619 return $row[0];
3620 } else {
3621 return $this->getRangOfLine($fk_parent_line);
3622 }
3623 }
3624 } else {
3625 // If not, search the last rang of element
3626 $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
3627 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3628
3629 dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
3630 $resql = $this->db->query($sql);
3631 if ($resql) {
3632 $row = $this->db->fetch_row($resql);
3633 return $row[0];
3634 }
3635 }
3636
3637 return 0;
3638 }
3639
3640 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3647 public function update_ref_ext($ref_ext)
3648 {
3649 // phpcs:enable
3650 if (!$this->table_element) {
3651 dol_syslog(get_class($this)."::update_ref_ext was called on object with property table_element not defined", LOG_ERR);
3652 return -1;
3653 }
3654
3655 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3656 $sql .= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
3657 $sql .= " WHERE ".(isset($this->table_rowid) ? $this->table_rowid : 'rowid')." = ".((int) $this->id);
3658
3659 dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
3660 if ($this->db->query($sql)) {
3661 $this->ref_ext = $ref_ext;
3662 return 1;
3663 } else {
3664 $this->error = $this->db->error();
3665 return -1;
3666 }
3667 }
3668
3669 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3678 public function update_note($note, $suffix = '', $notrigger = 0)
3679 {
3680 // phpcs:enable
3681 global $user;
3682
3683 if (!$this->table_element) {
3684 $this->error = 'update_note was called on object with property table_element not defined';
3685 dol_syslog(get_class($this)."::update_note was called on object with property table_element not defined", LOG_ERR);
3686 return -1;
3687 }
3688 if (!in_array($suffix, array('', '_public', '_private'))) {
3689 $this->error = 'update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
3690 dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
3691 return -2;
3692 }
3693
3694 $newsuffix = $suffix;
3695
3696 // Special case
3697 if ($this->table_element == 'product' && $newsuffix == '_private') {
3698 $newsuffix = '';
3699 }
3700 if (in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
3701 $fieldusermod = "fk_user_mod";
3702 } elseif ($this->table_element == 'ecm_files') {
3703 $fieldusermod = "fk_user_m";
3704 } else {
3705 $fieldusermod = "fk_user_modif";
3706 }
3707 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3708 $sql .= " SET note".$newsuffix." = ".(!empty($note) ? ("'".$this->db->escape($note)."'") : "NULL");
3709 $sql .= ", ".$fieldusermod." = ".((int) $user->id);
3710 $sql .= " WHERE rowid = ".((int) $this->id);
3711
3712 dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
3713 if ($this->db->query($sql)) {
3714 if ($suffix == '_public') {
3715 $this->note_public = $note;
3716 } elseif ($suffix == '_private') {
3717 $this->note_private = $note;
3718 } else {
3719 $this->note = $note; // deprecated
3720 $this->note_private = $note;
3721 }
3722 if (empty($notrigger)) {
3723 switch ($this->element) {
3724 case 'societe':
3725 $trigger_name = 'COMPANY_MODIFY';
3726 break;
3727 case 'commande':
3728 $trigger_name = 'ORDER_MODIFY';
3729 break;
3730 case 'facture':
3731 $trigger_name = 'BILL_MODIFY';
3732 break;
3733 case 'invoice_supplier':
3734 $trigger_name = 'BILL_SUPPLIER_MODIFY';
3735 break;
3736 case 'facturerec':
3737 $trigger_name = 'BILLREC_MODIFIY';
3738 break;
3739 case 'expensereport':
3740 $trigger_name = 'EXPENSE_REPORT_MODIFY';
3741 break;
3742 default:
3743 $trigger_name = strtoupper($this->element) . '_MODIFY';
3744 }
3745 $ret = $this->call_trigger($trigger_name, $user);
3746 if ($ret < 0) {
3747 return -1;
3748 }
3749 }
3750 return 1;
3751 } else {
3752 $this->error = $this->db->lasterror();
3753 return -1;
3754 }
3755 }
3756
3757 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3766 public function update_note_public($note)
3767 {
3768 // phpcs:enable
3769 return $this->update_note($note, '_public');
3770 }
3771
3772 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3783 public function update_price($exclspec = 0, $roundingadjust = 'auto', $nodatabaseupdate = 0, $seller = null)
3784 {
3785 // phpcs:enable
3786 global $conf, $hookmanager, $action;
3787
3788 $parameters = array('exclspec' => $exclspec, 'roundingadjust' => $roundingadjust, 'nodatabaseupdate' => $nodatabaseupdate, 'seller' => $seller);
3789 $reshook = $hookmanager->executeHooks('updateTotalPrice', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3790 if ($reshook > 0) {
3791 return 1; // replacement code
3792 } elseif ($reshook < 0) {
3793 return -1; // failure
3794 } // reshook = 0 => execute normal code
3795
3796 // Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
3797 $isElementForSupplier = false;
3798 $roundTotalConstName = 'MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND'; // const for customer by default
3799 $MODULE = "";
3800 if ($this->element == 'propal') {
3801 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
3802 } elseif ($this->element == 'commande' || $this->element == 'order') {
3803 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
3804 } elseif ($this->element == 'facture' || $this->element == 'invoice') {
3805 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
3806 } elseif ($this->element == 'facture_fourn' || $this->element == 'supplier_invoice' || $this->element == 'invoice_supplier' || $this->element == 'invoice_supplier_rec') {
3807 $isElementForSupplier = true;
3808 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
3809 } elseif ($this->element == 'order_supplier' || $this->element == 'supplier_order') {
3810 $isElementForSupplier = true;
3811 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
3812 } elseif ($this->element == 'supplier_proposal') {
3813 $isElementForSupplier = true;
3814 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
3815 }
3816 if ($isElementForSupplier) {
3817 $roundTotalConstName = 'MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND_SUPPLIER'; // const for supplier
3818 }
3819
3820 if (!empty($MODULE)) {
3821 if (getDolGlobalString($MODULE)) {
3822 $modsactivated = explode(',', getDolGlobalString($MODULE));
3823 foreach ($modsactivated as $mod) {
3824 if (isModEnabled($mod)) {
3825 return 1; // update was disabled by specific setup
3826 }
3827 }
3828 }
3829 }
3830
3831 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3832
3833 $forcedroundingmode = $roundingadjust;
3834 if ($forcedroundingmode == 'auto' && isset($conf->global->{$roundTotalConstName})) {
3835 $forcedroundingmode = getDolGlobalString($roundTotalConstName);
3836 } elseif ($forcedroundingmode == 'auto') {
3837 $forcedroundingmode = '0';
3838 }
3839
3840 $error = 0;
3841
3842 $multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
3843
3844 // Define constants to find lines to sum (field name int the table_element_line not into table_element)
3845 $fieldtva = 'total_tva';
3846 $fieldlocaltax1 = 'total_localtax1';
3847 $fieldlocaltax2 = 'total_localtax2';
3848 $fieldup = 'subprice';
3849 $base_price_type = 'HT';
3850 if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
3851 $fieldtva = 'tva';
3852 $fieldup = 'pu_ht';
3853 }
3854 if ($this->element == 'invoice_supplier_rec') {
3855 $fieldup = 'pu_ht';
3856 }
3857 if ($this->element == 'expensereport') {
3858 // Force rounding mode to '0', otherwise when you set MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND to 1, you may have lines with different totals.
3859 // 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
3860 // and 5,16 on HT total and 1,04 on VAT total to get 6,20 on TTT total on second line (see #30051).
3861 $forcedroundingmode = '0';
3862 $fieldup = 'value_unit';
3863 $base_price_type = 'TTC';
3864 }
3865
3866 $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,";
3867 $sql .= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
3868 if ($this->table_element_line == 'facturedet') {
3869 $sql .= ', situation_percent';
3870 }
3871 $sql .= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
3872 $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
3873 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3874 if ($exclspec) {
3875 $product_field = 'product_type';
3876 if ($this->table_element_line == 'contratdet') {
3877 $product_field = ''; // contratdet table has no product_type field
3878 }
3879 if ($product_field) {
3880 $sql .= " AND ".$product_field." <> 9";
3881 }
3882 }
3883 $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
3884
3885 dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3886
3887 $resql = $this->db->query($sql);
3888 if ($resql) {
3889 $this->total_ht = 0;
3890 $this->total_tva = 0;
3891 $this->total_localtax1 = 0;
3892 $this->total_localtax2 = 0;
3893 $this->total_ttc = 0;
3894 $total_ht_by_vats = array();
3895 $total_tva_by_vats = array();
3896 $total_ttc_by_vats = array();
3897 $this->multicurrency_total_ht = 0;
3898 $this->multicurrency_total_tva = 0;
3899 $this->multicurrency_total_ttc = 0;
3900
3901 $this->db->begin();
3902
3903 $num = $this->db->num_rows($resql);
3904 $i = 0;
3905 while ($i < $num) {
3906 $obj = $this->db->fetch_object($resql);
3907
3908 // Note: There is no check on detail line and no check on total, if $forcedroundingmode = '0'
3909 $parameters = array('fk_element' => $obj->rowid);
3910 $reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3911
3912 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'
3913 // This part of code is to fix data. We should not call it too often.
3914 $localtax_array = array($obj->localtax1_type, $obj->localtax1_tx, $obj->localtax2_type, $obj->localtax2_tx);
3915 $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);
3916
3917 $diff_when_using_price_ht = price2num($tmpcal[1] - $obj->total_tva, 'MT', 1); // If price was set with tax price and unit price HT has a low number of digits, then we may have a diff on recalculation from unit price HT.
3918 $diff_on_current_total = price2num($obj->total_ttc - $obj->total_ht - $obj->total_tva - $obj->total_localtax1 - $obj->total_localtax2, 'MT', 1);
3919 //var_dump($obj->total_ht.' '.$obj->total_tva.' '.$obj->total_localtax1.' '.$obj->total_localtax2.' => '.$obj->total_ttc);
3920 //var_dump($diff_when_using_price_ht.' '.$diff_on_current_total);
3921
3922 if ($diff_on_current_total) {
3923 // This should not happen, we should always have in table: total_ttc = total_ht + total_vat + total_localtax1 + total_localtax2
3924 $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldtva." = ".price2num((float) $tmpcal[1]).", total_ttc = ".price2num((float) $tmpcal[2])." WHERE rowid = ".((int) $obj->rowid);
3925 dol_syslog('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);
3926 $resqlfix = $this->db->query($sqlfix);
3927 if (!$resqlfix) {
3928 dol_print_error($this->db, 'Failed to update line');
3929 }
3930 $obj->total_tva = $tmpcal[1];
3931 $obj->total_ttc = $tmpcal[2];
3932 } elseif ($diff_when_using_price_ht) {
3933 // After calculation from HT, total is consistent but we have found a difference between VAT part in calculation and into database and
3934 // we ask to force the use of rounding on line (like done on calculation) so we force update of line
3935 $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldtva." = ".price2num((float) $tmpcal[1]).", total_ttc = ".price2num((float) $tmpcal[2])." WHERE rowid = ".((int) $obj->rowid);
3936 dol_syslog('We found a line with different rounding data into detailed line (diff_when_using_price_ht = '.$diff_when_using_price_ht.' and diff_on_current_total = '.$diff_on_current_total.') for line rowid = '.$obj->rowid." (total vat of line calculated=".$tmpcal[1].", database=".$obj->total_tva."). We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix);
3937 $resqlfix = $this->db->query($sqlfix);
3938 if (!$resqlfix) {
3939 dol_print_error($this->db, 'Failed to update line');
3940 }
3941 $obj->total_tva = $tmpcal[1];
3942 $obj->total_ttc = $tmpcal[2];
3943 }
3944 }
3945
3946 $this->total_ht += $obj->total_ht; // The field visible at end of line detail
3947 $this->total_tva += $obj->total_tva;
3948 $this->total_localtax1 += $obj->total_localtax1;
3949 $this->total_localtax2 += $obj->total_localtax2;
3950 $this->total_ttc += $obj->total_ttc;
3951 $this->multicurrency_total_ht += $obj->multicurrency_total_ht; // The field visible at end of line detail
3952 $this->multicurrency_total_tva += $obj->multicurrency_total_tva;
3953 $this->multicurrency_total_ttc += $obj->multicurrency_total_ttc;
3954
3955 if (!isset($total_ht_by_vats[$obj->vatrate])) {
3956 $total_ht_by_vats[$obj->vatrate] = 0;
3957 }
3958 if (!isset($total_tva_by_vats[$obj->vatrate])) {
3959 $total_tva_by_vats[$obj->vatrate] = 0;
3960 }
3961 if (!isset($total_ttc_by_vats[$obj->vatrate])) {
3962 $total_ttc_by_vats[$obj->vatrate] = 0;
3963 }
3964 $total_ht_by_vats[$obj->vatrate] += $obj->total_ht;
3965 $total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
3966 $total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
3967
3968 if ($forcedroundingmode == '1') { // Check if we need adjustment onto line for vat. TODO This works on the company currency but not on foreign currency
3969 if ($base_price_type == 'TTC') {
3970 $tmpvat = price2num($total_ttc_by_vats[$obj->vatrate] * $obj->vatrate / (100 + $obj->vatrate), 'MT', 1);
3971 } else {
3972 $tmpvat = price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
3973 }
3974 $diff = price2num($total_tva_by_vats[$obj->vatrate] - (float) $tmpvat, 'MT', 1);
3975 //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";
3976 if ($diff) {
3977 if (abs((float) $diff) > (10 * pow(10, -1 * getDolGlobalInt('MAIN_MAX_DECIMALS_TOT', 0)))) {
3978 // If error is more than 10 times the accuracy of rounding. This should not happen.
3979 $errmsg = 'A rounding difference was detected into TOTAL but is too high to be corrected. Some data in your lines may be corrupted. Try to edit each line manually to fix this before restarting.';
3980 dol_syslog($errmsg, LOG_WARNING);
3981 $this->error = $errmsg;
3982 $error++;
3983 break;
3984 }
3985 if ($base_price_type == 'TTC') {
3986 $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);
3987 dol_syslog('We found a difference of '.$diff.' for line rowid = '.$obj->rowid.". We fix the total_vat and total_ht of line by running sqlfix = ".$sqlfix);
3988 } else {
3989 $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);
3990 dol_syslog('We found a difference of '.$diff.' for line rowid = '.$obj->rowid.". We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix);
3991 }
3992
3993 $resqlfix = $this->db->query($sqlfix);
3994
3995 if (!$resqlfix) {
3996 dol_print_error($this->db, 'Failed to update line');
3997 }
3998
3999 $this->total_tva = (float) price2num($this->total_tva - (float) $diff, '', 1);
4000 $total_tva_by_vats[$obj->vatrate] = (float) price2num($total_tva_by_vats[$obj->vatrate] - (float) $diff, '', 1);
4001 if ($base_price_type == 'TTC') {
4002 $this->total_ht = (float) price2num($this->total_ht + (float) $diff, '', 1);
4003 $total_ht_by_vats[$obj->vatrate] = (float) price2num($total_ht_by_vats[$obj->vatrate] + (float) $diff, '', 1);
4004 } else {
4005 $this->total_ttc = (float) price2num($this->total_ttc - (float) $diff, '', 1);
4006 $total_ttc_by_vats[$obj->vatrate] = (float) price2num($total_ttc_by_vats[$obj->vatrate] - (float) $diff, '', 1);
4007 }
4008 }
4009 }
4010
4011 $i++;
4012 }
4013
4014 // Add revenue stamp to total
4015 $this->total_ttc += isset($this->revenuestamp) ? $this->revenuestamp : 0;
4016 $this->multicurrency_total_ttc += isset($this->revenuestamp) ? ($this->revenuestamp * $multicurrency_tx) : 0;
4017
4018 // Situations totals
4019 if (!empty($this->situation_cycle_ref) && !empty($this->situation_counter) && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits')) {
4020 include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
4021 if ($this->type != Facture::TYPE_CREDIT_NOTE) { // @phpstan-ignore-line
4022 if (getDolGlobalInt('INVOICE_USE_SITUATION') != 2) {
4023 $prev_sits = $this->get_prev_sits();
4024
4025 foreach ($prev_sits as $sit) { // $sit is an object Facture loaded with a fetch.
4026 $this->total_ht -= $sit->total_ht;
4027 $this->total_tva -= $sit->total_tva;
4028 $this->total_localtax1 -= $sit->total_localtax1;
4029 $this->total_localtax2 -= $sit->total_localtax2;
4030 $this->total_ttc -= $sit->total_ttc;
4031 $this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
4032 $this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
4033 $this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
4034 }
4035 }
4036 }
4037 }
4038
4039 // Clean total
4040 $this->total_ht = (float) price2num($this->total_ht);
4041 $this->total_tva = (float) price2num($this->total_tva);
4042 $this->total_localtax1 = (float) price2num($this->total_localtax1);
4043 $this->total_localtax2 = (float) price2num($this->total_localtax2);
4044 $this->total_ttc = (float) price2num($this->total_ttc);
4045
4046 $this->db->free($resql);
4047
4048 // Now update global fields total_ht, total_ttc, total_tva, total_localtax1, total_localtax2, multicurrency_total_* of main object
4049 $fieldht = 'total_ht';
4050 $fieldtva = 'tva';
4051 $fieldlocaltax1 = 'localtax1';
4052 $fieldlocaltax2 = 'localtax2';
4053 $fieldttc = 'total_ttc';
4054 // Specific code for backward compatibility with old field names
4055 if (in_array($this->element, array('propal', 'commande', 'facture', 'facturerec', 'supplier_proposal', 'order_supplier', 'facture_fourn', 'invoice_supplier', 'invoice_supplier_rec', 'expensereport'))) {
4056 $fieldtva = 'total_tva';
4057 }
4058
4059 if (!$error && empty($nodatabaseupdate)) {
4060 $sql = "UPDATE ".$this->db->prefix().$this->table_element.' SET';
4061 $sql .= " ".$fieldht." = ".((float) price2num($this->total_ht, 'MT', 1)).",";
4062 $sql .= " ".$fieldtva." = ".((float) price2num($this->total_tva, 'MT', 1)).",";
4063 $sql .= " ".$fieldlocaltax1." = ".((float) price2num($this->total_localtax1, 'MT', 1)).",";
4064 $sql .= " ".$fieldlocaltax2." = ".((float) price2num($this->total_localtax2, 'MT', 1)).",";
4065 $sql .= " ".$fieldttc." = ".((float) price2num($this->total_ttc, 'MT', 1));
4066 $sql .= ", multicurrency_total_ht = ".((float) price2num($this->multicurrency_total_ht, 'MT', 1));
4067 $sql .= ", multicurrency_total_tva = ".((float) price2num($this->multicurrency_total_tva, 'MT', 1));
4068 $sql .= ", multicurrency_total_ttc = ".((float) price2num($this->multicurrency_total_ttc, 'MT', 1));
4069 $sql .= " WHERE rowid = ".((int) $this->id);
4070
4071 dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
4072 $resql = $this->db->query($sql);
4073
4074 if (!$resql) {
4075 $error++;
4076 $this->error = $this->db->lasterror();
4077 $this->errors[] = $this->db->lasterror();
4078 }
4079 }
4080
4081 if (!$error) {
4082 $this->db->commit();
4083 return 1;
4084 } else {
4085 $this->db->rollback();
4086 return -1;
4087 }
4088 } else {
4089 dol_print_error($this->db, 'Bad request in update_price');
4090 return -1;
4091 }
4092 }
4093
4094 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4105 public function add_object_linked($origin = null, $origin_id = null, $f_user = null, $notrigger = 0)
4106 {
4107 // phpcs:enable
4108 global $user, $hookmanager, $action;
4109 $origin = (!empty($origin) ? $origin : $this->origin);
4110 $origin_id = (!empty($origin_id) ? $origin_id : $this->origin_id);
4111 $f_user = isset($f_user) ? $f_user : $user;
4112
4113 // Special case
4114 if ($origin == 'order') {
4115 $origin = 'commande';
4116 }
4117 if ($origin == 'invoice') {
4118 $origin = 'facture';
4119 }
4120 if ($origin == 'invoice_template') {
4121 $origin = 'facturerec';
4122 }
4123 if ($origin == 'supplierorder') {
4124 $origin = 'order_supplier';
4125 }
4126
4127 // Add module part to target type
4128 $targettype = $this->getElementType();
4129
4130 $parameters = array('targettype' => $targettype);
4131 // Hook for explicitly set the targettype if it must be different than $this->element
4132 $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4133 if ($reshook > 0) {
4134 if (!empty($hookmanager->resArray['targettype'])) {
4135 $targettype = $hookmanager->resArray['targettype'];
4136 }
4137 }
4138
4139 $this->db->begin();
4140 $error = 0;
4141
4142 $sql = "INSERT INTO " . $this->db->prefix() . "element_element (";
4143 $sql .= "fk_source";
4144 $sql .= ", sourcetype";
4145 $sql .= ", fk_target";
4146 $sql .= ", targettype";
4147 $sql .= ") VALUES (";
4148 $sql .= ((int) $origin_id);
4149 $sql .= ", '" . $this->db->escape($origin) . "'";
4150 $sql .= ", " . ((int) $this->id);
4151 $sql .= ", '" . $this->db->escape($targettype) . "'";
4152 $sql .= ")";
4153
4154 dol_syslog(get_class($this) . "::add_object_linked", LOG_DEBUG);
4155 if ($this->db->query($sql)) {
4156 if (!$notrigger) {
4157 // Call trigger
4158 $this->context['link_origin'] = $origin;
4159 $this->context['link_origin_id'] = $origin_id;
4160 $result = $this->call_trigger('OBJECT_LINK_INSERT', $f_user);
4161 if ($result < 0) {
4162 $error++;
4163 }
4164 // End call triggers
4165 }
4166 } else {
4167 $this->error = $this->db->lasterror();
4168 $error++;
4169 }
4170
4171 if (!$error) {
4172 $this->db->commit();
4173 return 1;
4174 } else {
4175 $this->db->rollback();
4176 return 0;
4177 }
4178 }
4179
4185 public function getElementType()
4186 {
4187 // 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.
4188 // 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).
4189 $coreModule = array('knowledgemanagement', 'partnership', 'workstation', 'ticket', 'recruitment', 'eventorganization', 'asset');
4190 // Add module part to target type if object has $module property and isn't in core modules.
4191 return ((!empty($this->module) && !in_array($this->module, $coreModule)) ? $this->module.'_' : '').$this->element;
4192 }
4193
4194
4217 public function fetchObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1)
4218 {
4219 global $conf, $hookmanager, $action;
4220
4221 // Important for pdf generation time reduction
4222 // This boolean is true if $this->linkedObjects has already been loaded with all objects linked without filter
4223 // If you need to force the reload, you can call clearObjectLinkedCache() before calling fetchObjectLinked()
4224 if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
4225 return 1;
4226 }
4227
4228 $this->linkedObjectsIds = array();
4229 $this->linkedObjects = array();
4230
4231 $justsource = false;
4232 $justtarget = false;
4233 $withtargettype = false;
4234 $withsourcetype = false;
4235
4236 $parameters = array('sourcetype' => $sourcetype, 'sourceid' => $sourceid, 'targettype' => $targettype, 'targetid' => $targetid);
4237 // Hook for explicitly set the targettype if it must be differtent than $this->element
4238 $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4239 if ($reshook > 0) {
4240 if (!empty($hookmanager->resArray['sourcetype'])) {
4241 $sourcetype = $hookmanager->resArray['sourcetype'];
4242 }
4243 if (!empty($hookmanager->resArray['sourceid'])) {
4244 $sourceid = $hookmanager->resArray['sourceid'];
4245 }
4246 if (!empty($hookmanager->resArray['targettype'])) {
4247 $targettype = $hookmanager->resArray['targettype'];
4248 }
4249 if (!empty($hookmanager->resArray['targetid'])) {
4250 $targetid = $hookmanager->resArray['targetid'];
4251 }
4252 }
4253
4254 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid)) {
4255 $justsource = true; // the source (id and type) is a search criteria
4256 if (!empty($targettype)) {
4257 $withtargettype = true;
4258 }
4259 }
4260 if (!empty($targetid) && !empty($targettype) && empty($sourceid)) {
4261 $justtarget = true; // the target (id and type) is a search criteria
4262 if (!empty($sourcetype)) {
4263 $withsourcetype = true;
4264 }
4265 }
4266
4267 $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
4268 $targetid = (!empty($targetid) ? $targetid : $this->id);
4269 $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
4270 $targettype = (!empty($targettype) ? $targettype : $this->element);
4271
4272 /*if (empty($sourceid) && empty($targetid))
4273 {
4274 dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
4275 return -1;
4276 }*/
4277
4278 // Links between objects are stored in table element_element
4279 $sql = "SELECT rowid, fk_source, sourcetype, fk_target, targettype";
4280 $sql .= " FROM ".$this->db->prefix()."element_element";
4281 $sql .= " WHERE ";
4282 if ($justsource || $justtarget) {
4283 if ($justsource) {
4284 $sql .= "fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."'";
4285 if ($withtargettype) {
4286 $sql .= " AND targettype = '".$this->db->escape($targettype)."'";
4287 }
4288 } elseif ($justtarget) {
4289 $sql .= "fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."'";
4290 if ($withsourcetype) {
4291 $sql .= " AND sourcetype = '".$this->db->escape($sourcetype)."'";
4292 }
4293 }
4294 } else {
4295 $sql .= "(fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."')";
4296 $sql .= " ".$clause." (fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."')";
4297 if ($loadalsoobjects && $this->id > 0 && $sourceid == $this->id && $sourcetype == $this->element && $targetid == $this->id && $targettype == $this->element && $clause == 'OR') {
4298 $this->linkedObjectsFullLoaded[$this->id] = true;
4299 }
4300 }
4301 $sql .= " ORDER BY ".$orderby;
4302
4303 dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
4304 $resql = $this->db->query($sql);
4305 if ($resql) {
4306 $num = $this->db->num_rows($resql);
4307 $i = 0;
4308 while ($i < $num) {
4309 $obj = $this->db->fetch_object($resql);
4310 if ($justsource || $justtarget) {
4311 if ($justsource) {
4312 $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
4313 } elseif ($justtarget) {
4314 $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
4315 }
4316 } else {
4317 if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype) {
4318 $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
4319 }
4320 if ($obj->fk_target == $targetid && $obj->targettype == $targettype) {
4321 $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
4322 }
4323 }
4324 $i++;
4325 }
4326
4327 if (!empty($this->linkedObjectsIds)) {
4328 $tmparray = $this->linkedObjectsIds;
4329 foreach ($tmparray as $objecttype => $objectids) { // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
4330 $element_properties = getElementProperties($objecttype);
4331 $element = $element_properties['element'];
4332 $classPath = $element_properties['classpath'];
4333 $classFile = $element_properties['classfile'];
4334 $className = $element_properties['classname'];
4335 $module = $element_properties['module'];
4336
4337 // Here $module, $classFile and $className are set, we can use them.
4338 if (isModEnabled($module) && (($element != $this->element) || $alsosametype)) {
4339 if ($loadalsoobjects && (is_numeric($loadalsoobjects) || ($loadalsoobjects === $objecttype))) {
4340 dol_include_once('/'.$classPath.'/'.$classFile.'.class.php');
4341 if (class_exists($className)) {
4342 foreach ($objectids as $i => $objectid) { // $i is rowid into llx_element_element
4343 $object = new $className($this->db);
4344 $ret = $object->fetch($objectid);
4345 if ($ret >= 0) {
4346 $this->linkedObjects[$objecttype][$i] = $object;
4347 }
4348 }
4349 }
4350 }
4351 } else {
4352 unset($this->linkedObjectsIds[$objecttype]);
4353 }
4354 }
4355 }
4356 return 1;
4357 } else {
4358 dol_print_error($this->db);
4359 return -1;
4360 }
4361 }
4362
4369 public function clearObjectLinkedCache()
4370 {
4371 if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
4372 unset($this->linkedObjectsFullLoaded[$this->id]);
4373 }
4374
4375 return 1;
4376 }
4377
4390 public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $f_user = null, $notrigger = 0)
4391 {
4392 global $user;
4393 $updatesource = false;
4394 $updatetarget = false;
4395 $f_user = isset($f_user) ? $f_user : $user;
4396
4397 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4398 $updatesource = true;
4399 } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4400 $updatetarget = true;
4401 }
4402
4403 $this->db->begin();
4404 $error = 0;
4405
4406 $sql = "UPDATE " . $this->db->prefix() . "element_element SET ";
4407 if ($updatesource) {
4408 $sql .= "fk_source = " . ((int) $sourceid);
4409 $sql .= ", sourcetype = '" . $this->db->escape($sourcetype) . "'";
4410 $sql .= " WHERE fk_target = " . ((int) $this->id);
4411 $sql .= " AND targettype = '" . $this->db->escape($this->element) . "'";
4412 } elseif ($updatetarget) {
4413 $sql .= "fk_target = " . ((int) $targetid);
4414 $sql .= ", targettype = '" . $this->db->escape($targettype) . "'";
4415 $sql .= " WHERE fk_source = " . ((int) $this->id);
4416 $sql .= " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4417 }
4418
4419 dol_syslog(get_class($this) . "::updateObjectLinked", LOG_DEBUG);
4420 if ($this->db->query($sql)) {
4421 if (!$notrigger) {
4422 // Call trigger
4423 $this->context['link_source_id'] = $sourceid;
4424 $this->context['link_source_type'] = $sourcetype;
4425 $this->context['link_target_id'] = $targetid;
4426 $this->context['link_target_type'] = $targettype;
4427 $result = $this->call_trigger('OBJECT_LINK_MODIFY', $f_user);
4428 if ($result < 0) {
4429 $error++;
4430 }
4431 // End call triggers
4432 }
4433 } else {
4434 $this->error = $this->db->lasterror();
4435 $error++;
4436 }
4437
4438 if (!$error) {
4439 $this->db->commit();
4440 return 1;
4441 } else {
4442 $this->db->rollback();
4443 return -1;
4444 }
4445 }
4446
4460 public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = 0, $f_user = null, $notrigger = 0)
4461 {
4462 global $user;
4463 $deletesource = false;
4464 $deletetarget = false;
4465 $f_user = isset($f_user) ? $f_user : $user;
4466
4467 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4468 $deletesource = true;
4469 } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4470 $deletetarget = true;
4471 }
4472
4473 $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
4474 $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
4475 $targetid = (!empty($targetid) ? $targetid : $this->id);
4476 $targettype = (!empty($targettype) ? $targettype : $this->element);
4477 $this->db->begin();
4478 $error = 0;
4479
4480 if (!$notrigger) {
4481 // Call trigger
4482 $this->context['link_id'] = $rowid;
4483 $this->context['link_source_id'] = $sourceid;
4484 $this->context['link_source_type'] = $sourcetype;
4485 $this->context['link_target_id'] = $targetid;
4486 $this->context['link_target_type'] = $targettype;
4487 $result = $this->call_trigger('OBJECT_LINK_DELETE', $f_user);
4488 if ($result < 0) {
4489 $error++;
4490 }
4491 // End call triggers
4492 }
4493
4494 if (!$error) {
4495 $sql = "DELETE FROM " . $this->db->prefix() . "element_element";
4496 $sql .= " WHERE";
4497 if ($rowid > 0) {
4498 $sql .= " rowid = " . ((int) $rowid);
4499 } else {
4500 if ($deletesource) {
4501 $sql .= " fk_source = " . ((int) $sourceid) . " AND sourcetype = '" . $this->db->escape($sourcetype) . "'";
4502 $sql .= " AND fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "'";
4503 } elseif ($deletetarget) {
4504 $sql .= " fk_target = " . ((int) $targetid) . " AND targettype = '" . $this->db->escape($targettype) . "'";
4505 $sql .= " AND fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4506 } else {
4507 $sql .= " (fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "')";
4508 $sql .= " OR";
4509 $sql .= " (fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "')";
4510 }
4511 }
4512
4513 dol_syslog(get_class($this) . "::deleteObjectLinked", LOG_DEBUG);
4514 if (!$this->db->query($sql)) {
4515 $this->error = $this->db->lasterror();
4516 $this->errors[] = $this->error;
4517 $error++;
4518 }
4519 }
4520
4521 if (!$error) {
4522 $this->db->commit();
4523 return 1;
4524 } else {
4525 $this->db->rollback();
4526 return 0;
4527 }
4528 }
4529
4539 public static function getAllItemsLinkedByObjectID($fk_object_where, $field_select, $field_where, $table_element)
4540 {
4541 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4542 return -1;
4543 }
4544 if (!preg_match('/^[_a-zA-Z0-9]+$/', $field_select)) {
4545 dol_syslog('Invalid value $field_select for parameter '.$field_select.' in call to getAllItemsLinkedByObjectID(). Must be a single field name.', LOG_ERR);
4546 }
4547
4548 global $db;
4549
4550 $sql = "SELECT ".$field_select." FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4551 $resql = $db->query($sql);
4552
4553 $TRes = array();
4554 if (!empty($resql)) {
4555 while ($res = $db->fetch_object($resql)) {
4556 $TRes[] = $res->{$field_select};
4557 }
4558 }
4559
4560 return $TRes;
4561 }
4562
4571 public static function getCountOfItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
4572 {
4573 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4574 return -1;
4575 }
4576
4577 global $db;
4578
4579 $sql = "SELECT COUNT(*) as nb FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4580 $resql = $db->query($sql);
4581 $n = 0;
4582 if ($resql) {
4583 $res = $db->fetch_object($resql);
4584 if ($res) {
4585 $n = $res->nb;
4586 }
4587 }
4588
4589 return $n;
4590 }
4591
4600 public static function deleteAllItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
4601 {
4602 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4603 return -1;
4604 }
4605
4606 global $db;
4607
4608 $sql = "DELETE FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4609 $resql = $db->query($sql);
4610
4611 if (empty($resql)) {
4612 return 0;
4613 }
4614
4615 return 1;
4616 }
4617
4628 public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '', $fieldstatus = 'fk_statut')
4629 {
4630 global $user;
4631
4632 $savElementId = $elementId; // To be used later to know if we were using the method using the id of this or not.
4633
4634 $elementId = (!empty($elementId) ? $elementId : $this->id);
4635 $elementTable = (!empty($elementType) ? $elementType : $this->table_element);
4636
4637 $this->db->begin();
4638
4639 if ($elementTable == 'facture_rec') {
4640 $fieldstatus = "suspended";
4641 }
4642 if ($elementTable == 'mailing') {
4643 $fieldstatus = "statut";
4644 }
4645 if ($elementTable == 'cronjob') {
4646 $fieldstatus = "status";
4647 }
4648 if ($elementTable == 'user') {
4649 $fieldstatus = "statut";
4650 }
4651 if ($elementTable == 'expensereport') {
4652 $fieldstatus = "fk_statut";
4653 }
4654 if ($elementTable == 'receptiondet_batch') {
4655 $fieldstatus = "status";
4656 }
4657 if ($elementTable == 'prelevement_bons') {
4658 $fieldstatus = "statut";
4659 }
4660 if (isset($this->fields) && is_array($this->fields) && array_key_exists('status', $this->fields)) {
4661 $fieldstatus = 'status';
4662 }
4663
4664 $sql = "UPDATE ".$this->db->prefix().$elementTable;
4665 $sql .= " SET ".$fieldstatus." = ".((int) $status);
4666 // If status = 1 = validated, update also fk_user_valid
4667 // TODO Replace the test on $elementTable by doing a test on existence of the field in $this->fields
4668 if ($status == 1 && in_array($elementTable, array('expensereport', 'inventory'))) {
4669 $sql .= ", fk_user_valid = ".((int) $user->id);
4670 }
4671 if ($status == 1 && in_array($elementTable, array('expensereport'))) {
4672 $sql .= ", date_valid = '".$this->db->idate(dol_now())."'";
4673 }
4674 if ($status == 1 && in_array($elementTable, array('inventory'))) {
4675 $sql .= ", date_validation = '".$this->db->idate(dol_now())."'";
4676 }
4677 $sql .= " WHERE rowid = ".((int) $elementId);
4678 $sql .= " AND ".$fieldstatus." <> ".((int) $status); // We avoid update if status already correct
4679
4680 dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
4681 $resql = $this->db->query($sql);
4682 if ($resql) {
4683 $error = 0;
4684
4685 $nb_rows_affected = $this->db->affected_rows($resql); // should be 1 or 0 if status was already correct
4686
4687 if ($nb_rows_affected > 0) {
4688 if (empty($trigkey)) {
4689 // Try to guess trigkey (for backward compatibility, now we should have trigkey defined into the call of setStatus)
4690 if ($this->element == 'supplier_proposal' && $status == 2) {
4691 $trigkey = 'SUPPLIER_PROPOSAL_SIGN'; // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
4692 }
4693 if ($this->element == 'supplier_proposal' && $status == 3) {
4694 $trigkey = 'SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
4695 }
4696 if ($this->element == 'supplier_proposal' && $status == 4) {
4697 $trigkey = 'SUPPLIER_PROPOSAL_CLOSE'; // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
4698 }
4699 if ($this->element == 'fichinter' && $status == 3) {
4700 $trigkey = 'FICHINTER_CLASSIFY_DONE';
4701 }
4702 if ($this->element == 'fichinter' && $status == 2) {
4703 $trigkey = 'FICHINTER_CLASSIFY_BILLED';
4704 }
4705 if ($this->element == 'fichinter' && $status == 1) {
4706 $trigkey = 'FICHINTER_CLASSIFY_UNBILLED';
4707 }
4708 }
4709
4710 if ($trigkey) {
4711 // Call trigger
4712 $result = $this->call_trigger($trigkey, $user);
4713 if ($result < 0) {
4714 $error++;
4715 }
4716 // End call triggers
4717 }
4718 } else {
4719 // The status was probably already good. We do nothing more, no triggers.
4720 }
4721
4722 if (!$error) {
4723 $this->db->commit();
4724
4725 if (empty($savElementId)) {
4726 // If the element we update is $this (so $elementId was provided as null)
4727 if ($fieldstatus == 'tosell') {
4728 $this->status = $status;
4729 } elseif ($fieldstatus == 'tobuy') {
4730 $this->status_buy = $status; // @phpstan-ignore-line
4731 } else {
4732 $this->status = $status;
4733 }
4734 }
4735
4736 return 1;
4737 } else {
4738 $this->db->rollback();
4739 dol_syslog(get_class($this)."::setStatut ".$this->error, LOG_ERR);
4740 return -1;
4741 }
4742 } else {
4743 $this->error = $this->db->lasterror();
4744 $this->db->rollback();
4745 return -1;
4746 }
4747 }
4748
4749
4757 public function getCanvas($id = 0, $ref = '')
4758 {
4759 global $conf;
4760
4761 if (empty($id) && empty($ref)) {
4762 return 0;
4763 }
4764 if (getDolGlobalString('MAIN_DISABLE_CANVAS')) {
4765 return 0; // To increase speed. Not enabled by default.
4766 }
4767
4768 // Clean parameters
4769 $ref = trim($ref);
4770
4771 $sql = "SELECT rowid, canvas";
4772 $sql .= " FROM ".$this->db->prefix().$this->table_element;
4773 $sql .= " WHERE entity IN (".getEntity($this->element).")";
4774 if (!empty($id)) {
4775 $sql .= " AND rowid = ".((int) $id);
4776 }
4777 if (!empty($ref)) {
4778 $sql .= " AND ref = '".$this->db->escape($ref)."'";
4779 }
4780
4781 $resql = $this->db->query($sql);
4782 if ($resql) {
4783 $obj = $this->db->fetch_object($resql);
4784 if ($obj) {
4785 $this->canvas = $obj->canvas;
4786 return 1;
4787 } else {
4788 return 0;
4789 }
4790 } else {
4791 dol_print_error($this->db);
4792 return -1;
4793 }
4794 }
4795
4796
4803 public function getSpecialCode($lineid)
4804 {
4805 $sql = "SELECT special_code FROM ".$this->db->prefix().$this->table_element_line;
4806 $sql .= " WHERE rowid = ".((int) $lineid);
4807 $resql = $this->db->query($sql);
4808 if ($resql) {
4809 $row = $this->db->fetch_row($resql);
4810 return (!empty($row[0]) ? $row[0] : 0);
4811 }
4812
4813 return 0;
4814 }
4815
4824 public function isObjectUsed($id = 0, $entity = 0)
4825 {
4826 global $langs;
4827
4828 if (empty($id)) {
4829 $id = $this->id;
4830 }
4831
4832 // Check parameters
4833 if (!isset($this->childtables) || !is_array($this->childtables) || count($this->childtables) == 0) {
4834 dol_print_error(null, 'Called isObjectUsed on a class with property this->childtables not defined');
4835 return -1;
4836 }
4837
4838 $arraytoscan = $this->childtables; // array('tablename'=>array('fk_element'=>'parentfield'), ...) or array('tablename'=>array('parent'=>table_parent, 'parentkey'=>'nameoffieldforparentfkkey'), ...)
4839 // For backward compatibility, we check if array is old format array('tablename1', 'tablename2', ...)
4840 $tmparray = array_keys($this->childtables);
4841 if (is_numeric($tmparray[0])) {
4842 $arraytoscan = array_flip($this->childtables);
4843 }
4844
4845 // Test if child exists
4846 $haschild = 0;
4847 foreach ($arraytoscan as $table => $element) {
4848 //print $id.'-'.$table.'-'.$elementname.'<br>';
4849 // Check if element can be deleted
4850 $sql = "SELECT COUNT(*) as nb";
4851 $sql .= " FROM ".$this->db->prefix().$table." as c";
4852 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4853 $sql .= ", ".$this->db->prefix().$element['parent']." as p";
4854 }
4855 if (!empty($element['fk_element'])) {
4856 $sql .= " WHERE c.".$element['fk_element']." = ".((int) $id);
4857 } else {
4858 $sql .= " WHERE c.".$this->fk_element." = ".((int) $id);
4859 }
4860 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4861 $sql .= " AND c.".$element['parentkey']." = p.rowid";
4862 }
4863 if (!empty($element['parent']) && !empty($element['parenttypefield']) && !empty($element['parenttypevalue'])) {
4864 $sql .= " AND c.".$element['parenttypefield']." = '".$this->db->escape($element['parenttypevalue'])."'";
4865 }
4866 if (!empty($entity)) {
4867 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4868 $sql .= " AND p.entity = ".((int) $entity);
4869 } else {
4870 $sql .= " AND c.entity = ".((int) $entity);
4871 }
4872 }
4873
4874 $resql = $this->db->query($sql);
4875 if ($resql) {
4876 $obj = $this->db->fetch_object($resql);
4877 if ($obj->nb > 0) {
4878 $langs->load("errors");
4879 //print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
4880 $haschild += $obj->nb;
4881 if (is_numeric($element)) { // very old usage array('table1', 'table2', ...)
4882 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $table);
4883 } elseif (is_string($element)) { // old usage array('table1' => 'TranslateKey1', 'table2' => 'TranslateKey2', ...)
4884 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element));
4885 } else { // new usage: $element['name']=Translation key
4886 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element['name']));
4887 }
4888 break; // We found at least one, we stop here
4889 }
4890 } else {
4891 $this->errors[] = $this->db->lasterror();
4892 return -1;
4893 }
4894 }
4895 if ($haschild > 0) {
4896 $this->errors[] = "ErrorRecordHasChildren";
4897 return $haschild;
4898 } else {
4899 return 0;
4900 }
4901 }
4902
4909 public function hasProductsOrServices($predefined = -1)
4910 {
4911 $nb = 0;
4912
4913 foreach ($this->lines as $key => $val) {
4914 $qualified = 0;
4915 if ($predefined == -1) {
4916 $qualified = 1;
4917 }
4918 if ($predefined == 1 && $val->fk_product > 0) {
4919 $qualified = 1;
4920 }
4921 if ($predefined == 0 && $val->fk_product <= 0) {
4922 $qualified = 1;
4923 }
4924 if ($predefined == 2 && $val->fk_product > 0 && $val->product_type == 0) {
4925 $qualified = 1;
4926 }
4927 if ($predefined == 3 && $val->fk_product > 0 && $val->product_type == 1) {
4928 $qualified = 1;
4929 }
4930 if ($qualified) {
4931 $nb++;
4932 }
4933 }
4934 dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
4935 return $nb;
4936 }
4937
4943 public function getTotalDiscount()
4944 {
4945 if (!empty($this->table_element_line)) {
4946 $total_discount = 0.00;
4947
4948 $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
4949 $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
4950 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
4951
4952 dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
4953 $resql = $this->db->query($sql);
4954 if ($resql) {
4955 $num = $this->db->num_rows($resql);
4956 $i = 0;
4957 while ($i < $num) {
4958 $obj = $this->db->fetch_object($resql);
4959
4960 $pu_ht = $obj->pu_ht;
4961 $qty = $obj->qty;
4962 $total_ht = $obj->total_ht;
4963
4964 $total_discount_line = (float) price2num(($pu_ht * $qty) - $total_ht, 'MT');
4965 $total_discount += $total_discount_line;
4966
4967 $i++;
4968 }
4969 }
4970
4971 //print $total_discount; exit;
4972 return (float) price2num($total_discount);
4973 }
4974
4975 return null;
4976 }
4977
4978
4985 public function getTotalWeightVolume()
4986 {
4987 $totalWeight = 0;
4988 $totalVolume = 0;
4989 // defined for shipment only
4990 $totalOrdered = '';
4991 // defined for shipment only
4992 $totalToShip = '';
4993
4994 if (empty($this->lines)) {
4995 return array();
4996 }
4997
4998 foreach ($this->lines as $line) {
4999 if (isset($line->qty_asked)) {
5000 if (empty($totalOrdered)) {
5001 $totalOrdered = 0; // Avoid warning because $totalOrdered is ''
5002 }
5003 $totalOrdered += $line->qty_asked; // defined for shipment only
5004 }
5005 if (isset($line->qty_shipped)) {
5006 if (empty($totalToShip)) {
5007 $totalToShip = 0; // Avoid warning because $totalToShip is ''
5008 }
5009 $totalToShip += $line->qty_shipped; // defined for shipment only
5010 } elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty)) {
5011 if (empty($totalToShip)) {
5012 $totalToShip = 0;
5013 }
5014 $totalToShip += $line->qty; // defined for reception only
5015 }
5016
5017 // Define qty, weight, volume, weight_units, volume_units
5018 if ($this->element == 'shipping') {
5019 // for shipments
5020 $qty = $line->qty_shipped ? $line->qty_shipped : 0;
5021 } else {
5022 $qty = $line->qty ? $line->qty : 0;
5023 }
5024
5025 $weight = !empty($line->weight) ? $line->weight : 0;
5026 ($weight == 0 && !empty($line->product->weight)) ? $weight = $line->product->weight : 0;
5027 $volume = !empty($line->volume) ? $line->volume : 0;
5028 ($volume == 0 && !empty($line->product->volume)) ? $volume = $line->product->volume : 0;
5029
5030 $weight_units = !empty($line->weight_units) ? $line->weight_units : 0;
5031 ($weight_units == 0 && !empty($line->product->weight_units)) ? $weight_units = $line->product->weight_units : 0;
5032 $volume_units = !empty($line->volume_units) ? $line->volume_units : 0;
5033 ($volume_units == 0 && !empty($line->product->volume_units)) ? $volume_units = $line->product->volume_units : 0;
5034
5035 $weightUnit = 0;
5036 $volumeUnit = 0;
5037 if (!empty($weight_units)) {
5038 $weightUnit = $weight_units;
5039 }
5040 if (!empty($volume_units)) {
5041 $volumeUnit = $volume_units;
5042 }
5043
5044 if (empty($totalWeight)) {
5045 $totalWeight = 0; // Avoid warning because $totalWeight is ''
5046 }
5047 if (empty($totalVolume)) {
5048 $totalVolume = 0; // Avoid warning because $totalVolume is ''
5049 }
5050
5051 //var_dump($line->volume_units);
5052 if ($weight_units < 50) { // < 50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
5053 $trueWeightUnit = pow(10, $weightUnit);
5054 $totalWeight += $weight * $qty * $trueWeightUnit;
5055 } else {
5056 if ($weight_units == 99) {
5057 // conversion 1 Pound = 0.45359237 KG
5058 $trueWeightUnit = 0.45359237;
5059 $totalWeight += $weight * $qty * $trueWeightUnit;
5060 } elseif ($weight_units == 98) {
5061 // conversion 1 Ounce = 0.0283495 KG
5062 $trueWeightUnit = 0.0283495;
5063 $totalWeight += $weight * $qty * $trueWeightUnit;
5064 } else {
5065 $totalWeight += $weight * $qty; // This may be wrong if we mix different units
5066 }
5067 }
5068 if ($volume_units < 50) { // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
5069 //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
5070 $trueVolumeUnit = pow(10, $volumeUnit);
5071 //print $line->volume;
5072 $totalVolume += $volume * $qty * $trueVolumeUnit;
5073 } else {
5074 $totalVolume += $volume * $qty; // This may be wrong if we mix different units
5075 }
5076 }
5077
5078 return array('weight' => $totalWeight, 'volume' => $totalVolume, 'ordered' => $totalOrdered, 'toship' => $totalToShip);
5079 }
5080
5081
5087 public function setExtraParameters()
5088 {
5089 $this->db->begin();
5090
5091 $extraparams = (!empty($this->extraparams) ? json_encode($this->extraparams) : null);
5092
5093 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
5094 $sql .= " SET extraparams = ".(!empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
5095 $sql .= " WHERE rowid = ".((int) $this->id);
5096
5097 dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
5098 $resql = $this->db->query($sql);
5099 if (!$resql) {
5100 $this->error = $this->db->lasterror();
5101 $this->db->rollback();
5102 return -1;
5103 } else {
5104 $this->db->commit();
5105 return 1;
5106 }
5107 }
5108
5109
5110 // --------------------
5111 // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
5112 // --------------------
5113
5114 /* This is to show add lines */
5115
5125 public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl')
5126 {
5127 global $conf, $user, $langs, $object, $hookmanager, $extrafields, $form;
5128
5129 // Line extrafield
5130 if (!is_object($extrafields)) {
5131 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5132 $extrafields = new ExtraFields($this->db);
5133 }
5134 $extrafields->fetch_name_optionals_label($this->table_element_line);
5135
5136 // Output template part (modules that overwrite templates must declare this into descriptor)
5137 // Use global variables + $dateSelector + $seller and $buyer
5138 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'.
5139 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5140 foreach ($dirtpls as $module => $reldir) {
5141 if (!empty($module)) {
5142 $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
5143 } else {
5144 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php';
5145 }
5146
5147 if (empty($conf->file->strict_mode)) {
5148 $res = @include $tpl;
5149 } else {
5150 $res = include $tpl; // for debug
5151 }
5152 if ($res) {
5153 break;
5154 }
5155 }
5156 }
5157
5158
5159
5160 /* This is to show array of line of details */
5161
5162
5177 public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl')
5178 {
5179 global $conf, $hookmanager, $langs, $user, $form, $extrafields, $object;
5180 // TODO We should not use global var for this
5181 global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
5182
5183 // Define $usemargins (used by objectline_xxx.tpl.php files)
5184 $usemargins = 0;
5185 if (isModEnabled('margin') && !empty($this->element) && in_array($this->element, array('facture', 'facturerec', 'propal', 'commande'))) {
5186 $usemargins = 1;
5187 }
5188
5189 $num = count($this->lines);
5190
5191 // Line extrafield
5192 if (!is_object($extrafields)) {
5193 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5194 $extrafields = new ExtraFields($this->db);
5195 }
5196 $extrafields->fetch_name_optionals_label($this->table_element_line);
5197
5198 $parameters = array('num' => $num, 'dateSelector' => $dateSelector, 'seller' => $seller, 'buyer' => $buyer, 'selected' => $selected, 'table_element_line' => $this->table_element_line);
5199 $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5200 if (empty($reshook)) {
5201 // Output template part (modules that overwrite templates must declare this into descriptor)
5202 // Use global variables + $dateSelector + $seller and $buyer
5203 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook.
5204 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5205 foreach ($dirtpls as $module => $reldir) {
5206 $res = 0;
5207 if (!empty($module)) {
5208 $tpl = dol_buildpath($reldir.'/objectline_title.tpl.php');
5209 } else {
5210 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php';
5211 }
5212 if (file_exists($tpl)) {
5213 if (empty($conf->file->strict_mode)) {
5214 $res = @include $tpl;
5215 } else {
5216 $res = include $tpl; // for debug
5217 }
5218 }
5219 if ($res) {
5220 break;
5221 }
5222 }
5223 }
5224
5225 $i = 0;
5226
5227 print "<!-- begin printObjectLines() --><tbody>\n";
5228 foreach ($this->lines as $line) {
5229 //Line extrafield
5230 $line->fetch_optionals();
5231
5232 //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line)))
5233 if (is_object($hookmanager)) { // Old code is commented on preceding line.
5234 if (empty($line->fk_parent_line)) {
5235 $parameters = array('line' => $line, 'num' => $num, 'i' => $i, 'dateSelector' => $dateSelector, 'seller' => $seller, 'buyer' => $buyer, 'selected' => $selected, 'table_element_line' => $line->table_element, 'defaulttpldir' => $defaulttpldir);
5236 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5237 } else {
5238 $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);
5239 $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5240 }
5241 }
5242 if (empty($reshook)) {
5243 $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafields, $defaulttpldir);
5244 }
5245
5246 $i++;
5247 }
5248 print "</tbody><!-- end printObjectLines() -->\n";
5249 }
5250
5268 public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/core/tpl')
5269 {
5270 global $conf, $langs, $user, $object, $hookmanager;
5271 global $form;
5272 global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
5273
5274 $object_rights = $this->getRights();
5275
5276 // var used into tpl
5277 $text = '';
5278 $description = '';
5279
5280 // Line in view mode
5281 if ($action != 'editline' || $selected != $line->id) {
5282 // Product
5283 if (!empty($line->fk_product) && $line->fk_product > 0) {
5284 $product_static = new Product($this->db);
5285 $product_static->fetch($line->fk_product);
5286
5287 $product_static->ref = $line->ref; //can change ref in hook
5288 $product_static->label = !empty($line->label) ? $line->label : ""; //can change label in hook
5289
5290 $text = $product_static->getNomUrl(1);
5291
5292 // Define output language and label
5293 if (getDolGlobalInt('MAIN_MULTILANGS')) {
5294 if (property_exists($this, 'socid') && !is_object($this->thirdparty)) {
5295 dol_print_error(null, 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
5296 return;
5297 }
5298
5299 $prod = new Product($this->db);
5300 $prod->fetch($line->fk_product);
5301
5302 $outputlangs = $langs;
5303 $newlang = '';
5304 if (empty($newlang) && GETPOST('lang_id', 'aZ09')) {
5305 $newlang = GETPOST('lang_id', 'aZ09');
5306 }
5307 if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE') && empty($newlang) && is_object($this->thirdparty)) {
5308 $newlang = $this->thirdparty->default_lang; // To use language of customer
5309 }
5310 if (!empty($newlang)) {
5311 $outputlangs = new Translate("", $conf);
5312 $outputlangs->setDefaultLang($newlang);
5313 }
5314
5315 $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
5316 } else {
5317 $label = $line->product_label;
5318 }
5319
5320 $text .= ' - '.(!empty($line->label) ? $line->label : $label);
5321 $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.
5322 }
5323
5324 $line->pu_ttc = price2num((!empty($line->subprice) ? $line->subprice : 0) * (1 + ((!empty($line->tva_tx) ? $line->tva_tx : 0) / 100)), 'MU');
5325
5326 // Output template part (modules that overwrite templates must declare this into descriptor)
5327 // Use global variables + $dateSelector + $seller and $buyer
5328 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5329 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5330 foreach ($dirtpls as $module => $reldir) {
5331 $res = 0;
5332 if (!empty($module)) {
5333 $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
5334 } else {
5335 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php';
5336 }
5337 //var_dump($tpl);
5338 if (file_exists($tpl)) {
5339 if (empty($conf->file->strict_mode)) {
5340 $res = @include $tpl;
5341 } else {
5342 $res = include $tpl; // for debug
5343 }
5344 }
5345 if ($res) {
5346 break;
5347 }
5348 }
5349 }
5350
5351 // Line in update mode
5352 if ($this->status == 0 && $action == 'editline' && $selected == $line->id) {
5353 $label = (!empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
5354
5355 $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU');
5356
5357 // Output template part (modules that overwrite templates must declare this into descriptor)
5358 // Use global variables + $dateSelector + $seller and $buyer
5359 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5360 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5361 foreach ($dirtpls as $module => $reldir) {
5362 if (!empty($module)) {
5363 $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
5364 } else {
5365 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php';
5366 }
5367
5368 if (empty($conf->file->strict_mode)) {
5369 $res = @include $tpl;
5370 } else {
5371 $res = include $tpl; // for debug
5372 }
5373 if ($res) {
5374 break;
5375 }
5376 }
5377 }
5378 }
5379
5380
5381 /* This is to show array of line of details of source object */
5382
5383
5394 public function printOriginLinesList($restrictlist = '', $selectedLines = array())
5395 {
5396 global $langs, $hookmanager, $form, $action;
5397
5398 print '<tr class="liste_titre">';
5399 print '<td class="linecolref">'.$langs->trans('Ref').'</td>';
5400 print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
5401 print '<td class="linecolvat right">'.$langs->trans('VATRate').'</td>';
5402 print '<td class="linecoluht right">'.$langs->trans('PriceUHT').'</td>';
5403 if (isModEnabled("multicurrency")) {
5404 print '<td class="linecoluht_currency right">'.$langs->trans('PriceUHTCurrency').'</td>';
5405 }
5406 print '<td class="linecolqty right">'.$langs->trans('Qty').'</td>';
5407 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5408 print '<td class="linecoluseunit left">'.$langs->trans('Unit').'</td>';
5409 }
5410 print '<td class="linecoldiscount right">'.$langs->trans('ReductionShort').'</td>';
5411 print '<td class="linecolht right">'.$langs->trans('TotalHT').'</td>';
5412 print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
5413 print '</tr>';
5414 $i = 0;
5415
5416 if (!empty($this->lines)) {
5417 foreach ($this->lines as $line) {
5418 $reshook = 0;
5419 //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line))) {
5420 if (is_object($hookmanager)) { // Old code is commented on preceding line.
5421 $parameters = array('line' => $line, 'i' => $i, 'restrictlist' => $restrictlist, 'selectedLines' => $selectedLines);
5422 if (!empty($line->fk_parent_line)) {
5423 $parameters['fk_parent_line'] = $line->fk_parent_line;
5424 }
5425 $reshook = $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5426 }
5427 if (empty($reshook)) {
5428 $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines);
5429 }
5430
5431 $i++;
5432 }
5433 }
5434 }
5435
5449 public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
5450 {
5451 global $langs, $conf;
5452
5453 //var_dump($line);
5454 if (!empty($line->date_start)) {
5455 $date_start = $line->date_start;
5456 } else {
5457 $date_start = $line->date_debut_prevue;
5458 if ($line->date_debut_reel) {
5459 $date_start = $line->date_debut_reel;
5460 }
5461 }
5462 if (!empty($line->date_end)) {
5463 $date_end = $line->date_end;
5464 } else {
5465 $date_end = $line->date_fin_prevue;
5466 if ($line->date_fin_reel) {
5467 $date_end = $line->date_fin_reel;
5468 }
5469 }
5470
5471 $this->tpl['id'] = $line->id;
5472
5473 $this->tpl['label'] = '';
5474 if (!empty($line->fk_parent_line)) {
5475 $this->tpl['label'] .= img_picto('', 'rightarrow');
5476 }
5477
5478 if (($line->info_bits & 2) == 2) { // TODO Not sure this is used for source object
5479 $discount = new DiscountAbsolute($this->db);
5480 if (property_exists($this, 'socid')) {
5481 $discount->fk_soc = $this->socid;
5482 $discount->socid = $this->socid;
5483 }
5484 $this->tpl['label'] .= $discount->getNomUrl(0, 'discount');
5485 } elseif (!empty($line->fk_product)) {
5486 $productstatic = new Product($this->db);
5487 $productstatic->id = $line->fk_product;
5488 $productstatic->ref = $line->ref;
5489 $productstatic->type = $line->fk_product_type;
5490 if (empty($productstatic->ref)) {
5491 $line->fetch_product();
5492 $productstatic = $line->product;
5493 }
5494
5495 $this->tpl['label'] .= $productstatic->getNomUrl(1);
5496 $this->tpl['label'] .= ' - '.(!empty($line->label) ? $line->label : $line->product_label);
5497 // Dates
5498 if ($line->product_type == 1 && ($date_start || $date_end)) {
5499 $this->tpl['label'] .= get_date_range($date_start, $date_end);
5500 }
5501 } else {
5502 $this->tpl['label'] .= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
5503 if (!empty($line->desc)) {
5504 $this->tpl['label'] .= $line->desc;
5505 } else {
5506 $this->tpl['label'] .= ($line->label ? '&nbsp;'.$line->label : '');
5507 }
5508
5509 // Dates
5510 if ($line->product_type == 1 && ($date_start || $date_end)) {
5511 $this->tpl['label'] .= get_date_range($date_start, $date_end);
5512 }
5513 }
5514
5515 if (!empty($line->desc)) {
5516 if ($line->desc == '(CREDIT_NOTE)') { // TODO Not sure this is used for source object
5517 $discount = new DiscountAbsolute($this->db);
5518 $discount->fetch($line->fk_remise_except);
5519 $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
5520 } elseif ($line->desc == '(DEPOSIT)') { // TODO Not sure this is used for source object
5521 $discount = new DiscountAbsolute($this->db);
5522 $discount->fetch($line->fk_remise_except);
5523 $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
5524 } elseif ($line->desc == '(EXCESS RECEIVED)') {
5525 $discount = new DiscountAbsolute($this->db);
5526 $discount->fetch($line->fk_remise_except);
5527 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
5528 } elseif ($line->desc == '(EXCESS PAID)') {
5529 $discount = new DiscountAbsolute($this->db);
5530 $discount->fetch($line->fk_remise_except);
5531 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
5532 } else {
5533 $this->tpl['description'] = dol_trunc($line->desc, 60);
5534 }
5535 } else {
5536 $this->tpl['description'] = '&nbsp;';
5537 }
5538
5539 // VAT Rate
5540 $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
5541 $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
5542 if (!empty($line->vat_src_code) && !preg_match('/\‍(/', $this->tpl['vat_rate'])) {
5543 $this->tpl['vat_rate'] .= ' ('.$line->vat_src_code.')';
5544 }
5545
5546 $this->tpl['price'] = price($line->subprice);
5547 $this->tpl['total_ht'] = price($line->total_ht);
5548 $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
5549 $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
5550 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5551 $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
5552 }
5553 $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
5554
5555 // Is the line strike or not
5556 $this->tpl['strike'] = 0;
5557 if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) {
5558 $this->tpl['strike'] = 1;
5559 }
5560
5561 // Output template part (modules that overwrite templates must declare this into descriptor)
5562 // Use global variables + $dateSelector + $seller and $buyer
5563 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5564 foreach ($dirtpls as $module => $reldir) {
5565 if (!empty($module)) {
5566 $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
5567 } else {
5568 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php';
5569 }
5570
5571 if (empty($conf->file->strict_mode)) {
5572 $res = @include $tpl;
5573 } else {
5574 $res = include $tpl; // for debug
5575 }
5576 if ($res) {
5577 break;
5578 }
5579 }
5580 }
5581
5582
5583 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5594 public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
5595 {
5596 // phpcs:enable
5597 $this->db->begin();
5598
5599 $sql = "INSERT INTO ".$this->db->prefix()."element_resources (";
5600 $sql .= "resource_id";
5601 $sql .= ", resource_type";
5602 $sql .= ", element_id";
5603 $sql .= ", element_type";
5604 $sql .= ", busy";
5605 $sql .= ", mandatory";
5606 $sql .= ") VALUES (";
5607 $sql .= ((int) $resource_id);
5608 $sql .= ", '".$this->db->escape($resource_type)."'";
5609 $sql .= ", '".$this->db->escape($this->id)."'";
5610 $sql .= ", '".$this->db->escape($this->element)."'";
5611 $sql .= ", '".$this->db->escape($busy)."'";
5612 $sql .= ", '".$this->db->escape($mandatory)."'";
5613 $sql .= ")";
5614
5615 dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
5616 if ($this->db->query($sql)) {
5617 $this->db->commit();
5618 return 1;
5619 } else {
5620 $this->error = $this->db->lasterror();
5621 $this->db->rollback();
5622 return 0;
5623 }
5624 }
5625
5626 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5635 public function delete_resource($rowid, $element, $notrigger = 0)
5636 {
5637 // phpcs:enable
5638 global $user;
5639
5640 $this->db->begin();
5641
5642 $sql = "DELETE FROM ".$this->db->prefix()."element_resources";
5643 $sql .= " WHERE rowid = ".((int) $rowid);
5644
5645 dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
5646
5647 $resql = $this->db->query($sql);
5648 if (!$resql) {
5649 $this->error = $this->db->lasterror();
5650 $this->db->rollback();
5651 return -1;
5652 } else {
5653 if (!$notrigger) {
5654 $result = $this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
5655 if ($result < 0) {
5656 $this->db->rollback();
5657 return -1;
5658 }
5659 }
5660 $this->db->commit();
5661 return 1;
5662 }
5663 }
5664
5665
5671 public function __clone()
5672 {
5673 // Force a copy of this->lines, otherwise it will point to same object.
5674 if (isset($this->lines) && is_array($this->lines)) {
5675 $nboflines = count($this->lines);
5676 for ($i = 0; $i < $nboflines; $i++) {
5677 if (is_object($this->lines[$i])) {
5678 $this->lines[$i] = clone $this->lines[$i];
5679 }
5680 }
5681 }
5682 }
5683
5697 protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
5698 {
5699 global $conf, $langs, $user, $hookmanager, $action;
5700
5701 $srctemplatepath = '';
5702
5703 $parameters = array('modelspath' => $modelspath, 'modele' => $modele, 'outputlangs' => $outputlangs, 'hidedetails' => $hidedetails, 'hidedesc' => $hidedesc, 'hideref' => $hideref, 'moreparams' => $moreparams);
5704 $reshook = $hookmanager->executeHooks('commonGenerateDocument', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5705
5706 if (!empty($reshook)) {
5707 return $reshook;
5708 }
5709
5710 dol_syslog("commonGenerateDocument modele=".$modele." outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
5711
5712 if (empty($modele)) {
5713 $this->error = 'BadValueForParameterModele';
5714 return -1;
5715 }
5716
5717 // Increase limit for PDF build
5718 $err = error_reporting();
5719 error_reporting(0);
5720 @set_time_limit(120);
5721 error_reporting($err);
5722
5723 // If selected model is a filename template (then $modele="modelname" or "modelname:filename")
5724 $tmp = explode(':', $modele, 2);
5725 $saved_model = $modele;
5726 if (!empty($tmp[1])) {
5727 $modele = $tmp[0];
5728 $srctemplatepath = $tmp[1];
5729 }
5730
5731 // Search template files
5732 $file = '';
5733 $classname = '';
5734 $filefound = '';
5735 $dirmodels = array('/');
5736 if (is_array($conf->modules_parts['models'])) {
5737 $dirmodels = array_merge($dirmodels, $conf->modules_parts['models']);
5738 }
5739 foreach ($dirmodels as $reldir) {
5740 foreach (array('doc', 'pdf') as $prefix) {
5741 if (in_array(get_class($this), array('Adherent'))) {
5742 // Member module use prefix_modele.class.php
5743 $file = $prefix."_".$modele.".class.php";
5744 } else {
5745 // Other module use prefix_modele.modules.php
5746 $file = $prefix."_".$modele.".modules.php";
5747 }
5748
5749 $file = dol_sanitizeFileName($file);
5750
5751 // We check if the file exists
5752 $file = dol_buildpath($reldir.$modelspath.$file, 0);
5753 if (file_exists($file)) {
5754 $filefound = $file;
5755 $classname = $prefix.'_'.$modele;
5756 break;
5757 }
5758 }
5759 if ($filefound) {
5760 break;
5761 }
5762 }
5763
5764 if ($filefound === '' || $classname === '') {
5765 $this->error = $langs->trans("Error").' Failed to load doc generator with modelpaths='.$modelspath.' - modele='.$modele;
5766 $this->errors[] = $this->error;
5767 dol_syslog($this->error, LOG_ERR);
5768 return -1;
5769 }
5770
5771 // Sanitize $filefound
5772 $filefound = dol_sanitizePathName($filefound);
5773
5774 // If generator was found
5775 global $db; // Required to solve a conception error making an include of some code that uses $db instead of $this->db just after.
5776
5777 require_once $filefound;
5778
5779 $obj = new $classname($this->db);
5780
5781 // If generator is ODT, we must have srctemplatepath defined, if not we set it.
5782 if ($obj->type == 'odt' && empty($srctemplatepath)) {
5783 $varfortemplatedir = $obj->scandir;
5784 if ($varfortemplatedir && getDolGlobalString($varfortemplatedir)) {
5785 $dirtoscan = getDolGlobalString($varfortemplatedir);
5786
5787 $listoffiles = array();
5788
5789 // Now we add first model found in directories scanned
5790 $listofdir = explode(',', $dirtoscan);
5791 foreach ($listofdir as $key => $tmpdir) {
5792 $tmpdir = trim($tmpdir);
5793 $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
5794 if (!$tmpdir) {
5795 unset($listofdir[$key]);
5796 continue;
5797 }
5798 if (is_dir($tmpdir)) {
5799 $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
5800 if (count($tmpfiles)) {
5801 $listoffiles = array_merge($listoffiles, $tmpfiles);
5802 }
5803 }
5804 }
5805
5806 if (count($listoffiles)) {
5807 foreach ($listoffiles as $record) {
5808 $srctemplatepath = $record['fullname'];
5809 break;
5810 }
5811 }
5812 }
5813
5814 if (empty($srctemplatepath)) {
5815 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
5816 return -1;
5817 }
5818 }
5819
5820 if ($obj->type == 'odt' && !empty($srctemplatepath)) {
5821 if (!dol_is_file($srctemplatepath)) {
5822 dol_syslog("Failed to locate template file ".$srctemplatepath, LOG_WARNING);
5823 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
5824 return -1;
5825 }
5826 }
5827
5828 // We save charset_output to restore it because write_file can change it if needed for
5829 // output format that does not support UTF8.
5830 $sav_charset_output = empty($outputlangs->charset_output) ? '' : $outputlangs->charset_output;
5831
5832 // update model_pdf in object
5833 $this->model_pdf = $saved_model;
5834
5835 if (in_array(get_class($this), array('Adherent'))) {
5836 '@phan-var-force Adherent $this';
5837 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, 'tmp_cards', $moreparams);
5838 } else {
5839 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
5840 }
5841 // After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
5842
5843 if ($resultwritefile > 0) {
5844 $outputlangs->charset_output = $sav_charset_output;
5845
5846 // We delete old preview
5847 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5848 dol_delete_preview($this);
5849
5850 // Index file in database
5851 if (!empty($obj->result['fullpath'])) {
5852 $destfull = $obj->result['fullpath'];
5853
5854 // Update the last_main_doc field into main object (if document generator has property ->update_main_doc_field set)
5855 $update_main_doc_field = 0;
5856 if (!empty($obj->update_main_doc_field)) {
5857 $update_main_doc_field = 1;
5858 }
5859
5860 // Check that the file exists, before indexing it.
5861 // Hint: It does not exist, if we create a PDF and auto delete the ODT File
5862 if (dol_is_file($destfull)) {
5863 $this->indexFile($destfull, $update_main_doc_field);
5864 }
5865 } else {
5866 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);
5867 }
5868
5869 // Success in building document. We build meta file.
5870 dol_meta_create($this);
5871
5872 return 1;
5873 } else {
5874 $outputlangs->charset_output = $sav_charset_output;
5875 $this->error = $obj->error;
5876 $this->errors = $obj->errors;
5877 dol_syslog("Error generating document for ".__CLASS__.". Error: ".$obj->error, LOG_ERR);
5878 return -1;
5879 }
5880 }
5881
5891 public function indexFile($destfull, $update_main_doc_field)
5892 {
5893 global $conf, $user;
5894
5895 $upload_dir = dirname($destfull);
5896 $destfile = basename($destfull);
5897 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
5898
5899 if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a tmp dir
5900 $filename = basename($destfile);
5901 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
5902 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
5903
5904 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
5905 $ecmfile = new EcmFiles($this->db);
5906 $result = $ecmfile->fetch(0, '', ($rel_dir ? $rel_dir.'/' : '').$filename);
5907
5908 // Set the public "share" key
5909 $setsharekey = false;
5910 if ($this->element == 'propal' || $this->element == 'proposal') {
5911 if (getDolGlobalInt("PROPOSAL_ALLOW_ONLINESIGN")) {
5912 $setsharekey = true; // feature to make online signature is not set or set to on (default)
5913 }
5914 if (getDolGlobalInt("PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD")) {
5915 $setsharekey = true;
5916 }
5917 }
5918 if ($this->element == 'commande' && getDolGlobalInt("ORDER_ALLOW_EXTERNAL_DOWNLOAD")) {
5919 $setsharekey = true;
5920 }
5921 if ($this->element == 'facture' && getDolGlobalInt("INVOICE_ALLOW_EXTERNAL_DOWNLOAD")) {
5922 $setsharekey = true;
5923 }
5924 if ($this->element == 'bank_account' && getDolGlobalInt("BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD")) {
5925 $setsharekey = true;
5926 }
5927 if ($this->element == 'product' && getDolGlobalInt("PRODUCT_ALLOW_EXTERNAL_DOWNLOAD")) {
5928 $setsharekey = true;
5929 }
5930 if ($this->element == 'contrat' && getDolGlobalInt("CONTRACT_ALLOW_EXTERNAL_DOWNLOAD")) {
5931 $setsharekey = true;
5932 }
5933 if ($this->element == 'fichinter' && getDolGlobalInt("FICHINTER_ALLOW_EXTERNAL_DOWNLOAD")) {
5934 $setsharekey = true;
5935 }
5936 if ($this->element == 'supplier_proposal' && getDolGlobalInt("SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD")) {
5937 $setsharekey = true;
5938 }
5939 if ($this->element == 'societe_rib' && getDolGlobalInt("SOCIETE_RIB_ALLOW_ONLINESIGN")) {
5940 $setsharekey = true;
5941 }
5942
5943 if ($setsharekey) {
5944 if (empty($ecmfile->share)) { // Because object not found or share not set yet
5945 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
5946 $ecmfile->share = getRandomPassword(true);
5947 }
5948 }
5949
5950 if ($result > 0) {
5951 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5952 $ecmfile->fullpath_orig = '';
5953 $ecmfile->gen_or_uploaded = 'generated';
5954 $ecmfile->description = ''; // indexed content
5955 $ecmfile->keywords = ''; // keyword content
5956 $result = $ecmfile->update($user);
5957 if ($result < 0) {
5958 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5959 return -1;
5960 }
5961 } else {
5962 $ecmfile->entity = $conf->entity;
5963 $ecmfile->filepath = $rel_dir;
5964 $ecmfile->filename = $filename;
5965 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5966 $ecmfile->fullpath_orig = '';
5967 $ecmfile->gen_or_uploaded = 'generated';
5968 $ecmfile->description = ''; // indexed content
5969 $ecmfile->keywords = ''; // keyword content
5970 $ecmfile->src_object_type = $this->table_element; // $this->table_name is 'myobject' or 'mymodule_myobject'.
5971 $ecmfile->src_object_id = $this->id;
5972
5973 $result = $ecmfile->create($user);
5974 if ($result < 0) {
5975 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5976 return -1;
5977 }
5978 }
5979
5980 /*$this->result['fullname']=$destfull;
5981 $this->result['filepath']=$ecmfile->filepath;
5982 $this->result['filename']=$ecmfile->filename;*/
5983 //var_dump($obj->update_main_doc_field);exit;
5984
5985 if ($update_main_doc_field && !empty($this->table_element)) {
5986 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath."/".$ecmfile->filename)."'";
5987 $sql .= " WHERE rowid = ".((int) $this->id);
5988
5989 $resql = $this->db->query($sql);
5990 if (!$resql) {
5991 dol_print_error($this->db);
5992 return -1;
5993 } else {
5994 $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
5995 }
5996 }
5997 }
5998
5999 return 1;
6000 }
6001
6009 public function addThumbs($file)
6010 {
6011 $file_osencoded = dol_osencode($file);
6012
6013 if (file_exists($file_osencoded)) {
6014 require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
6015
6016 $tmparraysize = getDefaultImageSizes();
6017 $maxwidthsmall = $tmparraysize['maxwidthsmall'];
6018 $maxheightsmall = $tmparraysize['maxheightsmall'];
6019 $maxwidthmini = $tmparraysize['maxwidthmini'];
6020 $maxheightmini = $tmparraysize['maxheightmini'];
6021 //$quality = $tmparraysize['quality'];
6022 $quality = 50; // For thumbs, we force quality to 50
6023
6024 // Create small thumbs for company (Ratio is near 16/9)
6025 // Used on logon for example
6026 vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
6027
6028 // Create mini thumbs for company (Ratio is near 16/9)
6029 // Used on menu or for setup page for example
6030 vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
6031 }
6032 }
6033
6041 public function delThumbs($file)
6042 {
6043 $imgThumbName = getImageFileNameForSize($file, '_small'); // Full path of thumb file
6044 dol_delete_file($imgThumbName);
6045 $imgThumbName = getImageFileNameForSize($file, '_mini'); // Full path of thumb file
6046 dol_delete_file($imgThumbName);
6047 }
6048
6049
6050 /* Functions common to commonobject and commonobjectline */
6051
6052 /* For default values */
6053
6067 public function getDefaultCreateValueFor($fieldname, $alternatevalue = null, $type = 'alphanohtml')
6068 {
6069 // If param here has been posted, we use this value first.
6070 if (GETPOSTISSET($fieldname)) {
6071 return GETPOST($fieldname, $type, 3);
6072 }
6073
6074 if (isset($alternatevalue)) {
6075 return $alternatevalue;
6076 }
6077
6078 $newelement = $this->element;
6079 if ($newelement == 'facture') {
6080 $newelement = 'invoice';
6081 }
6082 if ($newelement == 'commande') {
6083 $newelement = 'order';
6084 }
6085 if (empty($newelement)) {
6086 dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
6087 return '';
6088 }
6089
6090 $keyforfieldname = strtoupper($newelement.'_DEFAULT_'.$fieldname);
6091 //var_dump($keyforfieldname);
6092 if (getDolGlobalString($keyforfieldname)) {
6093 return getDolGlobalString($keyforfieldname);
6094 }
6095
6096 // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
6097 // store content into $conf->cache['overwrite_default']
6098
6099 return '';
6100 }
6101
6102
6103 /* For triggers */
6104
6105
6106 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6117 public function call_trigger($triggerName, $user)
6118 {
6119 // phpcs:enable
6120 global $langs, $conf;
6121
6122 if (!empty(self::TRIGGER_PREFIX) && strpos($triggerName, self::TRIGGER_PREFIX . '_') !== 0) {
6123 dol_print_error(null, 'The trigger "' . $triggerName . '" does not start with "' . self::TRIGGER_PREFIX . '_" as required.');
6124 exit;
6125 }
6126 if (!is_object($langs)) { // If lang was not defined, we set it. It is required by run_triggers().
6127 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
6128 $langs = new Translate('', $conf);
6129 }
6130
6131 include_once DOL_DOCUMENT_ROOT.'/core/class/interfaces.class.php';
6132 $interface = new Interfaces($this->db);
6133 $result = $interface->run_triggers($triggerName, $this, $user, $langs, $conf);
6134
6135 if ($result < 0) {
6136 if (!empty($this->errors)) {
6137 $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.
6138 } else {
6139 $this->errors = $interface->errors;
6140 }
6141 }
6142 return $result;
6143 }
6144
6145
6146 /* Functions for data in other language */
6147
6148
6157 {
6158 // To avoid SQL errors. Probably not the better solution though
6159 if (!$this->element) {
6160 return 0;
6161 }
6162 if (!($this->id > 0)) {
6163 return 0;
6164 }
6165 if (is_array($this->array_languages)) {
6166 return 1;
6167 }
6168
6169 $this->array_languages = array();
6170
6171 $element = $this->element;
6172 if ($element == 'categorie') {
6173 $element = 'categories'; // For compatibility
6174 }
6175
6176 // Request to get translation values for object
6177 $sql = "SELECT rowid, property, lang , value";
6178 $sql .= " FROM ".$this->db->prefix()."object_lang";
6179 $sql .= " WHERE type_object = '".$this->db->escape($element)."'";
6180 $sql .= " AND fk_object = ".((int) $this->id);
6181
6182 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6183 $resql = $this->db->query($sql);
6184 if ($resql) {
6185 $numrows = $this->db->num_rows($resql);
6186 if ($numrows) {
6187 $i = 0;
6188 while ($i < $numrows) {
6189 $obj = $this->db->fetch_object($resql);
6190 $key = $obj->property;
6191 $value = $obj->value;
6192 $codelang = $obj->lang;
6193 $type = $this->fields[$key]['type'];
6194
6195 // we can add this attribute to object
6196 if (preg_match('/date/', $type)) {
6197 $this->array_languages[$key][$codelang] = $this->db->jdate($value);
6198 } else {
6199 $this->array_languages[$key][$codelang] = $value;
6200 }
6201
6202 $i++;
6203 }
6204 }
6205
6206 $this->db->free($resql);
6207
6208 if ($numrows) {
6209 return $numrows;
6210 } else {
6211 return 0;
6212 }
6213 } else {
6214 dol_print_error($this->db);
6215 return -1;
6216 }
6217 }
6218
6225 public function setValuesForExtraLanguages($onlykey = '')
6226 {
6227 // Get extra fields
6228 foreach ($_POST as $postfieldkey => $postfieldvalue) {
6229 $tmparray = explode('-', $postfieldkey);
6230 if ($tmparray[0] != 'field') {
6231 continue;
6232 }
6233
6234 $element = $tmparray[1];
6235 $key = $tmparray[2];
6236 $codelang = $tmparray[3];
6237 //var_dump("postfieldkey=".$postfieldkey." element=".$element." key=".$key." codelang=".$codelang);
6238
6239 if (!empty($onlykey) && $key != $onlykey) {
6240 continue;
6241 }
6242 if ($element != $this->element) {
6243 continue;
6244 }
6245
6246 $key_type = $this->fields[$key]['type'];
6247
6248 $enabled = 1;
6249 if (isset($this->fields[$key]['enabled'])) {
6250 $enabled = (int) dol_eval($this->fields[$key]['enabled'], 1, 1, '1');
6251 }
6252 /*$perms = 1;
6253 if (isset($this->fields[$key]['perms']))
6254 {
6255 $perms = (int) dol_eval($this->fields[$key]['perms'], 1, 1, '1');
6256 }*/
6257 if (empty($enabled)) {
6258 continue;
6259 }
6260 //if (empty($perms)) continue;
6261
6262 if (in_array($key_type, array('date'))) {
6263 // Clean parameters
6264 // TODO GMT date in memory must be GMT so we should add gm=true in parameters
6265 $value_key = dol_mktime(0, 0, 0, GETPOSTINT($postfieldkey."month"), GETPOSTINT($postfieldkey."day"), GETPOSTINT($postfieldkey."year"));
6266 } elseif (in_array($key_type, array('datetime'))) {
6267 // Clean parameters
6268 // TODO GMT date in memory must be GMT so we should add gm=true in parameters
6269 $value_key = dol_mktime(GETPOSTINT($postfieldkey."hour"), GETPOSTINT($postfieldkey."min"), 0, GETPOSTINT($postfieldkey."month"), GETPOSTINT($postfieldkey."day"), GETPOSTINT($postfieldkey."year"));
6270 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
6271 $value_arr = GETPOST($postfieldkey, 'array'); // check if an array
6272 if (!empty($value_arr)) {
6273 $value_key = implode(',', $value_arr);
6274 } else {
6275 $value_key = '';
6276 }
6277 } elseif (in_array($key_type, array('price', 'double'))) {
6278 $value_arr = GETPOST($postfieldkey, 'alpha');
6279 $value_key = price2num($value_arr);
6280 } else {
6281 $value_key = GETPOST($postfieldkey);
6282 if (in_array($key_type, array('link')) && $value_key == '-1') {
6283 $value_key = '';
6284 }
6285 }
6286
6287 $this->array_languages[$key][$codelang] = $value_key;
6288
6289 /*if ($nofillrequired) {
6290 $langs->load('errors');
6291 setEventMessages($langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required), null, 'errors');
6292 return -1;
6293 }*/
6294 }
6295
6296 return 1;
6297 }
6298
6299
6300 /* Functions for extrafields */
6301
6308 public function fetchNoCompute($id)
6309 {
6310 global $conf;
6311
6312 $savDisableCompute = $conf->disable_compute;
6313 $conf->disable_compute = 1;
6314
6315 $ret = $this->fetch($id); /* @phpstan-ignore-line */
6316
6317 $conf->disable_compute = $savDisableCompute;
6318
6319 return $ret;
6320 }
6321
6322 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6332 public function fetch_optionals($rowid = null, $optionsArray = null)
6333 {
6334 // phpcs:enable
6335 global $conf, $extrafields;
6336
6337 if (empty($rowid)) {
6338 $rowid = $this->id;
6339 }
6340 if (empty($rowid) && isset($this->rowid)) {
6341 $rowid = $this->rowid; // deprecated
6342 }
6343
6344 // To avoid SQL errors. Probably not the better solution though
6345 if (!$this->table_element) {
6346 return 0;
6347 }
6348
6349 $this->array_options = array();
6350
6351 if (!is_array($optionsArray)) {
6352 // If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
6353 if (!isset($extrafields) || !is_object($extrafields)) {
6354 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6355 $extrafields = new ExtraFields($this->db);
6356 }
6357
6358 // Load array of extrafields for elementype = $this->table_element
6359 if (empty($extrafields->attributes[$this->table_element]['loaded'])) {
6360 $extrafields->fetch_name_optionals_label($this->table_element);
6361 }
6362 $optionsArray = (!empty($extrafields->attributes[$this->table_element]['label']) ? $extrafields->attributes[$this->table_element]['label'] : null);
6363 } else {
6364 global $extrafields;
6365 dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
6366 }
6367
6368 $table_element = $this->table_element;
6369 if ($table_element == 'categorie') {
6370 $table_element = 'categories'; // For compatibility
6371 }
6372
6373 // Request to get complementary values
6374 if (is_array($optionsArray) && count($optionsArray) > 0) {
6375 $sql = "SELECT rowid";
6376 foreach ($optionsArray as $name => $label) {
6377 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || (!in_array($extrafields->attributes[$this->table_element]['type'][$name], ['separate', 'point', 'multipts', 'linestrg','polygon']))) {
6378 $sql .= ", ".$name;
6379 }
6380 // use geo sql fonction to read as text
6381 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] == 'point') {
6382 $sql .= ", ST_AsWKT(".$name.") as ".$name;
6383 }
6384 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] == 'multipts') {
6385 $sql .= ", ST_AsWKT(".$name.") as ".$name;
6386 }
6387 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] == 'linestrg') {
6388 $sql .= ", ST_AsWKT(".$name.") as ".$name;
6389 }
6390 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] == 'polygon') {
6391 $sql .= ", ST_AsWKT(".$name.") as ".$name;
6392 }
6393 }
6394 $sql .= " FROM ".$this->db->prefix().$table_element."_extrafields";
6395 $sql .= " WHERE fk_object = ".((int) $rowid);
6396
6397 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6398 $resql = $this->db->query($sql);
6399 if ($resql) {
6400 $numrows = $this->db->num_rows($resql);
6401 if ($numrows) {
6402 $tab = $this->db->fetch_array($resql);
6403
6404 foreach ($tab as $key => $value) {
6405 // 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)
6406 if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && !is_int($key)) {
6407 // we can add this attribute to object
6408 if (!empty($extrafields->attributes[$this->table_element]) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date', 'datetime'))) {
6409 //var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
6410 $this->array_options["options_".$key] = $this->db->jdate($value);
6411 } else {
6412 $this->array_options["options_".$key] = $value;
6413 }
6414
6415 //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
6416 }
6417 if (!empty($extrafields->attributes[$this->table_element]['type'][$key]) && $extrafields->attributes[$this->table_element]['type'][$key] == 'password') {
6418 if (!empty($value) && preg_match('/^dolcrypt:/', $value)) {
6419 $this->array_options["options_".$key] = dolDecrypt($value);
6420 }
6421 }
6422 }
6423 } else {
6428 if (is_array($extrafields->attributes[$this->table_element]['label'])) {
6429 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
6430 $this->array_options['options_' . $key] = null;
6431 }
6432 }
6433 }
6434
6435 // If field is a computed field, value must become result of compute (regardless of whether a row exists
6436 // in the element's extrafields table)
6437 if (is_array($extrafields->attributes[$this->table_element]['label'])) {
6438 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
6439 if (!empty($extrafields->attributes[$this->table_element]) && !empty($extrafields->attributes[$this->table_element]['computed'][$key])) {
6440 //var_dump($conf->disable_compute);
6441 if (empty($conf->disable_compute)) {
6442 global $objectoffield; // We set a global variable to $objectoffield so
6443 $objectoffield = $this; // we can use it inside computed formula
6444 $this->array_options['options_' . $key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0, '2');
6445 }
6446 }
6447 }
6448 }
6449
6450 $this->db->free($resql);
6451
6452 if ($numrows) {
6453 return $numrows;
6454 } else {
6455 return 0;
6456 }
6457 } else {
6458 $this->errors[] = $this->db->lasterror;
6459 return -1;
6460 }
6461 }
6462 return 0;
6463 }
6464
6471 public function deleteExtraFields()
6472 {
6473 global $conf;
6474
6475 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6476 return 0;
6477 }
6478
6479 $this->db->begin();
6480
6481 $table_element = $this->table_element;
6482 if ($table_element == 'categorie') {
6483 $table_element = 'categories'; // For compatibility
6484 }
6485
6486 dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
6487
6488 $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6489
6490 $resql = $this->db->query($sql_del);
6491 if (!$resql) {
6492 $this->error = $this->db->lasterror();
6493 $this->db->rollback();
6494 return -1;
6495 } else {
6496 $this->db->commit();
6497 return 1;
6498 }
6499 }
6500
6511 public function insertExtraFields($trigger = '', $userused = null)
6512 {
6513 global $conf, $langs, $user;
6514
6515 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6516 return 0;
6517 }
6518
6519 if (empty($userused)) {
6520 $userused = $user;
6521 }
6522
6523 $error = 0;
6524
6525 if (!empty($this->array_options)) {
6526 // Check parameters
6527 $langs->load('admin');
6528 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6529 $extrafields = new ExtraFields($this->db);
6530 $target_extrafields = $extrafields->fetch_name_optionals_label($this->table_element);
6531
6532 // Eliminate copied source object extra fields that do not exist in target object
6533 $new_array_options = array();
6534 foreach ($this->array_options as $key => $value) {
6535 if (in_array(substr($key, 8), array_keys($target_extrafields))) { // We remove the 'options_' from $key for test
6536 $new_array_options[$key] = $value;
6537 } elseif (in_array($key, array_keys($target_extrafields))) { // We test on $key that does not contain the 'options_' prefix
6538 $new_array_options['options_'.$key] = $value;
6539 }
6540 }
6541
6542 foreach ($new_array_options as $key => $value) {
6543 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6544 $attributeType = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
6545 $attributeLabel = $langs->transnoentities($extrafields->attributes[$this->table_element]['label'][$attributeKey]);
6546 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
6547 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
6548 $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
6549 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$attributeKey];
6550
6551 // If we clone, we have to clean unique extrafields to prevent duplicates.
6552 // This behaviour can be prevented by external code by changing $this->context['createfromclone'] value in createFrom hook
6553 if (!empty($this->context['createfromclone']) && $this->context['createfromclone'] == 'createfromclone' && !empty($attributeUnique)) {
6554 $new_array_options[$key] = null;
6555 }
6556
6557 // Similar code than into insertExtraFields
6558 if ($attributeRequired) {
6559 $v = $this->array_options[$key];
6560 if (ExtraFields::isEmptyValue($v, $attributeType)) {
6561 $langs->load("errors");
6562 dol_syslog("Mandatory field '".$key."' is empty during create and set to required into definition of extrafields");
6563 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6564 return -1;
6565 }
6566 }
6567
6568 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6569 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6570
6571 if (!empty($attrfieldcomputed)) {
6572 if (getDolGlobalString('MAIN_STORE_COMPUTED_EXTRAFIELDS')) {
6573 $value = dol_eval($attrfieldcomputed, 1, 0, '2');
6574 dol_syslog($langs->trans("Extrafieldcomputed")." on ".$attributeLabel."(".$value.")", LOG_DEBUG);
6575 $new_array_options[$key] = $value;
6576 } else {
6577 $new_array_options[$key] = null;
6578 }
6579 }
6580
6581 switch ($attributeType) {
6582 case 'int':
6583 if (!is_numeric($value) && $value != '') {
6584 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6585 return -1;
6586 } elseif ($value == '') {
6587 $new_array_options[$key] = null;
6588 }
6589 break;
6590 case 'price':
6591 case 'double':
6592 $value = price2num($value);
6593 if (!is_numeric($value) && $value != '') {
6594 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." for ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6595 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6596 return -1;
6597 } elseif ($value == '') {
6598 $value = null;
6599 }
6600 //dol_syslog("double value"." on ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6601 $new_array_options[$key] = $value;
6602 break;
6603 /*case 'select': // Not required, we chose value='0' for undefined values
6604 if ($value=='-1')
6605 {
6606 $this->array_options[$key] = null;
6607 }
6608 break;*/
6609 case 'password':
6610 $algo = '';
6611 if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
6612 // If there is an encryption choice, we use it to encrypt data before insert
6613 $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
6614 $algo = reset($tmparrays);
6615 if ($algo != '') {
6616 //global $action; // $action may be 'create', 'update', 'update_extras'...
6617 //var_dump($action);
6618 //var_dump($this->oldcopy);exit;
6619 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
6620 //var_dump('algo='.$algo.' '.$this->oldcopy->array_options[$key].' -> '.$this->array_options[$key]);
6621 if (isset($this->oldcopy->array_options[$key]) && $this->array_options[$key] == $this->oldcopy->array_options[$key]) {
6622 // If old value encrypted in database is same than submitted new value, it means we don't change it, so we don't update.
6623 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
6624 if (!preg_match('/^dolcrypt:/', $this->array_options[$key])) {
6625 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6626 } else {
6627 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6628 }
6629 } else {
6630 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6631 }
6632 } else {
6633 // If value has changed
6634 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
6635 if (!preg_match('/^dolcrypt:/', $this->array_options[$key])) {
6636 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6637 } else {
6638 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6639 }
6640 } else {
6641 $new_array_options[$key] = dol_hash($this->array_options[$key], $algo);
6642 }
6643 }
6644 } else {
6645 //var_dump('jjj'.$algo.' '.$this->oldcopy->array_options[$key].' -> '.$this->array_options[$key]);
6646 // If this->oldcopy is not defined, we can't know if we change attribute or not, so we must keep value
6647 if ($algo == 'dolcrypt' && !preg_match('/^dolcrypt:/', $this->array_options[$key])) { // dolibarr reversible encryption
6648 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6649 } else {
6650 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6651 }
6652 }
6653 } else {
6654 // No encryption
6655 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6656 }
6657 } else { // Common usage
6658 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6659 }
6660 break;
6661 case 'date':
6662 case 'datetime':
6663 // If data is a string instead of a timestamp, we convert it
6664 if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6665 $this->array_options[$key] = strtotime($this->array_options[$key]);
6666 }
6667 $new_array_options[$key] = $this->db->idate($this->array_options[$key]);
6668 break;
6669 case 'datetimegmt':
6670 // If data is a string instead of a timestamp, we convert it
6671 if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6672 $this->array_options[$key] = strtotime($this->array_options[$key]);
6673 }
6674 $new_array_options[$key] = $this->db->idate($this->array_options[$key], 'gmt');
6675 break;
6676 case 'link':
6677 $param_list = array_keys($attributeParam['options']);
6678 // 0 : ObjectName
6679 // 1 : classPath
6680 $InfoFieldList = explode(":", $param_list[0]);
6681 dol_include_once($InfoFieldList[1]);
6682 if ($InfoFieldList[0] && class_exists($InfoFieldList[0])) {
6683 if ($value == '-1') { // -1 is key for no defined in combo list of objects
6684 $new_array_options[$key] = '';
6685 } elseif ($value) {
6686 $object = new $InfoFieldList[0]($this->db);
6687 if (is_numeric($value)) {
6688 $res = $object->fetch($value); // Common case
6689 } else {
6690 $res = $object->fetch('', $value); // For compatibility
6691 }
6692
6693 if ($res > 0) {
6694 $new_array_options[$key] = $object->id;
6695 } else {
6696 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
6697 return -1;
6698 }
6699 }
6700 } else {
6701 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6702 }
6703 break;
6704 case 'checkbox':
6705 case 'chkbxlst':
6706 if (is_array($this->array_options[$key])) {
6707 $new_array_options[$key] = implode(',', $this->array_options[$key]);
6708 } else {
6709 $new_array_options[$key] = $this->array_options[$key];
6710 }
6711 break;
6712 }
6713 }
6714
6715 $this->db->begin();
6716
6717 $table_element = $this->table_element;
6718 if ($table_element == 'categorie') {
6719 $table_element = 'categories'; // For compatibility
6720 }
6721
6722 dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG);
6723
6724 $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6725 $this->db->query($sql_del);
6726
6727 $sql = "INSERT INTO ".$this->db->prefix().$table_element."_extrafields (fk_object";
6728 foreach ($new_array_options as $key => $value) {
6729 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6730 // Add field of attribute
6731 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator
6732 $sql .= ",".$attributeKey;
6733 }
6734 }
6735 // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6736 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6737 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6738 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6739 $sql .= ",".$tmpkey;
6740 }
6741 }
6742 }
6743 $sql .= ") VALUES (".$this->id;
6744
6745 foreach ($new_array_options as $key => $value) {
6746 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6747 // Add field of attribute
6748 if (!in_array($extrafields->attributes[$this->table_element]['type'][$attributeKey], ['separate', 'point', 'multipts', 'linestrg', 'polygon'])) { // Only for other type than separator)
6749 if ($new_array_options[$key] != '' || $new_array_options[$key] == '0') {
6750 $sql .= ",'".$this->db->escape($new_array_options[$key])."'";
6751 } else {
6752 $sql .= ",null";
6753 }
6754 }
6755 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] == 'point') { // for point type
6756 if (!empty($new_array_options[$key])) {
6757 $sql .= ",ST_PointFromText('".$this->db->escape($new_array_options[$key])."')";
6758 } else {
6759 $sql .= ",null";
6760 }
6761 }
6762 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] == 'multipts') { // for point type
6763 if (!empty($new_array_options[$key])) {
6764 $sql .= ",ST_MultiPointFromText('".$this->db->escape($new_array_options[$key])."')";
6765 } else {
6766 $sql .= ",null";
6767 }
6768 }
6769 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] == 'linestrg') { // for linestring type
6770 if (!empty($new_array_options[$key])) {
6771 $sql .= ",ST_LineFromText('".$this->db->escape($new_array_options[$key])."')";
6772 } else {
6773 $sql .= ",null";
6774 }
6775 }
6776 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] == 'polygon') { // for polygon type
6777 if (!empty($new_array_options[$key])) {
6778 $sql .= ",ST_PolyFromText('".$this->db->escape($new_array_options[$key])."')";
6779 } else {
6780 $sql .= ",null";
6781 }
6782 }
6783 }
6784 // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6785 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6786 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6787 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6788 if (in_array($tmpval, array('int', 'double', 'price'))) {
6789 $sql .= ", 0";
6790 } else {
6791 $sql .= ", ''";
6792 }
6793 }
6794 }
6795 }
6796
6797 $sql .= ")";
6798
6799 $resql = $this->db->query($sql);
6800 if (!$resql) {
6801 $this->error = $this->db->lasterror();
6802 $error++;
6803 }
6804
6805 if (!$error && $trigger) {
6806 // Call trigger
6807 $this->context = array('extrafieldaddupdate' => 1);
6808 $result = $this->call_trigger($trigger, $userused);
6809 if ($result < 0) {
6810 $error++;
6811 }
6812 // End call trigger
6813 }
6814
6815 if ($error) {
6816 $this->db->rollback();
6817 return -1;
6818 } else {
6819 $this->db->commit();
6820 return 1;
6821 }
6822 } else {
6823 return 0;
6824 }
6825 }
6826
6837 public function insertExtraLanguages($trigger = '', $userused = null)
6838 {
6839 global $conf, $langs, $user;
6840
6841 if (empty($userused)) {
6842 $userused = $user;
6843 }
6844
6845 $error = 0;
6846
6847 if (getDolGlobalString('MAIN_EXTRALANGUAGES_DISABLED')) {
6848 return 0; // For avoid conflicts if trigger used
6849 }
6850
6851 if (is_array($this->array_languages)) {
6852 $new_array_languages = $this->array_languages;
6853
6854 foreach ($new_array_languages as $key => $value) {
6855 $attributeKey = $key;
6856 $attributeType = $this->fields[$attributeKey]['type'];
6857 $attributeLabel = $this->fields[$attributeKey]['label'];
6858
6859 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6860 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6861
6862 switch ($attributeType) {
6863 case 'int':
6864 if (is_array($value) || (!is_numeric($value) && $value != '')) {
6865 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6866 return -1;
6867 } elseif ($value == '') {
6868 $new_array_languages[$key] = null;
6869 }
6870 break;
6871 case 'double':
6872 $value = price2num($value);
6873 if (!is_numeric($value) && $value != '') {
6874 dol_syslog($langs->trans("ExtraLanguageHasWrongValue")." on ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6875 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6876 return -1;
6877 } elseif ($value == '') {
6878 $new_array_languages[$key] = null;
6879 } else {
6880 $new_array_languages[$key] = $value;
6881 }
6882 break;
6883 /*case 'select': // Not required, we chose value='0' for undefined values
6884 if ($value=='-1')
6885 {
6886 $this->array_options[$key] = null;
6887 }
6888 break;*/
6889 }
6890 }
6891
6892 $this->db->begin();
6893
6894 $table_element = $this->table_element;
6895 if ($table_element == 'categorie') { // TODO Rename table llx_categories_extrafields into llx_categorie_extrafields so we can remove this.
6896 $table_element = 'categories'; // For compatibility
6897 }
6898
6899 dol_syslog(get_class($this)."::insertExtraLanguages delete then insert", LOG_DEBUG);
6900
6901 foreach ($new_array_languages as $key => $langcodearray) { // $key = 'name', 'town', ...
6902 foreach ($langcodearray as $langcode => $value) {
6903 $sql_del = "DELETE FROM ".$this->db->prefix()."object_lang";
6904 $sql_del .= " WHERE fk_object = ".((int) $this->id)." AND property = '".$this->db->escape($key)."' AND type_object = '".$this->db->escape($table_element)."'";
6905 $sql_del .= " AND lang = '".$this->db->escape($langcode)."'";
6906 $this->db->query($sql_del);
6907
6908 if ($value !== '') {
6909 $sql = "INSERT INTO ".$this->db->prefix()."object_lang (fk_object, property, type_object, lang, value";
6910 $sql .= ") VALUES (".$this->id.", '".$this->db->escape($key)."', '".$this->db->escape($table_element)."', '".$this->db->escape($langcode)."', '".$this->db->escape($value)."'";
6911 $sql .= ")";
6912
6913 $resql = $this->db->query($sql);
6914 if (!$resql) {
6915 $this->error = $this->db->lasterror();
6916 $error++;
6917 break;
6918 }
6919 }
6920 }
6921 }
6922
6923 if (!$error && $trigger) {
6924 // Call trigger
6925 $this->context = array('extralanguagesaddupdate' => 1);
6926 $result = $this->call_trigger($trigger, $userused);
6927 if ($result < 0) {
6928 $error++;
6929 }
6930 // End call trigger
6931 }
6932
6933 if ($error) {
6934 $this->db->rollback();
6935 return -1;
6936 } else {
6937 $this->db->commit();
6938 return 1;
6939 }
6940 } else {
6941 return 0;
6942 }
6943 }
6944
6955 public function updateExtraField($key, $trigger = null, $userused = null)
6956 {
6957 global $conf, $langs, $user, $hookmanager;
6958
6959 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6960 return 0;
6961 }
6962
6963 if (empty($userused)) {
6964 $userused = $user;
6965 }
6966
6967 $error = 0;
6968
6969 if (!empty($this->array_options) && isset($this->array_options["options_".$key])) {
6970 // Check parameters
6971 $langs->load('admin');
6972 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6973 $extrafields = new ExtraFields($this->db);
6974 $extrafields->fetch_name_optionals_label($this->table_element);
6975
6976 $value = $this->array_options["options_".$key];
6977
6978 $attributeKey = $key;
6979 $attributeType = $extrafields->attributes[$this->table_element]['type'][$key];
6980 $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key];
6981 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key];
6982 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
6983 $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
6984 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$key];
6985
6986 // Similar code than into insertExtraFields
6987 if ($attributeRequired) {
6988 $mandatorypb = false;
6989 if ($attributeType == 'link' && $this->array_options["options_".$key] == '-1') {
6990 $mandatorypb = true;
6991 }
6992 if ($this->array_options["options_".$key] === '') {
6993 $mandatorypb = true;
6994 }
6995 if ($mandatorypb) {
6996 $langs->load("errors");
6997 dol_syslog("Mandatory field 'options_".$key."' is empty during update and set to required into definition of extrafields");
6998 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6999 return -1;
7000 }
7001 }
7002
7003 // $new_array_options will be used for direct update, so must contains formatted data for the UPDATE.
7004 $new_array_options = $this->array_options;
7005
7006 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
7007 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
7008 if (!empty($attrfieldcomputed)) {
7009 if (getDolGlobalString('MAIN_STORE_COMPUTED_EXTRAFIELDS')) {
7010 $value = dol_eval($attrfieldcomputed, 1, 0, '2');
7011 dol_syslog($langs->trans("Extrafieldcomputed")." on ".$attributeLabel."(".$value.")", LOG_DEBUG);
7012
7013 $new_array_options["options_".$key] = $value;
7014
7015 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7016 } else {
7017 $new_array_options["options_".$key] = null;
7018
7019 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7020 }
7021 }
7022
7023 switch ($attributeType) {
7024 case 'int':
7025 if (!is_numeric($value) && $value != '') {
7026 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
7027 return -1;
7028 } elseif ($value === '') {
7029 $new_array_options["options_".$key] = null;
7030
7031 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7032 }
7033 break;
7034 case 'price':
7035 case 'double':
7036 $value = price2num($value);
7037 if (!is_numeric($value) && $value != '') {
7038 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." on ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
7039 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
7040 return -1;
7041 } elseif ($value === '') {
7042 $value = null;
7043 }
7044 //dol_syslog("double value"." on ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
7045 $new_array_options["options_".$key] = $value;
7046
7047 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7048 break;
7049 /*case 'select': // Not required, we chose value='0' for undefined values
7050 if ($value=='-1')
7051 {
7052 $new_array_options["options_".$key] = $value;
7053
7054 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7055 }
7056 break;*/
7057 case 'password':
7058 $algo = '';
7059 if ($this->array_options["options_".$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
7060 // If there is an encryption choice, we use it to encrypt data before insert
7061 $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
7062 $algo = reset($tmparrays);
7063 if ($algo != '') {
7064 //global $action; // $action may be 'create', 'update', 'update_extras'...
7065 //var_dump($action);
7066 //var_dump($this->oldcopy);exit;
7067 //var_dump($key.' '.$this->array_options["options_".$key].' '.$algo);
7068 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
7069 //var_dump($this->oldcopy->array_options["options_".$key]); var_dump($this->array_options["options_".$key]);
7070 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.
7071 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
7072 if (!preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) {
7073 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]); // warning, must be called when on the master
7074 } else {
7075 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7076 }
7077 } else {
7078 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7079 }
7080 } else {
7081 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
7082 if (!preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) {
7083 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]);
7084 } else {
7085 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7086 }
7087 } else {
7088 $new_array_options["options_".$key] = dol_hash($this->array_options["options_".$key], $algo);
7089 }
7090 }
7091 } else {
7092 if ($algo == 'dolcrypt' && !preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) { // dolibarr reversible encryption
7093 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]); // warning, must be called when on the master
7094 } else {
7095 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7096 }
7097 }
7098 } else {
7099 // No encryption
7100 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7101 }
7102 } else { // Common usage
7103 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7104 }
7105
7106 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7107 break;
7108 case 'date':
7109 case 'datetime':
7110 if (empty($this->array_options["options_".$key])) {
7111 $new_array_options["options_".$key] = null;
7112
7113 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7114 } else {
7115 $new_array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key]);
7116 }
7117 break;
7118 case 'datetimegmt':
7119 if (empty($this->array_options["options_".$key])) {
7120 $new_array_options["options_".$key] = null;
7121
7122 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7123 } else {
7124 $new_array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key], 'gmt');
7125 }
7126 break;
7127 case 'boolean':
7128 if (empty($this->array_options["options_".$key])) {
7129 $new_array_options["options_".$key] = null;
7130
7131 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7132 }
7133 break;
7134 case 'link':
7135 if ($this->array_options["options_".$key] === '') {
7136 $new_array_options["options_".$key] = null;
7137
7138 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7139 }
7140 break;
7141 /*
7142 case 'link':
7143 $param_list = array_keys($attributeParam['options']);
7144 // 0 : ObjectName
7145 // 1 : classPath
7146 $InfoFieldList = explode(":", $param_list[0]);
7147 dol_include_once($InfoFieldList[1]);
7148 if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
7149 {
7150 if ($value == '-1') // -1 is key for no defined in combo list of objects
7151 {
7152 $new_array_options[$key] = '';
7153 } elseif ($value) {
7154 $object = new $InfoFieldList[0]($this->db);
7155 if (is_numeric($value)) $res = $object->fetch($value); // Common case
7156 else $res = $object->fetch('', $value); // For compatibility
7157
7158 if ($res > 0) $new_array_options[$key] = $object->id;
7159 else {
7160 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
7161 $this->db->rollback();
7162 return -1;
7163 }
7164 }
7165 } else {
7166 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
7167 }
7168 break;
7169 */
7170 case 'checkbox':
7171 case 'chkbxlst':
7172 $new_array_options = array();
7173 if (is_array($this->array_options["options_".$key])) {
7174 $new_array_options["options_".$key] = implode(',', $this->array_options["options_".$key]);
7175 } else {
7176 $new_array_options["options_".$key] = $this->array_options["options_".$key];
7177 }
7178
7179 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7180 break;
7181 }
7182
7183 $this->db->begin();
7184
7185 $linealreadyfound = 0;
7186
7187 // 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)
7188 $table_element = $this->table_element;
7189 if ($table_element == 'categorie') { // TODO Rename table llx_categories_extrafields into llx_categorie_extrafields so we can remove this.
7190 $table_element = 'categories'; // For compatibility
7191 }
7192
7193 $sql = "SELECT COUNT(rowid) as nb FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
7194 $resql = $this->db->query($sql);
7195 if ($resql) {
7196 $tmpobj = $this->db->fetch_object($resql);
7197 if ($tmpobj) {
7198 $linealreadyfound = $tmpobj->nb;
7199 }
7200 }
7201
7202 //var_dump('linealreadyfound='.$linealreadyfound.' sql='.$sql); exit;
7203 if ($linealreadyfound) {
7204 if ($this->array_options["options_".$key] === null) {
7205 $sql = "UPDATE ".$this->db->prefix().$this->table_element."_extrafields SET ".$key." = null";
7206 } else {
7207 $sql = "UPDATE ".$this->db->prefix().$this->table_element."_extrafields SET ".$key." = '".$this->db->escape($new_array_options["options_".$key])."'";
7208 }
7209 $sql .= " WHERE fk_object = ".((int) $this->id);
7210
7211 $resql = $this->db->query($sql);
7212 if (!$resql) {
7213 $error++;
7214 $this->error = $this->db->lasterror();
7215 }
7216 } else {
7217 $result = $this->insertExtraFields('', $user);
7218 if ($result < 0) {
7219 $error++;
7220 }
7221 }
7222
7223 if (!$error) {
7224 $parameters = array('key' => $key);
7225 global $action;
7226 $reshook = $hookmanager->executeHooks('updateExtraFieldBeforeCommit', $parameters, $this, $action);
7227 if ($reshook < 0) {
7228 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
7229 }
7230 }
7231
7232 if (!$error && $trigger) {
7233 // Call trigger
7234 $this->context = array('extrafieldupdate' => 1);
7235 $result = $this->call_trigger($trigger, $userused);
7236 if ($result < 0) {
7237 $error++;
7238 }
7239 // End call trigger
7240 }
7241
7242 if ($error) {
7243 dol_syslog(__METHOD__.$this->error, LOG_ERR);
7244 $this->db->rollback();
7245 return -1;
7246 } else {
7247 $this->db->commit();
7248 return 1;
7249 }
7250 } else {
7251 return 0;
7252 }
7253 }
7254
7261 public function getExtraField($key)
7262 {
7263 return $this->array_options['options_'.$key] ?? null;
7264 }
7265
7273 public function setExtraField($key, $value)
7274 {
7275 $this->array_options['options_'.$key] = $value;
7276 }
7277
7288 public function updateExtraLanguages($key, $trigger = null, $userused = null)
7289 {
7290 global $conf, $langs, $user;
7291
7292 if (empty($userused)) {
7293 $userused = $user;
7294 }
7295
7296 $error = 0;
7297
7298 if (getDolGlobalString('MAIN_EXTRALANGUAGES_DISABLED')) {
7299 return 0; // For avoid conflicts if trigger used
7300 }
7301
7302 return 0;
7303 }
7304
7305
7321 public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0, $nonewbutton = 0)
7322 {
7323 global $conf, $langs, $form;
7324
7325 if (!is_object($form)) {
7326 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
7327 $form = new Form($this->db);
7328 }
7329
7330 if (!empty($this->fields)) {
7331 $val = $this->fields[$key];
7332 }
7333
7334 // Validation tests and output
7335 $fieldValidationErrorMsg = '';
7336 $validationClass = '';
7337 $fieldValidationErrorMsg = $this->getFieldError($key);
7338 if (!empty($fieldValidationErrorMsg)) {
7339 $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
7340 } else {
7341 $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
7342 }
7343
7344 $valuemultiselectinput = array();
7345 $out = '';
7346 $type = '';
7347 $isDependList = 0;
7348 $param = array();
7349 $param['options'] = array();
7350 $reg = array();
7351 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7352 $size = !empty($this->fields[$key]['size']) ? $this->fields[$key]['size'] : 0;
7353 // Because we work on extrafields
7354 if (preg_match('/^(integer|link):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7355 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
7356 $type = 'link';
7357 } elseif (preg_match('/^(integer|link):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7358 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7359 $type = 'link';
7360 } elseif (preg_match('/^(integer|link):(.*):(.*)/i', $val['type'], $reg)) {
7361 $param['options'] = array($reg[2].':'.$reg[3] => 'N');
7362 $type = 'link';
7363 } elseif (preg_match('/^(sellist):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7364 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
7365 $type = 'sellist';
7366 } elseif (preg_match('/^(sellist):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7367 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7368 $type = 'sellist';
7369 } elseif (preg_match('/^(sellist):(.*):(.*)/i', $val['type'], $reg)) {
7370 $param['options'] = array($reg[2].':'.$reg[3] => 'N');
7371 $type = 'sellist';
7372 } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
7373 $param['options'] = array($reg[1] => 'N');
7374 $type = 'chkbxlst';
7375 } elseif (preg_match('/varchar\‍((\d+)\‍)/', $val['type'], $reg)) {
7376 $param['options'] = array();
7377 $type = 'varchar';
7378 $size = $reg[1];
7379 } elseif (preg_match('/varchar/', $val['type'])) {
7380 $param['options'] = array();
7381 $type = 'varchar';
7382 } else {
7383 $param['options'] = array();
7384 $type = $this->fields[$key]['type'];
7385 }
7386 //var_dump($type); var_dump($param['options']);
7387
7388 // Special case that force options and type ($type can be integer, varchar, ...)
7389 if (!empty($this->fields[$key]['arrayofkeyval']) && is_array($this->fields[$key]['arrayofkeyval'])) {
7390 $param['options'] = $this->fields[$key]['arrayofkeyval'];
7391 // Special case that prevent to force $type to have multiple input
7392 if (empty($this->fields[$key]['multiinput'])) {
7393 $type = (($this->fields[$key]['type'] == 'checkbox') ? $this->fields[$key]['type'] : 'select');
7394 }
7395 }
7396
7397 $label = $this->fields[$key]['label'];
7398 //$elementtype=$this->fields[$key]['elementtype']; // Seems not used
7399 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7400 $default = (!empty($this->fields[$key]['default']) ? $this->fields[$key]['default'] : '');
7401 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7402 $computed = (!empty($this->fields[$key]['computed']) ? $this->fields[$key]['computed'] : '');
7403 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7404 $unique = (!empty($this->fields[$key]['unique']) ? $this->fields[$key]['unique'] : 0);
7405 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7406 $required = (!empty($this->fields[$key]['required']) ? $this->fields[$key]['required'] : 0);
7407 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7408 $autofocusoncreate = (!empty($this->fields[$key]['autofocusoncreate']) ? $this->fields[$key]['autofocusoncreate'] : 0);
7409
7410 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7411 $langfile = (!empty($this->fields[$key]['langfile']) ? $this->fields[$key]['langfile'] : '');
7412 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7413 $list = (!empty($this->fields[$key]['list']) ? $this->fields[$key]['list'] : 0);
7414 $hidden = (in_array(abs($this->fields[$key]['visible']), array(0, 2)) ? 1 : 0);
7415
7416 $objectid = $this->id;
7417
7418 if ($computed) {
7419 if (!preg_match('/^search_/', $keyprefix)) {
7420 return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
7421 } else {
7422 return '';
7423 }
7424 }
7425
7426 // Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine
7427 if (empty($morecss) && !empty($val['css'])) {
7428 $morecss = $val['css'];
7429 } elseif (empty($morecss)) {
7430 if ($type == 'date') {
7431 $morecss = 'minwidth100imp';
7432 } elseif ($type == 'datetime' || $type == 'link') { // link means an foreign key to another primary id
7433 $morecss = 'minwidth200imp';
7434 } elseif (in_array($type, array('int', 'integer', 'price')) || preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', (string) $type)) {
7435 $morecss = 'maxwidth75';
7436 } elseif ($type == 'url') {
7437 $morecss = 'minwidth400';
7438 } elseif ($type == 'boolean') {
7439 $morecss = '';
7440 } else {
7441 if (is_numeric($size) && round((float) $size) < 12) {
7442 $morecss = 'minwidth100';
7443 } elseif (is_numeric($size) && round((float) $size) <= 48) {
7444 $morecss = 'minwidth200';
7445 } else {
7446 $morecss = 'minwidth400';
7447 }
7448 }
7449 }
7450
7451 // Add validation state class
7452 if (!empty($validationClass)) {
7453 $morecss .= $validationClass;
7454 }
7455
7456 if (in_array($type, array('date'))) {
7457 $tmp = explode(',', $size);
7458 $newsize = $tmp[0];
7459 $showtime = 0;
7460
7461 // Do not show current date when field not required (see selectDate() method)
7462 if (!$required && $value == '') {
7463 $value = '-1';
7464 }
7465
7466 // TODO Must also support $moreparam
7467 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
7468 } elseif (in_array($type, array('datetime'))) {
7469 $tmp = explode(',', $size);
7470 $newsize = $tmp[0];
7471 $showtime = 1;
7472
7473 // Do not show current date when field not required (see selectDate() method)
7474 if (!$required && $value == '') {
7475 $value = '-1';
7476 }
7477
7478 // TODO Must also support $moreparam
7479 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
7480 } elseif (in_array($type, array('duration'))) {
7481 $out = $form->select_duration($keyprefix.$key.$keysuffix, $value, 0, 'text', 0, 1);
7482 } elseif (in_array($type, array('int', 'integer'))) {
7483 $tmp = explode(',', $size);
7484 $newsize = $tmp[0];
7485 $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' : '').'>';
7486 } elseif (in_array($type, array('real'))) {
7487 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7488 } elseif (preg_match('/varchar/', (string) $type)) {
7489 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"'.($size > 0 ? ' maxlength="'.$size.'"' : '').' value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7490 } elseif (in_array($type, array('email', 'mail', 'phone', 'url', 'ip'))) {
7491 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7492 } elseif (preg_match('/^text/', (string) $type)) {
7493 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7494 if (!empty($param['options'])) {
7495 // If the textarea field has a list of arrayofkeyval into its definition, we suggest a combo with possible values to fill the textarea.
7496 //var_dump($param['options']);
7497 $out .= $form->selectarray($keyprefix.$key.$keysuffix."_multiinput", $param['options'], '', 1, 0, 0, "flat maxwidthonphone".$morecss);
7498 $out .= "<script>";
7499 $out .= '
7500 function handlemultiinputdisabling(htmlname){
7501 console.log("We handle the disabling of used options for "+htmlname+"_multiinput");
7502 multiinput = $("#"+htmlname+"_multiinput");
7503 multiinput.find("option").each(function(){
7504 tmpval = $("#"+htmlname).val();
7505 tmpvalarray = tmpval.split(",");
7506 valtotest = $(this).val();
7507 if(tmpvalarray.includes(valtotest)){
7508 $(this).prop("disabled",true);
7509 } else {
7510 if($(this).prop("disabled") == true){
7511 console.log(valtotest)
7512 $(this).prop("disabled", false);
7513 }
7514 }
7515 });
7516 }
7517
7518 $(document).ready(function () {
7519 $("#'.$keyprefix.$key.$keysuffix.'_multiinput").on("change",function() {
7520 console.log("We add the selected value to the text area '.$keyprefix.$key.$keysuffix.'");
7521 tmpval = $("#'.$keyprefix.$key.$keysuffix.'").val();
7522 tmpvalarray = tmpval.split(",");
7523 valtotest = $(this).val();
7524 if(valtotest != -1 && !tmpvalarray.includes(valtotest)){
7525 if(tmpval == ""){
7526 tmpval = valtotest;
7527 } else {
7528 tmpval = tmpval + "," + valtotest;
7529 }
7530 $("#'.$keyprefix.$key.$keysuffix.'").val(tmpval);
7531 handlemultiinputdisabling("'.$keyprefix.$key.$keysuffix.'");
7532 }
7533 });
7534 $("#'.$keyprefix.$key.$keysuffix.'").on("change",function(){
7535 handlemultiinputdisabling($(this).attr("id"));
7536 });
7537 handlemultiinputdisabling("'.$keyprefix.$key.$keysuffix.'");
7538 })';
7539 $out .= "</script>";
7540 }
7541 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7542 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
7543 $out .= (string) $doleditor->Create(1, '', true, '', '', '', $morecss);
7544 } else {
7545 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7546 }
7547 } elseif (preg_match('/^html/', (string) $type)) {
7548 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7549 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7550 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && getDolGlobalInt('FCKEDITOR_ENABLE_SOCIETE'), ROWS_5, '90%');
7551 $out = (string) $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
7552 } else {
7553 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7554 }
7555 } elseif ($type == 'boolean') {
7556 $checked = '';
7557 if (!empty($value)) {
7558 $checked = ' checked value="1" ';
7559 } else {
7560 $checked = ' value="1" ';
7561 }
7562 $out = '<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
7563 } elseif ($type == 'price') {
7564 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7565 $value = price($value);
7566 }
7567 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '.$langs->getCurrencySymbol($conf->currency);
7568 } elseif (preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', (string) $type)) {
7569 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7570 $value = price($value);
7571 }
7572 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
7573 } elseif ($type == 'select') { // combo list
7574 $out = '';
7575 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
7576 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7577 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7578 }
7579
7580 $tmpselect = '';
7581 $nbchoice = 0;
7582 foreach ($param['options'] as $keyb => $valb) {
7583 if ((string) $keyb == '') {
7584 continue;
7585 }
7586 if (strpos($valb, "|") !== false) {
7587 list($valb, $parent) = explode('|', $valb);
7588 }
7589 $nbchoice++;
7590 $tmpselect .= '<option value="'.$keyb.'"';
7591 $tmpselect .= (((string) $value == (string) $keyb) ? ' selected' : '');
7592 if (!empty($parent)) {
7593 $isDependList = 1;
7594 }
7595 $tmpselect .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
7596 $tmpselect .= '>'.$langs->trans($valb).'</option>';
7597 }
7598
7599 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7600 if ((!isset($this->fields[$key]['default'])) || ($this->fields[$key]['notnull'] != 1) || $nbchoice >= 2) {
7601 $out .= '<option value="0">&nbsp;</option>';
7602 }
7603 $out .= $tmpselect;
7604 $out .= '</select>';
7605 } elseif ($type == 'sellist') {
7606 $out = '';
7607 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
7608 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7609 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7610 }
7611
7612 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7613 if (is_array($param['options'])) {
7614 $param_list = array_keys($param['options']);
7615 $InfoFieldList = explode(":", $param_list[0], 5);
7616 if (! empty($InfoFieldList[4])) {
7617 $pos = 0;
7618 $parenthesisopen = 0;
7619 while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
7620 if (substr($InfoFieldList[4], $pos, 1) == '(') {
7621 $parenthesisopen++;
7622 }
7623 if (substr($InfoFieldList[4], $pos, 1) == ')') {
7624 $parenthesisopen--;
7625 }
7626 $pos++;
7627 }
7628 $tmpbefore = substr($InfoFieldList[4], 0, $pos);
7629 $tmpafter = substr($InfoFieldList[4], $pos + 1);
7630 //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
7631 $InfoFieldList[4] = $tmpbefore;
7632 if ($tmpafter !== '') {
7633 $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
7634 }
7635 //var_dump($InfoFieldList);
7636 }
7637 $parentName = '';
7638 $parentField = '';
7639
7640 // 0 : tableName
7641 // 1 : label field name
7642 // 2 : key fields name (if differ of rowid)
7643 // 3 : key field parent (for dependent lists)
7644 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
7645 // 5 : id category type
7646 // 6 : ids categories list separated by comma for category root
7647 // 7 : sort field
7648 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7649
7650 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7651 if (strpos($InfoFieldList[4], 'extra.') !== false) {
7652 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7653 } else {
7654 $keyList = $InfoFieldList[2].' as rowid';
7655 }
7656 }
7657 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7658 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7659 $keyList .= ', '.$parentField;
7660 }
7661
7662 $filter_categorie = false;
7663 if (count($InfoFieldList) > 5) {
7664 if ($InfoFieldList[0] == 'categorie') {
7665 $filter_categorie = true;
7666 }
7667 }
7668
7669 if (!$filter_categorie) {
7670 $fields_label = explode('|', $InfoFieldList[1]);
7671 if (is_array($fields_label)) {
7672 $keyList .= ', ';
7673 $keyList .= implode(', ', $fields_label);
7674 }
7675
7676 $sqlwhere = '';
7677 $sql = "SELECT " . $keyList;
7678 $sql .= " FROM " . $this->db->prefix() . $InfoFieldList[0];
7679 if (!empty($InfoFieldList[4])) {
7680 // can use SELECT request
7681 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7682 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7683 }
7684
7685 // current object id can be use into filter
7686 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7687 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
7688 } else {
7689 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7690 }
7691
7692 // We have to join on extrafield table
7693 $errstr = '';
7694 if (strpos($InfoFieldList[4], 'extra') !== false) {
7695 $sql .= " as main, " . $this->db->prefix() . $InfoFieldList[0] . "_extrafields as extra";
7696 $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2];
7697 $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
7698 } else {
7699 $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
7700 }
7701 } else {
7702 $sqlwhere .= ' WHERE 1=1';
7703 }
7704 // Some tables may have field, some other not. For the moment we disable it.
7705 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7706 $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7707 }
7708 $sql .= $sqlwhere;
7709 //print $sql;
7710
7711 // Note: $InfoFieldList can be 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter[:CategoryIdType[:CategoryIdList[:Sortfield]]]]]]'
7712 if (isset($InfoFieldList[7]) && preg_match('/^[a-z0-9_\-,]+$/i', $InfoFieldList[7])) {
7713 $sql .= " ORDER BY ".$this->db->escape($InfoFieldList[7]);
7714 } else {
7715 $sql .= " ORDER BY ".$this->db->sanitize(implode(', ', $fields_label));
7716 }
7717
7718 dol_syslog(get_class($this) . '::showInputField type=sellist', LOG_DEBUG);
7719 $resql = $this->db->query($sql);
7720 if ($resql) {
7721 $out .= '<option value="0">&nbsp;</option>';
7722 $num = $this->db->num_rows($resql);
7723 $i = 0;
7724 while ($i < $num) {
7725 $labeltoshow = '';
7726 $obj = $this->db->fetch_object($resql);
7727
7728 // Several field into label (eq table:code|libelle:rowid)
7729 $notrans = false;
7730 $fields_label = explode('|', $InfoFieldList[1]);
7731 if (count($fields_label) > 1) {
7732 $notrans = true;
7733 foreach ($fields_label as $field_toshow) {
7734 $labeltoshow .= $obj->$field_toshow . ' ';
7735 }
7736 } else {
7737 $labeltoshow = $obj->{$InfoFieldList[1]};
7738 }
7739 $labeltoshow = dol_trunc($labeltoshow, 45);
7740
7741 if ($value == $obj->rowid) {
7742 foreach ($fields_label as $field_toshow) {
7743 $translabel = $langs->trans($obj->$field_toshow);
7744 if ($translabel != $obj->$field_toshow) {
7745 $labeltoshow = dol_trunc($translabel) . ' ';
7746 } else {
7747 $labeltoshow = dol_trunc($obj->$field_toshow) . ' ';
7748 }
7749 }
7750 $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7751 } else {
7752 if (!$notrans) {
7753 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7754 if ($translabel != $obj->{$InfoFieldList[1]}) {
7755 $labeltoshow = dol_trunc($translabel, 18);
7756 } else {
7757 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]});
7758 }
7759 }
7760 if (empty($labeltoshow)) {
7761 $labeltoshow = '(not defined)';
7762 }
7763 if ($value == $obj->rowid) {
7764 $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7765 }
7766
7767 if (!empty($InfoFieldList[3]) && $parentField) {
7768 $parent = $parentName . ':' . $obj->{$parentField};
7769 $isDependList = 1;
7770 }
7771
7772 $out .= '<option value="' . $obj->rowid . '"';
7773 $out .= ($value == $obj->rowid ? ' selected' : '');
7774 $out .= (!empty($parent) ? ' parent="' . $parent . '"' : '');
7775 $out .= '>' . $labeltoshow . '</option>';
7776 }
7777
7778 $i++;
7779 }
7780 $this->db->free($resql);
7781 } else {
7782 print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
7783 }
7784 } else {
7785 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7786 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
7787 $out .= '<option value="0">&nbsp;</option>';
7788 foreach ($data as $data_key => $data_value) {
7789 $out .= '<option value="' . $data_key . '"';
7790 $out .= ($value == $data_key ? ' selected' : '');
7791 $out .= '>' . $data_value . '</option>';
7792 }
7793 }
7794 }
7795 $out .= '</select>';
7796 } elseif ($type == 'checkbox') {
7797 $value_arr = explode(',', $value);
7798 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ? null : $param['options']), $value_arr, 0, 0, $morecss, 0, '100%');
7799 } elseif ($type == 'radio') {
7800 $out = '';
7801 foreach ($param['options'] as $keyopt => $valopt) {
7802 $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
7803 $out .= ' value="'.$keyopt.'"';
7804 $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
7805 $out .= ($value == $keyopt ? 'checked' : '');
7806 $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$valopt.'</label><br>';
7807 }
7808 } elseif ($type == 'chkbxlst') {
7809 if (is_array($value)) {
7810 $value_arr = $value;
7811 } else {
7812 $value_arr = explode(',', $value);
7813 }
7814
7815 if (is_array($param['options'])) {
7816 $param_list = array_keys($param['options']);
7817 $InfoFieldList = explode(":", $param_list[0]);
7818 $parentName = '';
7819 $parentField = '';
7820 // 0 : tableName
7821 // 1 : label field name
7822 // 2 : key fields name (if differ of rowid)
7823 // 3 : key field parent (for dependent lists)
7824 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
7825 // 5 : id category type
7826 // 6 : ids categories list separated by comma for category root
7827 '@phan-var-force array{0:string,1:string,2:string,3:string,3:string,5:string,6:string} $InfoFieldList';
7828
7829 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7830
7831 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7832 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7833 $keyList .= ', '.$parentField;
7834 }
7835 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7836 if (strpos($InfoFieldList[4], 'extra.') !== false) {
7837 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7838 } else {
7839 $keyList = $InfoFieldList[2].' as rowid';
7840 }
7841 }
7842
7843 $filter_categorie = false;
7844 if (count($InfoFieldList) > 5) {
7845 if ($InfoFieldList[0] == 'categorie') {
7846 $filter_categorie = true;
7847 }
7848 }
7849
7850 if (!$filter_categorie) {
7851 $fields_label = explode('|', $InfoFieldList[1]);
7852 if (is_array($fields_label)) {
7853 $keyList .= ', ';
7854 $keyList .= implode(', ', $fields_label);
7855 }
7856
7857 $sqlwhere = '';
7858 $sql = "SELECT " . $keyList;
7859 $sql .= ' FROM ' . $this->db->prefix() . $InfoFieldList[0];
7860 if (!empty($InfoFieldList[4])) {
7861 // can use SELECT request
7862 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7863 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7864 }
7865
7866 // current object id can be use into filter
7867 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7868 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
7869 } else {
7870 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7871 }
7872
7873 // We have to join on extrafield table
7874 if (strpos($InfoFieldList[4], 'extra') !== false) {
7875 $sql .= ' as main, ' . $this->db->prefix() . $InfoFieldList[0] . '_extrafields as extra';
7876 $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2] . " AND " . $InfoFieldList[4];
7877 } else {
7878 $sqlwhere .= " WHERE " . $InfoFieldList[4];
7879 }
7880 } else {
7881 $sqlwhere .= ' WHERE 1=1';
7882 }
7883 // Some tables may have field, some other not. For the moment we disable it.
7884 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7885 $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7886 }
7887 // $sql.=preg_replace('/^ AND /','',$sqlwhere);
7888 // print $sql;
7889
7890 $sql .= $sqlwhere;
7891 dol_syslog(get_class($this) . '::showInputField type=chkbxlst', LOG_DEBUG);
7892 $resql = $this->db->query($sql);
7893 if ($resql) {
7894 $num = $this->db->num_rows($resql);
7895 $i = 0;
7896
7897 $data = array();
7898
7899 while ($i < $num) {
7900 $labeltoshow = '';
7901 $obj = $this->db->fetch_object($resql);
7902
7903 $notrans = false;
7904 // Several field into label (eq table:code|libelle:rowid)
7905 $fields_label = explode('|', $InfoFieldList[1]);
7906 if (count($fields_label) > 1) {
7907 $notrans = true;
7908 foreach ($fields_label as $field_toshow) {
7909 $labeltoshow .= $obj->$field_toshow . ' ';
7910 }
7911 } else {
7912 $labeltoshow = $obj->{$InfoFieldList[1]};
7913 }
7914 $labeltoshow = dol_trunc($labeltoshow, 45);
7915
7916 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7917 foreach ($fields_label as $field_toshow) {
7918 $translabel = $langs->trans($obj->$field_toshow);
7919 if ($translabel != $obj->$field_toshow) {
7920 $labeltoshow = dol_trunc($translabel, 18) . ' ';
7921 } else {
7922 $labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
7923 }
7924 }
7925
7926 $data[$obj->rowid] = $labeltoshow;
7927 } else {
7928 if (!$notrans) {
7929 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7930 if ($translabel != $obj->{$InfoFieldList[1]}) {
7931 $labeltoshow = dol_trunc($translabel, 18);
7932 } else {
7933 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
7934 }
7935 }
7936 if (empty($labeltoshow)) {
7937 $labeltoshow = '(not defined)';
7938 }
7939
7940 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7941 $data[$obj->rowid] = $labeltoshow;
7942 }
7943
7944 if (!empty($InfoFieldList[3]) && $parentField) {
7945 $parent = $parentName . ':' . $obj->{$parentField};
7946 $isDependList = 1;
7947 }
7948
7949 $data[$obj->rowid] = $labeltoshow;
7950 }
7951
7952 $i++;
7953 }
7954 $this->db->free($resql);
7955
7956 $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, 0, 0, $morecss, 0, '100%');
7957 } else {
7958 print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
7959 }
7960 } else {
7961 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7962 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
7963 $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, 0, 0, $morecss, 0, '100%');
7964 }
7965 }
7966 } elseif ($type == 'link') {
7967 // $param_list='ObjectName:classPath[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
7968 // Filter can contains some ':' inside.
7969 $param_list = array_keys($param['options']);
7970 $param_list_array = explode(':', $param_list[0], 4);
7971
7972 $showempty = (($required && $default != '') ? 0 : 1);
7973
7974 if (!preg_match('/search_/', $keyprefix)) {
7975 if (!empty($param_list_array[2])) { // If the entry into $fields is set to add a create button
7976 if (!empty($this->fields[$key]['picto'])) {
7977 $morecss .= ' widthcentpercentminusxx';
7978 } else {
7979 $morecss .= ' widthcentpercentminusx';
7980 }
7981 } else {
7982 if (!empty($this->fields[$key]['picto'])) {
7983 $morecss .= ' widthcentpercentminusx';
7984 }
7985 }
7986 }
7987 $objectfield = $this->element.($this->module ? '@'.$this->module : '').':'.$key.$keysuffix;
7988 $out = $form->selectForForms($param_list_array[0], $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, $moreparam, 0, (empty($val['disabled']) ? 0 : 1), '', $objectfield);
7989
7990 if (!empty($param_list_array[2])) { // If the entry into $fields is set, we must add a create button
7991 if ((!GETPOSTISSET('backtopage') || strpos(GETPOST('backtopage'), $_SERVER['PHP_SELF']) === 0) // // To avoid to open several times the 'Plus' button (we accept only one level)
7992 && empty($val['disabled']) && empty($nonewbutton)) { // and to avoid to show the button if the field is protected by a "disabled".
7993 list($class, $classfile) = explode(':', $param_list[0]);
7994 if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) {
7995 $url_path = dol_buildpath(dirname(dirname($classfile)).'/card.php', 1);
7996 } else {
7997 $url_path = dol_buildpath(dirname(dirname($classfile)).'/'.strtolower($class).'_card.php', 1);
7998 }
7999 $paramforthenewlink = '';
8000 $paramforthenewlink .= (GETPOSTISSET('action') ? '&action='.GETPOST('action', 'aZ09') : '');
8001 $paramforthenewlink .= (GETPOSTISSET('id') ? '&id='.GETPOSTINT('id') : '');
8002 $paramforthenewlink .= (GETPOSTISSET('origin') ? '&origin='.GETPOST('origin', 'aZ09') : '');
8003 $paramforthenewlink .= (GETPOSTISSET('originid') ? '&originid='.GETPOSTINT('originid') : '');
8004 $paramforthenewlink .= '&fk_'.strtolower($class).'=--IDFORBACKTOPAGE--';
8005 // TODO Add JavaScript code to add input fields already filled into $paramforthenewlink so we won't loose them when going back to main page
8006 $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>';
8007 }
8008 }
8009 } elseif ($type == 'password') {
8010 // If prefix is 'search_', field is used as a filter, we use a common text field.
8011 if ($keyprefix.$key.$keysuffix == 'pass_crypted') {
8012 $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="pass" id="pass" value="" '.($moreparam ? $moreparam : '').'>';
8013 $out .= '<input type="hidden" name="pass_crypted" id="pass_crypted" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
8014 } else {
8015 $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
8016 }
8017 } elseif ($type == 'array') {
8018 $newval = $val;
8019 $newval['type'] = 'varchar(256)';
8020
8021 $out = '';
8022 if (!empty($value)) {
8023 foreach ($value as $option) {
8024 $out .= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
8025 $out .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $morecss).'<br></span>';
8026 }
8027 }
8028 $out .= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
8029
8030 $newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
8031 $newInput .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $morecss).'<br></span>';
8032
8033 if (!empty($conf->use_javascript_ajax)) {
8034 $out .= '
8035 <script nonce="'.getNonce().'">
8036 $(document).ready(function() {
8037 $("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
8038 $("'.dol_escape_js($newInput).'").insertBefore(this);
8039 });
8040
8041 $(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
8042 $(this).parent().remove();
8043 });
8044 });
8045 </script>';
8046 }
8047 }
8048 if (!empty($hidden)) {
8049 $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
8050 }
8051
8052 if ($isDependList == 1) {
8053 $out .= $this->getJSListDependancies('_common');
8054 }
8055 /* Add comments
8056 if ($type == 'date') $out.=' (YYYY-MM-DD)';
8057 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
8058 */
8059
8060 // Display error message for field
8061 if (!empty($fieldValidationErrorMsg) && function_exists('getFieldErrorIcon')) {
8062 $out .= ' '.getFieldErrorIcon($fieldValidationErrorMsg);
8063 }
8064
8065 return $out;
8066 }
8067
8081 public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '')
8082 {
8083 global $conf, $langs, $form;
8084
8085 if (!is_object($form)) {
8086 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
8087 $form = new Form($this->db);
8088 }
8089
8090 //$label = empty($val['label']) ? '' : $val['label'];
8091 $type = empty($val['type']) ? '' : $val['type'];
8092 $size = empty($val['css']) ? '' : $val['css'];
8093 $reg = array();
8094
8095 // Convert var to be able to share same code than showOutputField of extrafields
8096 if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
8097 $type = 'varchar'; // convert varchar(xx) int varchar
8098 $size = $reg[1];
8099 } elseif (preg_match('/varchar/', $type)) {
8100 $type = 'varchar'; // convert varchar(xx) int varchar
8101 }
8102 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8103 if (empty($this->fields[$key]['multiinput'])) {
8104 $type = (($this->fields[$key]['type'] == 'checkbox') ? $this->fields[$key]['type'] : 'select');
8105 }
8106 }
8107 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8108 $type = 'link';
8109 }
8110
8111 $default = empty($val['default']) ? '' : $val['default'];
8112 $computed = empty($val['computed']) ? '' : $val['computed'];
8113 $unique = empty($val['unique']) ? '' : $val['unique'];
8114 $required = empty($val['required']) ? '' : $val['required'];
8115 $param = array();
8116 $param['options'] = array();
8117
8118 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8119 $param['options'] = $val['arrayofkeyval'];
8120 }
8121 if (preg_match('/^integer:([^:]*):([^:]*)/i', $val['type'], $reg)) { // ex: integer:User:user/class/user.class.php
8122 $type = 'link';
8123 $stringforoptions = $reg[1].':'.$reg[2];
8124 // Special case: Force addition of getnomurlparam1 to -1 for users
8125 if ($reg[1] == 'User') {
8126 $stringforoptions .= ':#getnomurlparam1=-1';
8127 }
8128 $param['options'] = array($stringforoptions => $stringforoptions);
8129 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
8130 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
8131 $type = 'sellist';
8132 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
8133 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
8134 $type = 'sellist';
8135 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
8136 $param['options'] = array($reg[1].':'.$reg[2] => 'N');
8137 $type = 'sellist';
8138 } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
8139 $param['options'] = array($reg[1] => 'N');
8140 $type = 'chkbxlst';
8141 }
8142
8143 $langfile = empty($val['langfile']) ? '' : $val['langfile'];
8144 $list = (empty($val['list']) ? '' : $val['list']);
8145 $help = (empty($val['help']) ? '' : $val['help']);
8146 $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)
8147
8148 if ($hidden) {
8149 return '';
8150 }
8151
8152 // If field is a computed field, value must become result of compute
8153 if ($computed) {
8154 // Make the eval of compute string
8155 //var_dump($computed);
8156 $value = dol_eval($computed, 1, 0, '2');
8157 }
8158
8159 if (empty($morecss)) {
8160 if ($type == 'date') {
8161 $morecss = 'minwidth100imp';
8162 } elseif ($type == 'datetime' || $type == 'timestamp') {
8163 $morecss = 'minwidth200imp';
8164 } elseif (in_array($type, array('int', 'double', 'price'))) {
8165 $morecss = 'maxwidth75';
8166 } elseif ($type == 'url') {
8167 $morecss = 'minwidth400';
8168 } elseif ($type == 'boolean') {
8169 $morecss = '';
8170 } else {
8171 if (is_numeric($size) && round((float) $size) < 12) {
8172 $morecss = 'minwidth100';
8173 } elseif (is_numeric($size) && round((float) $size) <= 48) {
8174 $morecss = 'minwidth200';
8175 } else {
8176 $morecss = 'minwidth400';
8177 }
8178 }
8179 }
8180
8181 // Format output value differently according to properties of field
8182 if (in_array($key, array('rowid', 'ref')) && method_exists($this, 'getNomUrl')) {
8183 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.
8184 $value = $this->getNomUrl(1, '', 0, '', 1);
8185 }
8186 } elseif ($key == 'status' && method_exists($this, 'getLibStatut')) {
8187 $value = $this->getLibStatut(3);
8188 } elseif ($type == 'date') {
8189 if (!empty($value)) {
8190 $value = dol_print_date($value, 'day'); // We suppose dates without time are always gmt (storage of course + output)
8191 } else {
8192 $value = '';
8193 }
8194 } elseif ($type == 'datetime' || $type == 'timestamp') {
8195 if (!empty($value)) {
8196 $value = dol_print_date($value, 'dayhour', 'tzuserrel');
8197 } else {
8198 $value = '';
8199 }
8200 } elseif ($type == 'duration') {
8201 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
8202 if (!is_null($value) && $value !== '') {
8203 $value = convertSecondToTime($value, 'allhourmin');
8204 }
8205 } elseif ($type == 'double' || $type == 'real') {
8206 if (!is_null($value) && $value !== '') {
8207 $value = price($value);
8208 }
8209 } elseif ($type == 'boolean') {
8210 $checked = '';
8211 if (!empty($value)) {
8212 $checked = ' checked ';
8213 }
8214 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
8215 $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
8216 } else {
8217 $value = yn($value ? 1 : 0);
8218 }
8219 } elseif ($type == 'mail' || $type == 'email') {
8220 $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
8221 } elseif ($type == 'url') {
8222 $value = dol_print_url($value, '_blank', 32, 1);
8223 } elseif ($type == 'phone') {
8224 $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
8225 } elseif ($type == 'ip') {
8226 $value = dol_print_ip($value, 0);
8227 } elseif ($type == 'price') {
8228 if (!is_null($value) && $value !== '') {
8229 $value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
8230 }
8231 } elseif ($type == 'select') {
8232 $value = isset($param['options'][(string) $value]) ? $param['options'][(string) $value] : '';
8233 if (strpos($value, "|") !== false) {
8234 $value = $langs->trans(explode('|', $value)[0]);
8235 } elseif (! is_numeric($value)) {
8236 $value = $langs->trans($value);
8237 }
8238 } elseif ($type == 'sellist') {
8239 $param_list = array_keys($param['options']);
8240 $InfoFieldList = explode(":", $param_list[0]);
8241
8242 $selectkey = "rowid";
8243 $keyList = 'rowid';
8244
8245 if (count($InfoFieldList) > 2 && !empty($InfoFieldList[2])) {
8246 $selectkey = $InfoFieldList[2];
8247 $keyList = $InfoFieldList[2].' as rowid';
8248 }
8249
8250 $fields_label = explode('|', $InfoFieldList[1]);
8251 if (is_array($fields_label)) {
8252 $keyList .= ', ';
8253 $keyList .= implode(', ', $fields_label);
8254 }
8255
8256 $filter_categorie = false;
8257 if (count($InfoFieldList) > 5) {
8258 if ($InfoFieldList[0] == 'categorie') {
8259 $filter_categorie = true;
8260 }
8261 }
8262
8263 $sql = "SELECT ".$keyList;
8264 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
8265 if (strpos($InfoFieldList[4], 'extra') !== false) {
8266 $sql .= ' as main';
8267 }
8268 if ($selectkey == 'rowid' && empty($value)) {
8269 $sql .= " WHERE ".$selectkey." = 0";
8270 } elseif ($selectkey == 'rowid') {
8271 $sql .= " WHERE ".$selectkey." = ".((int) $value);
8272 } else {
8273 $sql .= " WHERE ".$selectkey." = '".$this->db->escape($value)."'";
8274 }
8275
8276 //$sql.= ' AND entity = '.$conf->entity;
8277
8278 dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
8279 $resql = $this->db->query($sql);
8280 if ($resql) {
8281 if (!$filter_categorie) {
8282 $value = ''; // value was used, so now we reste it to use it to build final output
8283 $numrows = $this->db->num_rows($resql);
8284 if ($numrows) {
8285 $obj = $this->db->fetch_object($resql);
8286
8287 // Several field into label (eq table:code|libelle:rowid)
8288 $fields_label = explode('|', $InfoFieldList[1]);
8289
8290 if (is_array($fields_label) && count($fields_label) > 1) {
8291 foreach ($fields_label as $field_toshow) {
8292 $translabel = '';
8293 if (!empty($obj->$field_toshow)) {
8294 $translabel = $langs->trans($obj->$field_toshow);
8295 }
8296 if ($translabel != $field_toshow) {
8297 $value .= dol_trunc($translabel, 18) . ' ';
8298 } else {
8299 $value .= $obj->$field_toshow . ' ';
8300 }
8301 }
8302 } else {
8303 $translabel = '';
8304 if (!empty($obj->{$InfoFieldList[1]})) {
8305 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
8306 }
8307 if ($translabel != $obj->{$InfoFieldList[1]}) {
8308 $value = dol_trunc($translabel, 18);
8309 } else {
8310 $value = $obj->{$InfoFieldList[1]};
8311 }
8312 }
8313 }
8314 } else {
8315 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
8316
8317 $toprint = array();
8318 $obj = $this->db->fetch_object($resql);
8319 $c = new Categorie($this->db);
8320 $c->fetch($obj->rowid);
8321 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
8322 foreach ($ways as $way) {
8323 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
8324 }
8325 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
8326 }
8327 } else {
8328 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
8329 }
8330 } elseif ($type == 'radio') {
8331 $value = $param['options'][(string) $value];
8332 } elseif ($type == 'checkbox') {
8333 $value_arr = explode(',', (string) $value);
8334 $value = '';
8335 if (is_array($value_arr) && count($value_arr) > 0) {
8336 $toprint = array();
8337 foreach ($value_arr as $keyval => $valueval) {
8338 if (!empty($valueval)) {
8339 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $param['options'][$valueval] . '</li>';
8340 }
8341 }
8342 if (!empty($toprint)) {
8343 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
8344 }
8345 }
8346 } elseif ($type == 'chkbxlst') {
8347 $value_arr = (isset($value) ? explode(',', $value) : array());
8348
8349 $param_list = array_keys($param['options']);
8350 $InfoFieldList = explode(":", $param_list[0]);
8351
8352 $selectkey = "rowid";
8353 $keyList = 'rowid';
8354
8355 if (count($InfoFieldList) >= 3) {
8356 $selectkey = $InfoFieldList[2];
8357 $keyList = $InfoFieldList[2].' as rowid';
8358 }
8359
8360 $fields_label = explode('|', $InfoFieldList[1]);
8361 if (is_array($fields_label)) {
8362 $keyList .= ', ';
8363 $keyList .= implode(', ', $fields_label);
8364 }
8365
8366 $filter_categorie = false;
8367 if (count($InfoFieldList) > 5) {
8368 if ($InfoFieldList[0] == 'categorie') {
8369 $filter_categorie = true;
8370 }
8371 }
8372
8373 $sql = "SELECT ".$keyList;
8374 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
8375 if (strpos($InfoFieldList[4], 'extra') !== false) {
8376 $sql .= ' as main';
8377 }
8378 // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
8379 // $sql.= ' AND entity = '.$conf->entity;
8380
8381 dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG);
8382 $resql = $this->db->query($sql);
8383 if ($resql) {
8384 if (!$filter_categorie) {
8385 $value = ''; // value was used, so now we reset it to use it to build final output
8386 $toprint = array();
8387 while ($obj = $this->db->fetch_object($resql)) {
8388 // Several field into label (eq table:code|libelle:rowid)
8389 $fields_label = explode('|', $InfoFieldList[1]);
8390 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8391 if (is_array($fields_label) && count($fields_label) > 1) {
8392 foreach ($fields_label as $field_toshow) {
8393 $translabel = '';
8394 if (!empty($obj->$field_toshow)) {
8395 $translabel = $langs->trans($obj->$field_toshow);
8396 }
8397 if ($translabel != $field_toshow) {
8398 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
8399 } else {
8400 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->$field_toshow . '</li>';
8401 }
8402 }
8403 } else {
8404 $translabel = '';
8405 if (!empty($obj->{$InfoFieldList[1]})) {
8406 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
8407 }
8408 if ($translabel != $obj->{$InfoFieldList[1]}) {
8409 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
8410 } else {
8411 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->{$InfoFieldList[1]} . '</li>';
8412 }
8413 }
8414 }
8415 }
8416 } else {
8417 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
8418
8419 $toprint = array();
8420 while ($obj = $this->db->fetch_object($resql)) {
8421 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8422 $c = new Categorie($this->db);
8423 $c->fetch($obj->rowid);
8424 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
8425 foreach ($ways as $way) {
8426 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
8427 }
8428 }
8429 }
8430 }
8431 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
8432 } else {
8433 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
8434 }
8435 } elseif ($type == 'link') {
8436 $out = '';
8437
8438 // only if something to display (perf)
8439 if ($value) {
8440 $param_list = array_keys($param['options']);
8441 // Example: $param_list='ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
8442 // Example: $param_list='ObjectClass:PathToClass:#getnomurlparam1=-1#getnomurlparam2=customer'
8443
8444 $InfoFieldList = explode(":", $param_list[0]);
8445
8446 $classname = $InfoFieldList[0];
8447 $classpath = $InfoFieldList[1];
8448
8449 // Set $getnomurlparam1 et getnomurlparam2
8450 $getnomurlparam = 3;
8451 $getnomurlparam2 = '';
8452 $regtmp = array();
8453 if (preg_match('/#getnomurlparam1=([^#]*)/', $param_list[0], $regtmp)) {
8454 $getnomurlparam = $regtmp[1];
8455 }
8456 if (preg_match('/#getnomurlparam2=([^#]*)/', $param_list[0], $regtmp)) {
8457 $getnomurlparam2 = $regtmp[1];
8458 }
8459
8460 if (!empty($classpath)) {
8461 dol_include_once($InfoFieldList[1]);
8462
8463 if ($classname && !class_exists($classname)) {
8464 // from V19 of Dolibarr, In some cases link use element instead of class, example project_task
8465 // TODO use newObjectByElement() introduce in V20 by PR #30036 for better errors management
8466 $element_prop = getElementProperties($classname);
8467 if ($element_prop) {
8468 $classname = $element_prop['classname'];
8469 }
8470 }
8471
8472
8473 if ($classname && class_exists($classname)) {
8474 $object = new $classname($this->db);
8475 if ($object->element === 'product') { // Special case for product because default valut of fetch are wrong
8476 $result = $object->fetch($value, '', '', '', 0, 1, 1);
8477 } else {
8478 $result = $object->fetch($value);
8479 }
8480 if ($result > 0) {
8481 if ($object->element === 'product') {
8482 $get_name_url_param_arr = array($getnomurlparam, $getnomurlparam2, 0, -1, 0, '', 0);
8483 if (isset($val['get_name_url_params'])) {
8484 $get_name_url_params = explode(':', $val['get_name_url_params']);
8485 if (!empty($get_name_url_params)) {
8486 $param_num_max = count($get_name_url_param_arr) - 1;
8487 foreach ($get_name_url_params as $param_num => $param_value) {
8488 if ($param_num > $param_num_max) {
8489 break;
8490 }
8491 $get_name_url_param_arr[$param_num] = $param_value;
8492 }
8493 }
8494 }
8495
8499 $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]);
8500 } else {
8501 $value = $object->getNomUrl($getnomurlparam, $getnomurlparam2);
8502 }
8503 } else {
8504 $value = '';
8505 }
8506 }
8507 } else {
8508 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
8509 return 'Error bad setup of extrafield';
8510 }
8511 } else {
8512 $value = '';
8513 }
8514 } elseif ($type == 'password') {
8515 $value = '<span class="opacitymedium">'.$langs->trans("Encrypted").'</span>';
8516 //$value = preg_replace('/./i', '*', $value);
8517 } elseif ($type == 'array') {
8518 if (is_array($value)) {
8519 $value = implode('<br>', $value);
8520 } else {
8521 dol_syslog(__METHOD__.' Expected array from dol_eval, but got '.gettype($value), LOG_ERR);
8522 return 'Error unexpected result from code evaluation';
8523 }
8524 } else { // text|html|varchar
8525 $value = dol_htmlentitiesbr($value);
8526 }
8527
8528 //print $type.'-'.$size.'-'.$value;
8529 $out = $value;
8530
8531 return is_null($out) ? '' : $out;
8532 }
8533
8540 public function clearFieldError($fieldKey)
8541 {
8542 $this->error = '';
8543 unset($this->validateFieldsErrors[$fieldKey]);
8544 }
8545
8553 public function setFieldError($fieldKey, $msg = '')
8554 {
8555 global $langs;
8556 if (empty($msg)) {
8557 $msg = $langs->trans("UnknownError");
8558 }
8559
8560 $this->error = $this->validateFieldsErrors[$fieldKey] = $msg;
8561 }
8562
8569 public function getFieldError($fieldKey)
8570 {
8571 if (!empty($this->validateFieldsErrors[$fieldKey])) {
8572 return $this->validateFieldsErrors[$fieldKey];
8573 }
8574 return '';
8575 }
8576
8585 public function validateField($fields, $fieldKey, $fieldValue)
8586 {
8587 global $langs;
8588
8589 if (!class_exists('Validate')) {
8590 require_once DOL_DOCUMENT_ROOT . '/core/class/validate.class.php';
8591 }
8592
8593 $this->clearFieldError($fieldKey);
8594
8595 if (!isset($fields[$fieldKey])) {
8596 $this->setFieldError($fieldKey, $langs->trans('FieldNotFoundInObject'));
8597 return false;
8598 }
8599
8600 $val = $fields[$fieldKey];
8601
8602 $param = array();
8603 $param['options'] = array();
8604 $type = $val['type'];
8605
8606 $required = false;
8607 if (isset($val['notnull']) && $val['notnull'] === 1) {
8608 // 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
8609 $required = true;
8610 }
8611
8612 $maxSize = 0;
8613 $minSize = 0;
8614
8615 //
8616 // PREPARE Elements
8617 //
8618 $reg = array();
8619
8620 // Convert var to be able to share same code than showOutputField of extrafields
8621 if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
8622 $type = 'varchar'; // convert varchar(xx) int varchar
8623 $maxSize = $reg[1];
8624 } elseif (preg_match('/varchar/', $type)) {
8625 $type = 'varchar'; // convert varchar(xx) int varchar
8626 }
8627
8628 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8629 $type = 'select';
8630 }
8631
8632 if (!empty($val['type']) && preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8633 $type = 'link';
8634 }
8635
8636 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8637 $param['options'] = $val['arrayofkeyval'];
8638 }
8639
8640 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8641 $type = 'link';
8642 $param['options'] = array($reg[1].':'.$reg[2] => $reg[1].':'.$reg[2]);
8643 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
8644 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
8645 $type = 'sellist';
8646 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
8647 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
8648 $type = 'sellist';
8649 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
8650 $param['options'] = array($reg[1].':'.$reg[2] => 'N');
8651 $type = 'sellist';
8652 }
8653
8654 //
8655 // TEST Value
8656 //
8657
8658 // Use Validate class to allow external Modules to use data validation part instead of concentrate all test here (factoring) or just for reuse
8659 $validate = new Validate($this->db, $langs);
8660
8661
8662 // little trick : to perform tests with good performances sort tests by quick to low
8663
8664 //
8665 // COMMON TESTS
8666 //
8667
8668 // Required test and empty value
8669 if ($required && !$validate->isNotEmptyString($fieldValue)) {
8670 $this->setFieldError($fieldKey, $validate->error);
8671 return false;
8672 } elseif (!$required && !$validate->isNotEmptyString($fieldValue)) {
8673 // if no value sent and the field is not mandatory, no need to perform tests
8674 return true;
8675 }
8676
8677 // MAX Size test
8678 if (!empty($maxSize) && !$validate->isMaxLength($fieldValue, $maxSize)) {
8679 $this->setFieldError($fieldKey, $validate->error);
8680 return false;
8681 }
8682
8683 // MIN Size test
8684 if (!empty($minSize) && !$validate->isMinLength($fieldValue, $minSize)) {
8685 $this->setFieldError($fieldKey, $validate->error);
8686 return false;
8687 }
8688
8689 //
8690 // TESTS for TYPE
8691 //
8692
8693 if (in_array($type, array('date', 'datetime', 'timestamp'))) {
8694 if (!$validate->isTimestamp($fieldValue)) {
8695 $this->setFieldError($fieldKey, $validate->error);
8696 return false;
8697 } else {
8698 return true;
8699 }
8700 } elseif ($type == 'duration') {
8701 if (!$validate->isDuration($fieldValue)) {
8702 $this->setFieldError($fieldKey, $validate->error);
8703 return false;
8704 } else {
8705 return true;
8706 }
8707 } elseif (in_array($type, array('double', 'real', 'price'))) {
8708 // is numeric
8709 if (!$validate->isNumeric($fieldValue)) {
8710 $this->setFieldError($fieldKey, $validate->error);
8711 return false;
8712 } else {
8713 return true;
8714 }
8715 } elseif ($type == 'boolean') {
8716 if (!$validate->isBool($fieldValue)) {
8717 $this->setFieldError($fieldKey, $validate->error);
8718 return false;
8719 } else {
8720 return true;
8721 }
8722 } elseif ($type == 'mail') {
8723 if (!$validate->isEmail($fieldValue)) {
8724 $this->setFieldError($fieldKey, $validate->error);
8725 return false;
8726 }
8727 } elseif ($type == 'url') {
8728 if (!$validate->isUrl($fieldValue)) {
8729 $this->setFieldError($fieldKey, $validate->error);
8730 return false;
8731 } else {
8732 return true;
8733 }
8734 } elseif ($type == 'phone') {
8735 if (!$validate->isPhone($fieldValue)) {
8736 $this->setFieldError($fieldKey, $validate->error);
8737 return false;
8738 } else {
8739 return true;
8740 }
8741 } elseif ($type == 'select' || $type == 'radio') {
8742 if (!isset($param['options'][$fieldValue])) {
8743 $this->error = $langs->trans('RequireValidValue');
8744 return false;
8745 } else {
8746 return true;
8747 }
8748 } elseif ($type == 'sellist' || $type == 'chkbxlst') {
8749 $param_list = array_keys($param['options']);
8750 $InfoFieldList = explode(":", $param_list[0]);
8751 $value_arr = explode(',', $fieldValue);
8752 $value_arr = array_map(array($this->db, 'escape'), $value_arr);
8753
8754 $selectkey = "rowid";
8755 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
8756 $selectkey = $InfoFieldList[2];
8757 }
8758
8759 if (!$validate->isInDb($value_arr, $InfoFieldList[0], $selectkey)) {
8760 $this->setFieldError($fieldKey, $validate->error);
8761 return false;
8762 } else {
8763 return true;
8764 }
8765 } elseif ($type == 'link') {
8766 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
8767 $InfoFieldList = explode(":", $param_list[0]);
8768 $classname = $InfoFieldList[0];
8769 $classpath = $InfoFieldList[1];
8770 if (!$validate->isFetchable($fieldValue, $classname, $classpath)) {
8771 $lastIsFetchableError = $validate->error;
8772
8773 // from V19 of Dolibarr, In some cases link use element instead of class, example project_task
8774 if ($validate->isFetchableElement($fieldValue, $classname)) {
8775 return true;
8776 }
8777
8778 $this->setFieldError($fieldKey, $lastIsFetchableError);
8779 return false;
8780 } else {
8781 return true;
8782 }
8783 }
8784
8785 // if no test failed all is ok
8786 return true;
8787 }
8788
8802 public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = '', $display_type = 'card')
8803 {
8804 global $db, $conf, $langs, $action, $form, $hookmanager;
8805
8806 if (!is_object($form)) {
8807 $form = new Form($db);
8808 }
8809 if (!is_object($extrafields)) {
8810 dol_syslog('Bad parameter extrafields for showOptionals', LOG_ERR);
8811 return 'Bad parameter extrafields for showOptionals';
8812 }
8813 if (!is_array($extrafields->attributes[$this->table_element])) {
8814 dol_syslog("extrafields->attributes was not loaded with extrafields->fetch_name_optionals_label(table_element);", LOG_WARNING);
8815 }
8816
8817 $out = '';
8818
8819 $parameters = array('mode' => $mode, 'params' => $params, 'keysuffix' => $keysuffix, 'keyprefix' => $keyprefix, 'display_type' => $display_type);
8820 $reshook = $hookmanager->executeHooks('showOptionals', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
8821
8822 if (empty($reshook)) {
8823 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) {
8824 $out .= "\n";
8825 $out .= '<!-- commonobject:showOptionals --> ';
8826 $out .= "\n";
8827
8828 $nbofextrafieldsshown = 0;
8829 $e = 0; // var to manage the modulo (odd/even)
8830
8831 $lastseparatorkeyfound = '';
8832 $extrafields_collapse_num = '';
8833 $extrafields_collapse_num_old = '';
8834 $i = 0;
8835
8836 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $label) {
8837 $i++;
8838
8839 // Show only the key field in params @phan-suppress-next-line PhanTypeArraySuspiciousNullable
8840 if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) {
8841 continue;
8842 }
8843
8844 // Test on 'enabled' ('enabled' is different than 'list' = 'visibility')
8845 $enabled = 1;
8846 if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
8847 $enabled = (int) dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
8848 }
8849 if (empty($enabled)) {
8850 continue;
8851 }
8852
8853 $visibility = 1;
8854 if (isset($extrafields->attributes[$this->table_element]['list'][$key])) {
8855 $visibility = (int) dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
8856 }
8857
8858 $perms = 1;
8859 if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
8860 $perms = (int) dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
8861 }
8862
8863 if (($mode == 'create') && !in_array(abs($visibility), array(1, 3))) {
8864 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list
8865 } elseif (($mode == 'edit') && !in_array(abs($visibility), array(1, 3, 4))) {
8866 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list and <> 4 = not visible at the creation
8867 } elseif ($mode == 'view' && empty($visibility)) {
8868 continue;
8869 }
8870 if (empty($perms)) {
8871 continue;
8872 }
8873
8874 // Load language if required
8875 if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
8876 $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
8877 }
8878
8879 $colspan = 0;
8880 $value = null;
8881 if (is_array($params) && count($params) > 0 && $display_type == 'card') {
8882 if (array_key_exists('cols', $params)) {
8883 $colspan = $params['cols'];
8884 } elseif (array_key_exists('colspan', $params)) { // For backward compatibility. Use cols instead now.
8885 $reg = array();
8886 if (preg_match('/colspan="(\d+)"/', $params['colspan'], $reg)) {
8887 $colspan = $reg[1];
8888 } else {
8889 $colspan = $params['colspan'];
8890 }
8891 }
8892 }
8893 $colspan = intval($colspan);
8894
8895 switch ($mode) {
8896 case "view":
8897 $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
8898 break;
8899 case "create":
8900 case "edit":
8901 // We get the value of property found with GETPOST so it takes into account:
8902 // default values overwrite, restore back to list link, ... (but not 'default value in database' of field)
8903 $check = 'alphanohtml';
8904 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text'))) {
8905 $check = 'restricthtml';
8906 }
8907 $getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, $check, 3); // GETPOST can get value from GET, POST or setup of default values overwrite.
8908 // GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
8909 if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)) {
8910 if (is_array($getposttemp)) {
8911 // $getposttemp is an array but following code expects a comma separated string
8912 $value = implode(",", $getposttemp);
8913 } else {
8914 $value = $getposttemp;
8915 }
8916 } elseif (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('int'))) {
8917 $value =( !empty($this->array_options["options_".$key]) || $this->array_options["options_".$key] === '0' ) ? $this->array_options["options_".$key] : '';
8918 } else {
8919 $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.
8920 }
8921 //var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
8922 break;
8923 }
8924
8925 $nbofextrafieldsshown++;
8926
8927 // Output value of the current field
8928 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
8929 $extrafields_collapse_num = $key;
8930 /*
8931 $extrafield_param = $extrafields->attributes[$this->table_element]['param'][$key];
8932 if (!empty($extrafield_param) && is_array($extrafield_param)) {
8933 $extrafield_param_list = array_keys($extrafield_param['options']);
8934
8935 if (count($extrafield_param_list) > 0) {
8936 $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
8937
8938 if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
8939 //$extrafields_collapse_num = $extrafields->attributes[$this->table_element]['pos'][$key];
8940 $extrafields_collapse_num = $key;
8941 }
8942 }
8943 }
8944 */
8945
8946 // if colspan=0 or 1, the second column is not extended, so the separator must be on 2 columns
8947 $out .= $extrafields->showSeparator($key, $this, ($colspan ? $colspan + 1 : 2), $display_type, $mode);
8948
8949 $lastseparatorkeyfound = $key;
8950 } else {
8951 $collapse_group = $extrafields_collapse_num.(!empty($this->id) ? '_'.$this->id : '');
8952
8953 $class = (!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
8954 $csstyle = '';
8955 if (is_array($params) && count($params) > 0) {
8956 if (array_key_exists('class', $params)) {
8957 $class .= $params['class'].' ';
8958 }
8959 if (array_key_exists('style', $params)) {
8960 $csstyle = $params['style'];
8961 }
8962 }
8963
8964 // add html5 elements
8965 $domData = ' data-element="extrafield"';
8966 $domData .= ' data-targetelement="'.$this->element.'"';
8967 $domData .= ' data-targetid="'.$this->id.'"';
8968
8969 $html_id = (empty($this->id) ? '' : 'extrarow-'.$this->element.'_'.$key.'_'.$this->id);
8970 if ($display_type == 'card') {
8971 if (getDolGlobalString('MAIN_EXTRAFIELDS_USE_TWO_COLUMS') && ($e % 2) == 0) {
8972 $colspan = 0;
8973 }
8974
8975 if ($action == 'selectlines') {
8976 $colspan++;
8977 }
8978 }
8979
8980 // Convert date into timestamp format (value in memory must be a timestamp)
8981 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date'))) {
8982 $datenotinstring = null;
8983 if (array_key_exists('options_'.$key, $this->array_options)) {
8984 $datenotinstring = $this->array_options['options_'.$key];
8985 if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
8986 $datenotinstring = $this->db->jdate($datenotinstring);
8987 }
8988 }
8989 $datekey = $keyprefix.'options_'.$key.$keysuffix;
8990 $value = (GETPOSTISSET($datekey)) ? dol_mktime(12, 0, 0, GETPOSTINT($datekey.'month', 3), GETPOSTINT($datekey.'day', 3), GETPOSTINT($datekey.'year', 3)) : $datenotinstring;
8991 }
8992 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('datetime'))) {
8993 $datenotinstring = null;
8994 if (array_key_exists('options_'.$key, $this->array_options)) {
8995 $datenotinstring = $this->array_options['options_'.$key];
8996 if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
8997 $datenotinstring = $this->db->jdate($datenotinstring);
8998 }
8999 }
9000 $timekey = $keyprefix.'options_'.$key.$keysuffix;
9001 $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;
9002 }
9003 // Convert float submitted string into real php numeric (value in memory must be a php numeric)
9004 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price', 'double'))) {
9005 if (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) {
9006 $value = price2num($value);
9007 } elseif (isset($this->array_options['options_'.$key])) {
9008 $value = $this->array_options['options_'.$key];
9009 }
9010 }
9011
9012 // HTML, text, select, integer and varchar: take into account default value in database if in create mode
9013 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text', 'varchar', 'select', 'radio', 'int', 'boolean'))) {
9014 if ($action == 'create' || $mode == 'create') {
9015 $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) ? $value : $extrafields->attributes[$this->table_element]['default'][$key];
9016 }
9017 }
9018
9019 $labeltoshow = $langs->trans($label);
9020 $helptoshow = $langs->trans($extrafields->attributes[$this->table_element]['help'][$key]);
9021 if ($display_type == 'card') {
9022 $out .= '<tr '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="field_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
9023 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER') && ($action == 'view' || $action == 'valid' || $action == 'editline' || $action == 'confirm_valid' || $action == 'confirm_cancel')) {
9024 $out .= '<td></td>';
9025 }
9026 $out .= '<td class="'.(empty($params['tdclass']) ? 'titlefieldcreate' : $params['tdclass']).' wordbreak';
9027 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'text') {
9028 $out .= ' tdtop';
9029 }
9030 } elseif ($display_type == 'line') {
9031 $out .= '<div '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="fieldline_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
9032 $out .= '<div style="display: inline-block; padding-right:4px" class="wordbreak';
9033 }
9034 //$out .= "titlefield";
9035 //if (GETPOST('action', 'restricthtml') == 'create') $out.='create';
9036 // BUG #11554 : For public page, use red dot for required fields, instead of bold label
9037 $tpl_context = isset($params["tpl_context"]) ? $params["tpl_context"] : "none";
9038 if ($tpl_context != "public") { // Public page : red dot instead of fieldrequired characters
9039 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
9040 $out .= ' fieldrequired';
9041 }
9042 }
9043 $out .= '">';
9044 if ($tpl_context == "public") { // Public page : red dot instead of fieldrequired characters
9045 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
9046 $out .= $form->textwithpicto($labeltoshow, $helptoshow);
9047 } else {
9048 $out .= $labeltoshow;
9049 }
9050 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
9051 $out .= '&nbsp;<span style="color: red">*</span>';
9052 }
9053 } else {
9054 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
9055 $out .= $form->textwithpicto($labeltoshow, $helptoshow);
9056 } else {
9057 $out .= $labeltoshow;
9058 }
9059 }
9060
9061 $out .= ($display_type == 'card' ? '</td>' : '</div>');
9062
9063 // Second column
9064 $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
9065 if ($display_type == 'card') {
9066 // 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
9067 $out .= '<td '.($html_id ? 'id="'.$html_id.'" ' : '').' class="valuefieldcreate '.$this->element.'_extras_'.$key;
9068 $out .= '" '.($colspan ? ' colspan="'.$colspan.'"' : '');
9069 $out .= '>';
9070 } elseif ($display_type == 'line') {
9071 $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].'">';
9072 }
9073
9074 switch ($mode) {
9075 case "view":
9076 $out .= $extrafields->showOutputField($key, $value, '', $this->table_element);
9077 break;
9078 case "create":
9079 $listoftypestoshowpicto = explode(',', getDolGlobalString('MAIN_TYPES_TO_SHOW_PICOT', 'email,phone,ip,password'));
9080 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], $listoftypestoshowpicto)) {
9081 $out .= getPictoForType($extrafields->attributes[$this->table_element]['type'][$key], ($extrafields->attributes[$this->table_element]['type'][$key] == 'text' ? 'tdtop' : ''));
9082 }
9083 //$out .= '<!-- type = '.$extrafields->attributes[$this->table_element]['type'][$key].' -->';
9084 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
9085 break;
9086 case "edit":
9087 $listoftypestoshowpicto = explode(',', getDolGlobalString('MAIN_TYPES_TO_SHOW_PICOT', 'email,phone,ip,password'));
9088 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], $listoftypestoshowpicto)) {
9089 $out .= getPictoForType($extrafields->attributes[$this->table_element]['type'][$key], ($extrafields->attributes[$this->table_element]['type'][$key] == 'text' ? 'tdtop' : ''));
9090 }
9091 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
9092 break;
9093 }
9094
9095 $out .= ($display_type == 'card' ? '</td>' : '</div>');
9096 $out .= ($display_type == 'card' ? '</tr>'."\n" : '</div>');
9097 $e++;
9098 }
9099 }
9100 $out .= "\n";
9101 // Add code to manage list depending on others
9102 if (!empty($conf->use_javascript_ajax)) {
9103 $out .= $this->getJSListDependancies();
9104 }
9105
9106 $out .= '<!-- commonobject:showOptionals end --> '."\n";
9107
9108 if (empty($nbofextrafieldsshown)) {
9109 $out = '';
9110 }
9111 }
9112 }
9113
9114 $out .= $hookmanager->resPrint;
9115
9116 return $out;
9117 }
9118
9123 public function getJSListDependancies($type = '_extra')
9124 {
9125 $out = '
9126 <script nonce="'.getNonce().'">
9127 jQuery(document).ready(function() {
9128 function showOptions'.$type.'(child_list, parent_list, orig_select)
9129 {
9130 var val = $("select[name=\""+parent_list+"\"]").val();
9131 var parentVal = parent_list + ":" + val;
9132 if(typeof val == "string"){
9133 if(val != "") {
9134 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
9135 $("select[name=\""+child_list+"\"] option[parent]").remove();
9136 $("select[name=\""+child_list+"\"]").append(options);
9137 } else {
9138 var options = orig_select.find("option[parent]").clone();
9139 $("select[name=\""+child_list+"\"] option[parent]").remove();
9140 $("select[name=\""+child_list+"\"]").append(options);
9141 }
9142 } else if(val > 0) {
9143 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
9144 $("select[name=\""+child_list+"\"] option[parent]").remove();
9145 $("select[name=\""+child_list+"\"]").append(options);
9146 } else {
9147 var options = orig_select.find("option[parent]").clone();
9148 $("select[name=\""+child_list+"\"] option[parent]").remove();
9149 $("select[name=\""+child_list+"\"]").append(options);
9150 }
9151 }
9152 function setListDependencies'.$type.'() {
9153 jQuery("select option[parent]").parent().each(function() {
9154 var orig_select = {};
9155 var child_list = $(this).attr("name");
9156 orig_select[child_list] = $(this).clone();
9157 var parent = $(this).find("option[parent]:first").attr("parent");
9158 var infos = parent.split(":");
9159 var parent_list = infos[0];
9160
9161 //Hide daughters lists
9162 if ($("#"+child_list).val() == 0 && $("#"+parent_list).val() == 0){
9163 $("#"+child_list).hide();
9164 //Show mother lists
9165 } else if ($("#"+parent_list).val() != 0){
9166 $("#"+parent_list).show();
9167 }
9168 //Show the child list if the parent list value is selected
9169 $("select[name=\""+parent_list+"\"]").click(function() {
9170 if ($(this).val() != 0){
9171 $("#"+child_list).show()
9172 }
9173 });
9174
9175 //When we change parent list
9176 $("select[name=\""+parent_list+"\"]").change(function() {
9177 showOptions'.$type.'(child_list, parent_list, orig_select[child_list]);
9178 //Select the value 0 on child list after a change on the parent list
9179 $("#"+child_list).val(0).trigger("change");
9180 //Hide child lists if the parent value is set to 0
9181 if ($(this).val() == 0){
9182 $("#"+child_list).hide();
9183 }
9184 });
9185 });
9186 }
9187
9188 setListDependencies'.$type.'();
9189 });
9190 </script>'."\n";
9191 return $out;
9192 }
9193
9199 public function getRights()
9200 {
9201 global $user;
9202
9203 $module = empty($this->module) ? '' : $this->module;
9204 $element = $this->element;
9205
9206 if ($element == 'facturerec') {
9207 $element = 'facture';
9208 } elseif ($element == 'invoice_supplier_rec') {
9209 return !$user->hasRight('fournisseur', 'facture') ? null : $user->hasRight('fournisseur', 'facture');
9210 } elseif ($module && $user->hasRight($module, $element)) {
9211 // for modules built with ModuleBuilder
9212 return $user->hasRight($module, $element);
9213 }
9214
9215 return $user->rights->$element;
9216 }
9217
9230 public static function commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
9231 {
9232 foreach ($tables as $table) {
9233 $sql = 'UPDATE '.$dbs->prefix().$table.' SET fk_soc = '.((int) $dest_id).' WHERE fk_soc = '.((int) $origin_id);
9234
9235 if (!$dbs->query($sql)) {
9236 if ($ignoreerrors) {
9237 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.
9238 }
9239 //$this->errors = $db->lasterror();
9240 return false;
9241 }
9242 }
9243
9244 return true;
9245 }
9246
9259 public static function commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
9260 {
9261 foreach ($tables as $table) {
9262 $sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_product = '.((int) $dest_id).' WHERE fk_product = '.((int) $origin_id);
9263
9264 if (!$dbs->query($sql)) {
9265 if ($ignoreerrors) {
9266 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.
9267 }
9268 //$this->errors = $db->lasterror();
9269 return false;
9270 }
9271 }
9272
9273 return true;
9274 }
9275
9288 public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
9289 {
9290 global $conf;
9291
9292 $buyPrice = 0;
9293
9294 if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && getDolGlobalInt('ForceBuyingPriceIfNull') > 0)) {
9295 // When ForceBuyingPriceIfNull is set
9296 $buyPrice = $unitPrice * (1 - $discountPercent / 100);
9297 } else {
9298 // Get cost price for margin calculation
9299 if (!empty($fk_product) && $fk_product > 0) {
9300 $result = 0;
9301 if (getDolGlobalString('MARGIN_TYPE') == 'costprice') {
9302 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
9303 $product = new Product($this->db);
9304 $result = $product->fetch($fk_product);
9305 if ($result <= 0) {
9306 $this->errors[] = 'ErrorProductIdDoesNotExists';
9307 return -1;
9308 }
9309 if ($product->cost_price > 0) {
9310 $buyPrice = $product->cost_price;
9311 } elseif ($product->pmp > 0) {
9312 $buyPrice = $product->pmp;
9313 }
9314 } elseif (getDolGlobalString('MARGIN_TYPE') == 'pmp') {
9315 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
9316 $product = new Product($this->db);
9317 $result = $product->fetch($fk_product);
9318 if ($result <= 0) {
9319 $this->errors[] = 'ErrorProductIdDoesNotExists';
9320 return -1;
9321 }
9322 if ($product->pmp > 0) {
9323 $buyPrice = $product->pmp;
9324 }
9325 }
9326
9327 if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1', 'pmp', 'costprice'))) {
9328 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
9329 $productFournisseur = new ProductFournisseur($this->db);
9330 if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0) {
9331 $buyPrice = $productFournisseur->fourn_unitprice;
9332 } elseif ($result < 0) {
9333 $this->errors[] = $productFournisseur->error;
9334 return -2;
9335 }
9336 }
9337 }
9338 }
9339 return $buyPrice;
9340 }
9341
9349 public function getDataToShowPhoto($modulepart, $imagesize)
9350 {
9351 // See getDataToShowPhoto() implemented by Product for example.
9352 return array('dir' => '', 'file' => '', 'originalfile' => '', 'altfile' => '', 'email' => '', 'capture' => '');
9353 }
9354
9355
9356 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
9376 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')
9377 {
9378 // phpcs:enable
9379 global $user, $langs;
9380
9381 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
9382 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
9383
9384 $sortfield = 'position_name';
9385 $sortorder = 'asc';
9386
9387 $dir = $sdir.'/';
9388 $pdir = '/';
9389
9390 $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
9391 $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
9392
9393 // For backward compatibility
9394 if ($modulepart == 'product') {
9395 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
9396 $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9397 $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9398 }
9399 }
9400 if ($modulepart == 'category') {
9401 $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9402 $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9403 }
9404
9405 // Defined relative dir to DOL_DATA_ROOT
9406 $relativedir = '';
9407 if ($dir) {
9408 $relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
9409 $relativedir = preg_replace('/^[\\/]/', '', $relativedir);
9410 $relativedir = preg_replace('/[\\/]$/', '', $relativedir);
9411 }
9412
9413 $dirthumb = $dir.'thumbs/';
9414 $pdirthumb = $pdir.'thumbs/';
9415
9416 $return = '<!-- Photo -->'."\n";
9417 $nbphoto = 0;
9418
9419 $filearray = dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ? SORT_DESC : SORT_ASC), 1);
9420
9421 /*if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) // For backward compatibility, we scan also old dirs
9422 {
9423 $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
9424 $filearray=array_merge($filearray, $filearrayold);
9425 }*/
9426
9427 completeFileArrayWithDatabaseInfo($filearray, $relativedir);
9428
9429 if (count($filearray)) {
9430 if ($sortfield && $sortorder) {
9431 $filearray = dol_sort_array($filearray, $sortfield, $sortorder);
9432 }
9433
9434 foreach ($filearray as $key => $val) {
9435 $photo = '';
9436 $file = $val['name'];
9437
9438 //if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
9439 if (image_format_supported($file) >= 0) {
9440 $nbphoto++;
9441 $photo = $file;
9442 $viewfilename = $file;
9443
9444 if ($size == 1 || $size == 'small') { // Format vignette
9445 // Find name of thumb file
9446 $photo_vignette = basename(getImageFileNameForSize($dir.$file, '_small'));
9447 if (!dol_is_file($dirthumb.$photo_vignette)) {
9448 // The thumb does not exists, so we will use the original file
9449 $dirthumb = $dir;
9450 $pdirthumb = $pdir;
9451 $photo_vignette = basename($file);
9452 }
9453
9454 // Get filesize of original file
9455 $imgarray = dol_getImageSize($dir.$photo);
9456
9457 if ($nbbyrow > 0) {
9458 if ($nbphoto == 1) {
9459 $return .= '<table class="valigntop center centpercent" style="border: 0; padding: 2px; border-spacing: 2px; border-collapse: separate;">';
9460 }
9461
9462 if ($nbphoto % $nbbyrow == 1) {
9463 $return .= '<tr class="center valignmiddle" style="border: 1px">';
9464 }
9465 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%" class="photo">'."\n";
9466 } elseif ($nbbyrow < 0) {
9467 $return .= '<div class="inline-block">'."\n";
9468 }
9469
9470 $relativefile = preg_replace('/^\//', '', $pdir.$photo);
9471 if (empty($nolink)) {
9472 $urladvanced = getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
9473 if ($urladvanced) {
9474 $return .= '<a href="'.$urladvanced.'">';
9475 } else {
9476 $return .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank" rel="noopener noreferrer">';
9477 }
9478 }
9479
9480 // Show image (width height=$maxHeight)
9481 // Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
9482 $alt = $langs->transnoentitiesnoconv('File').': '.$relativefile;
9483 $alt .= ' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
9484 if ($overwritetitle) {
9485 if (is_numeric($overwritetitle)) {
9486 $alt = '';
9487 } else {
9488 $alt = $overwritetitle;
9489 }
9490 }
9491 if (empty($cache) && !empty($val['label'])) {
9492 // label is md5 of file
9493 // use it in url to say we want to cache this version of the file
9494 $cache = $val['label'];
9495 }
9496 if ($usesharelink) {
9497 if (array_key_exists('share', $val) && $val['share']) {
9498 if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
9499 $return .= '<!-- Show original file (thumb not yet available with shared links) -->';
9500 $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).'">';
9501 } else {
9502 $return .= '<!-- Show original file -->';
9503 $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).'">';
9504 }
9505 } else {
9506 $return .= '<!-- Show nophoto file (because file is not shared) -->';
9507 $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
9508 }
9509 } else {
9510 if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
9511 $return .= '<!-- Show thumb -->';
9512 $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).'">';
9513 } else {
9514 $return .= '<!-- Show original file -->';
9515 $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).'">';
9516 }
9517 }
9518
9519 if (empty($nolink)) {
9520 $return .= '</a>';
9521 }
9522
9523 if ($showfilename) {
9524 $return .= '<br>'.$viewfilename;
9525 }
9526 if ($showaction) {
9527 $return .= '<br>';
9528 // If $photo_vignette set, we add a link to generate thumbs if file is an image and width or height higher than limits
9529 if ($photo_vignette && (image_format_supported($photo) > 0) && ((isset($imgarray['width']) && $imgarray['width'] > $maxWidth) || (isset($imgarray['width']) && $imgarray['width'] > $maxHeight))) {
9530 $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>';
9531 }
9532 // Special case for product
9533 if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
9534 // Link to resize
9535 $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; ';
9536
9537 // Link to delete
9538 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
9539 $return .= img_delete().'</a>';
9540 }
9541 }
9542 $return .= "\n";
9543
9544 if ($nbbyrow > 0) {
9545 $return .= '</td>';
9546 if (($nbphoto % $nbbyrow) == 0) {
9547 $return .= '</tr>';
9548 }
9549 } elseif ($nbbyrow < 0) {
9550 $return .= '</div>'."\n";
9551 }
9552 }
9553
9554 if (empty($size)) { // Format origine
9555 $return .= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
9556
9557 if ($showfilename) {
9558 $return .= '<br>'.$viewfilename;
9559 }
9560 if ($showaction) {
9561 // Special case for product
9562 if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
9563 // Link to resize
9564 $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; ';
9565
9566 // Link to delete
9567 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
9568 $return .= img_delete().'</a>';
9569 }
9570 }
9571 }
9572
9573 // On continue ou on arrete de boucler ?
9574 if ($nbmax && $nbphoto >= $nbmax) {
9575 break;
9576 }
9577 }
9578 }
9579
9580 if ($size == 1 || $size == 'small') {
9581 if ($nbbyrow > 0) {
9582 // Ferme tableau
9583 while ($nbphoto % $nbbyrow) {
9584 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%">&nbsp;</td>';
9585 $nbphoto++;
9586 }
9587
9588 if ($nbphoto) {
9589 $return .= '</table>';
9590 }
9591 }
9592 }
9593 }
9594
9595 $this->nbphoto = $nbphoto;
9596
9597 return $return;
9598 }
9599
9600
9607 protected function isArray($info)
9608 {
9609 if (is_array($info)) {
9610 if (isset($info['type']) && $info['type'] == 'array') {
9611 return true;
9612 } else {
9613 return false;
9614 }
9615 }
9616 return false;
9617 }
9618
9625 public function isDate($info)
9626 {
9627 if (isset($info['type']) && ($info['type'] == 'date' || $info['type'] == 'datetime' || $info['type'] == 'timestamp')) {
9628 return true;
9629 }
9630 return false;
9631 }
9632
9639 public function isDuration($info)
9640 {
9641 if (is_array($info)) {
9642 if (isset($info['type']) && ($info['type'] == 'duration')) {
9643 return true;
9644 } else {
9645 return false;
9646 }
9647 } else {
9648 return false;
9649 }
9650 }
9651
9658 public function isInt($info)
9659 {
9660 if (is_array($info)) {
9661 if (isset($info['type']) && (preg_match('/(^int|int$)/i', $info['type']))) {
9662 return true;
9663 } else {
9664 return false;
9665 }
9666 } else {
9667 return false;
9668 }
9669 }
9670
9677 public function isFloat($info)
9678 {
9679 if (is_array($info)) {
9680 if (isset($info['type']) && (preg_match('/^(double|real|price)/i', $info['type']))) {
9681 return true;
9682 } else {
9683 return false;
9684 }
9685 }
9686 return false;
9687 }
9688
9695 public function isText($info)
9696 {
9697 if (is_array($info)) {
9698 if (isset($info['type']) && $info['type'] == 'text') {
9699 return true;
9700 } else {
9701 return false;
9702 }
9703 }
9704 return false;
9705 }
9706
9713 protected function canBeNull($info)
9714 {
9715 if (is_array($info)) {
9716 if (array_key_exists('notnull', $info) && $info['notnull'] != '1') {
9717 return true;
9718 } else {
9719 return false;
9720 }
9721 }
9722 return true;
9723 }
9724
9731 protected function isForcedToNullIfZero($info)
9732 {
9733 if (is_array($info)) {
9734 if (array_key_exists('notnull', $info) && $info['notnull'] == '-1') {
9735 return true;
9736 } else {
9737 return false;
9738 }
9739 }
9740 return false;
9741 }
9742
9749 protected function isIndex($info)
9750 {
9751 if (is_array($info)) {
9752 if (array_key_exists('index', $info) && $info['index'] == true) {
9753 return true;
9754 } else {
9755 return false;
9756 }
9757 }
9758 return false;
9759 }
9760
9761
9770 protected function setSaveQuery()
9771 {
9772 global $conf;
9773
9774 $queryarray = array();
9775 foreach ($this->fields as $field => $info) { // Loop on definition of fields
9776 // Depending on field type ('datetime', ...)
9777 if ($this->isDate($info)) {
9778 if (empty($this->{$field})) {
9779 $queryarray[$field] = null;
9780 } else {
9781 $queryarray[$field] = $this->db->idate($this->{$field});
9782 }
9783 } elseif ($this->isDuration($info)) {
9784 // $this->{$field} may be null, '', 0, '0', 123, '123'
9785 if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
9786 if (!isset($this->{$field})) {
9787 if (!empty($info['default'])) {
9788 $queryarray[$field] = $info['default'];
9789 } else {
9790 $queryarray[$field] = 0;
9791 }
9792 } else {
9793 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9794 }
9795 } else {
9796 $queryarray[$field] = null;
9797 }
9798 } elseif ($this->isInt($info) || $this->isFloat($info)) {
9799 if ($field == 'entity' && is_null($this->{$field})) {
9800 $queryarray[$field] = ((int) $conf->entity);
9801 } else {
9802 // $this->{$field} may be null, '', 0, '0', 123, '123'
9803 if ((isset($this->{$field}) && ((string) $this->{$field}) != '') || !empty($info['notnull'])) {
9804 if (!isset($this->{$field})) {
9805 $queryarray[$field] = 0;
9806 } elseif ($this->isInt($info)) {
9807 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9808 } elseif ($this->isFloat($info)) {
9809 $queryarray[$field] = (float) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9810 }
9811 } else {
9812 $queryarray[$field] = null;
9813 }
9814 }
9815 } else {
9816 // Note: If $this->{$field} is not defined, it means there is a bug into definition of ->fields or a missing declaration of property
9817 // We should keep the warning generated by this because it is a bug somewhere else in code, not here.
9818 $queryarray[$field] = $this->{$field};
9819 }
9820
9821 if (array_key_exists('type', $info) && $info['type'] == 'timestamp' && empty($queryarray[$field])) {
9822 unset($queryarray[$field]);
9823 }
9824 if (!empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) {
9825 $queryarray[$field] = null; // May force 0 to null
9826 }
9827 }
9828
9829 return $queryarray;
9830 }
9831
9838 public function setVarsFromFetchObj(&$obj)
9839 {
9840 global $db;
9841
9842 foreach ($this->fields as $field => $info) {
9843 if ($this->isDate($info)) {
9844 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') {
9845 $this->$field = '';
9846 } else {
9847 $this->$field = $db->jdate($obj->$field);
9848 }
9849 } elseif ($this->isInt($info)) {
9850 if ($field == 'rowid') {
9851 $this->id = (int) $obj->$field;
9852 } else {
9853 if ($this->isForcedToNullIfZero($info)) {
9854 if (empty($obj->$field)) {
9855 $this->$field = null;
9856 } else {
9857 $this->$field = (int) $obj->$field;
9858 }
9859 } else {
9860 if (isset($obj->$field) && (!is_null($obj->$field) || (array_key_exists('notnull', $info) && $info['notnull'] == 1))) {
9861 $this->$field = (int) $obj->$field;
9862 } else {
9863 $this->$field = null;
9864 }
9865 }
9866 }
9867 } elseif ($this->isFloat($info)) {
9868 if ($this->isForcedToNullIfZero($info)) {
9869 if (empty($obj->$field)) {
9870 $this->$field = null;
9871 } else {
9872 $this->$field = (float) $obj->$field;
9873 }
9874 } else {
9875 if (isset($obj->$field) && (!is_null($obj->$field) || (array_key_exists('notnull', $info) && $info['notnull'] == 1))) {
9876 $this->$field = (float) $obj->$field;
9877 } else {
9878 $this->$field = null;
9879 }
9880 }
9881 } else {
9882 $this->$field = isset($obj->$field) ? $obj->$field : null;
9883 }
9884 }
9885
9886 // If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
9887 if (!isset($this->fields['ref']) && isset($this->id)) {
9888 $this->ref = (string) $this->id;
9889 }
9890 }
9891
9896 public function emtpyObjectVars()
9897 {
9898 foreach ($this->fields as $field => $arr) {
9899 $this->$field = null;
9900 }
9901 }
9902
9910 public function getFieldList($alias = '', $excludefields = array())
9911 {
9912 $keys = array_keys($this->fields);
9913 if (!empty($alias)) {
9914 $keys_with_alias = array();
9915 foreach ($keys as $fieldname) {
9916 if (!empty($excludefields)) {
9917 if (in_array($fieldname, $excludefields)) { // The field is excluded and must not be in output
9918 continue;
9919 }
9920 }
9921 $keys_with_alias[] = $alias . '.' . $fieldname;
9922 }
9923 return implode(',', $keys_with_alias);
9924 } else {
9925 return implode(',', $keys);
9926 }
9927 }
9928
9936 protected function quote($value, $fieldsentry)
9937 {
9938 if (is_null($value)) {
9939 return 'NULL';
9940 } elseif (preg_match('/^(int|double|real|price)/i', $fieldsentry['type'])) {
9941 return price2num((string) $value);
9942 } elseif (preg_match('/int$/i', $fieldsentry['type'])) {
9943 return (int) $value;
9944 } elseif ($fieldsentry['type'] == 'boolean') {
9945 if ($value) {
9946 return 'true';
9947 } else {
9948 return 'false';
9949 }
9950 } else {
9951 return "'".$this->db->escape($value)."'";
9952 }
9953 }
9954
9955
9963 public function createCommon(User $user, $notrigger = 0)
9964 {
9965 global $langs;
9966
9967 dol_syslog(get_class($this)."::createCommon create", LOG_DEBUG);
9968
9969 $error = 0;
9970
9971 $now = dol_now();
9972
9973 $fieldvalues = $this->setSaveQuery();
9974
9975 // Note: Here, $fieldvalues contains same keys (or less) that are inside ->fields
9976
9977 if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) {
9978 $fieldvalues['date_creation'] = $this->db->idate($now);
9979 }
9980 if (array_key_exists('fk_user_creat', $fieldvalues) && !($fieldvalues['fk_user_creat'] > 0)) {
9981 $fieldvalues['fk_user_creat'] = $user->id;
9982 $this->fk_user_creat = $user->id;
9983 }
9984 if (array_key_exists('user_creation_id', $fieldvalues) && !($fieldvalues['user_creation_id'] > 0)) {
9985 $fieldvalues['user_creation_id'] = $user->id;
9986 $this->user_creation_id = $user->id;
9987 }
9988 if (array_key_exists('pass_crypted', $fieldvalues) && property_exists($this, 'pass')) {
9989 // @phan-suppress-next-line PhanUndeclaredProperty
9990 $fieldvalues['pass_crypted'] = dol_hash($this->pass);
9991 }
9992 if (array_key_exists('ref', $fieldvalues)) {
9993 $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
9994 }
9995
9996 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
9997
9998 $keys = array();
9999 $values = array(); // Array to store string forged for SQL syntax
10000 foreach ($fieldvalues as $k => $v) {
10001 $keys[$k] = $k;
10002 $value = $this->fields[$k];
10003 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
10004 $values[$k] = $this->quote($v, $value); // May return string 'NULL' if $value is null
10005 }
10006
10007 // Clean and check mandatory
10008 foreach ($keys as $key) {
10009 if (!isset($this->fields[$key])) {
10010 continue;
10011 }
10012 $key_fields = $this->fields[$key];
10013
10014 // If field is an implicit foreign key field (so type = 'integer:...')
10015 if (preg_match('/^integer:/i', $key_fields['type']) && $values[$key] == '-1') {
10016 $values[$key] = '';
10017 }
10018 if (!empty($key_fields['foreignkey']) && $values[$key] == '-1') {
10019 $values[$key] = '';
10020 }
10021
10022 if (isset($key_fields['notnull']) && $key_fields['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && (!isset($key_fields['default']) || is_null($key_fields['default']))) {
10023 $error++;
10024 $langs->load("errors");
10025 dol_syslog("Mandatory field '".$key."' is empty and required into ->fields definition of class");
10026 $this->errors[] = $langs->trans("ErrorFieldRequired", isset($key_fields['label']) ? $key_fields['label'] : $key);
10027 }
10028
10029 // If value is null and there is a default value for field @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
10030 if (isset($key_fields['notnull']) && $key_fields['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && !is_null($key_fields['default'])) {
10031 $values[$key] = $this->quote($key_fields['default'], $key_fields);
10032 }
10033
10034 // If field is an implicit foreign key field (so type = 'integer:...')
10035 if (isset($key_fields['type']) && preg_match('/^integer:/i', $key_fields['type']) && empty($values[$key])) {
10036 if (isset($key_fields['default'])) {
10037 $values[$key] = ((int) $key_fields['default']);
10038 } else {
10039 $values[$key] = 'null';
10040 }
10041 }
10042 if (!empty($key_fields['foreignkey']) && empty($values[$key])) {
10043 $values[$key] = 'null';
10044 }
10045 }
10046
10047 if ($error) {
10048 return -1;
10049 }
10050
10051 $this->db->begin();
10052
10053 if (!$error) {
10054 $sql = "INSERT INTO ".$this->db->prefix().$this->table_element;
10055 $sql .= " (".implode(", ", $keys).')';
10056 $sql .= " VALUES (".implode(", ", $values).")"; // $values can contains 'abc' or 123
10057
10058 $res = $this->db->query($sql);
10059 if (!$res) {
10060 $error++;
10061 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
10062 $this->errors[] = "ErrorRefAlreadyExists";
10063 } else {
10064 $this->errors[] = $this->db->lasterror();
10065 }
10066 }
10067 }
10068
10069 if (!$error) {
10070 $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
10071 }
10072
10073 // If we have a field ref with a default value of (PROV)
10074 if (!$error) {
10075 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
10076 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)') {
10077 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ref = '(PROV".((int) $this->id).")' WHERE (ref = '(PROV)' OR ref = '') AND rowid = ".((int) $this->id);
10078 $resqlupdate = $this->db->query($sql);
10079
10080 if ($resqlupdate === false) {
10081 $error++;
10082 $this->errors[] = $this->db->lasterror();
10083 } else {
10084 $this->ref = '(PROV'.$this->id.')';
10085 }
10086 }
10087 }
10088
10089 // Create extrafields
10090 if (!$error) {
10091 $result = $this->insertExtraFields();
10092 if ($result < 0) {
10093 $error++;
10094 }
10095 }
10096
10097 // Create lines
10098 if (!empty($this->table_element_line) && !empty($this->fk_element)) {
10099 foreach ($this->lines as $line) {
10100 $keyforparent = $this->fk_element;
10101 $line->$keyforparent = $this->id;
10102
10103 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
10104 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
10105 if (!is_object($line)) {
10106 $line = (object) $line;
10107 }
10108
10109 $result = 0;
10110 if (method_exists($line, 'insert')) {
10111 $result = $line->insert($user, 1);
10112 } elseif (method_exists($line, 'create')) {
10113 $result = $line->create($user, 1);
10114 }
10115 if ($result < 0) {
10116 $this->error = $line->error;
10117 $this->db->rollback();
10118 return -1;
10119 }
10120 }
10121 }
10122
10123 // Triggers
10124 if (!$error && !$notrigger) {
10125 // Call triggers
10126 $result = $this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user);
10127 if ($result < 0) {
10128 $error++;
10129 }
10130 // End call triggers
10131 }
10132
10133 // Commit or rollback
10134 if ($error) {
10135 $this->db->rollback();
10136 return -1;
10137 } else {
10138 $this->db->commit();
10139 return $this->id;
10140 }
10141 }
10142
10143
10153 public function fetchCommon($id, $ref = null, $morewhere = '', $noextrafields = 0)
10154 {
10155 if (empty($id) && empty($ref) && empty($morewhere)) {
10156 return -1;
10157 }
10158
10159 $fieldlist = $this->getFieldList('t');
10160 if (empty($fieldlist)) {
10161 return 0;
10162 }
10163
10164 $sql = "SELECT ".$fieldlist;
10165 $sql .= " FROM ".$this->db->prefix().$this->table_element.' as t';
10166
10167 if (!empty($id)) {
10168 $sql .= ' WHERE t.rowid = '.((int) $id);
10169 } elseif (!empty($ref)) {
10170 $sql .= " WHERE t.ref = '".$this->db->escape($ref)."'";
10171 } else {
10172 $sql .= ' WHERE 1 = 1'; // usage with empty id and empty ref is very rare
10173 }
10174 if (empty($id) && isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
10175 $sql .= ' AND t.entity IN ('.getEntity($this->element).')';
10176 }
10177 if ($morewhere) {
10178 $sql .= $morewhere;
10179 }
10180 $sql .= ' LIMIT 1'; // This is a fetch, to be certain to get only one record
10181
10182 $res = $this->db->query($sql);
10183 if ($res) {
10184 $obj = $this->db->fetch_object($res);
10185 if ($obj) {
10186 $this->setVarsFromFetchObj($obj);
10187
10188 // Retrieve all extrafield
10189 // fetch optionals attributes and labels
10190 if (empty($noextrafields)) {
10191 $result = $this->fetch_optionals();
10192 if ($result < 0) {
10193 $this->error = $this->db->lasterror();
10194 $this->errors[] = $this->error;
10195 return -4;
10196 }
10197 }
10198
10199 return $this->id;
10200 } else {
10201 return 0;
10202 }
10203 } else {
10204 $this->error = $this->db->lasterror();
10205 $this->errors[] = $this->error;
10206 return -1;
10207 }
10208 }
10209
10217 public function fetchLinesCommon($morewhere = '', $noextrafields = 0)
10218 {
10219 $objectlineclassname = get_class($this).'Line';
10220 if (!class_exists($objectlineclassname)) {
10221 $this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
10222 return -1;
10223 }
10224
10225 $objectline = new $objectlineclassname($this->db);
10226 '@phan-var-force CommonObjectLine $objectline';
10227
10228 $sql = "SELECT ".$objectline->getFieldList('l');
10229 $sql .= " FROM ".$this->db->prefix().$objectline->table_element." as l";
10230 $sql .= " WHERE l.fk_".$this->db->escape($this->element)." = ".((int) $this->id);
10231 if ($morewhere) {
10232 $sql .= $morewhere;
10233 }
10234 if (isset($objectline->fields['position'])) {
10235 $sql .= $this->db->order('position', 'ASC');
10236 }
10237
10238 $resql = $this->db->query($sql);
10239 if ($resql) {
10240 $num_rows = $this->db->num_rows($resql);
10241 $i = 0;
10242 while ($i < $num_rows) {
10243 $obj = $this->db->fetch_object($resql);
10244 if ($obj) {
10245 $newline = new $objectlineclassname($this->db);
10246 '@phan-var-force CommonObjectLine $newline';
10247 $newline->setVarsFromFetchObj($obj);
10248
10249 // Note: extrafields load of line not yet supported
10250 /*
10251 if (empty($noextrafields)) {
10252 // Load extrafields of line
10253 }*/
10254
10255 $this->lines[] = $newline;
10256 }
10257 $i++;
10258 }
10259
10260 return 1;
10261 } else {
10262 $this->error = $this->db->lasterror();
10263 $this->errors[] = $this->error;
10264 return -1;
10265 }
10266 }
10267
10275 public function updateCommon(User $user, $notrigger = 0)
10276 {
10277 dol_syslog(get_class($this)."::updateCommon update", LOG_DEBUG);
10278
10279 $error = 0;
10280
10281 $now = dol_now();
10282
10283 // $this->oldcopy should have been set by the caller of update
10284 //if (empty($this->oldcopy)) {
10285 // dol_syslog("this->oldcopy should have been set by the caller of update (here properties were already modified)", LOG_WARNING);
10286 // $this->oldcopy = dol_clone($this, 2);
10287 //}
10288
10289 $fieldvalues = $this->setSaveQuery();
10290
10291 // Note: Here, $fieldvalues contains same keys (or less) that are inside ->fields
10292
10293 if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) {
10294 $fieldvalues['date_modification'] = $this->db->idate($now);
10295 }
10296 if (array_key_exists('fk_user_modif', $fieldvalues) && !($fieldvalues['fk_user_modif'] > 0)) {
10297 $fieldvalues['fk_user_modif'] = $user->id;
10298 }
10299 if (array_key_exists('user_modification_id', $fieldvalues) && !($fieldvalues['user_modification_id'] > 0)) {
10300 $fieldvalues['user_modification_id'] = $user->id;
10301 }
10302 if (array_key_exists('ref', $fieldvalues)) {
10303 $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
10304 }
10305
10306 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
10307
10308 // Add quotes and escape on fields with type string
10309 $keys = array();
10310 $values = array();
10311 $tmp = array();
10312 foreach ($fieldvalues as $k => $v) {
10313 $keys[$k] = $k;
10314 $value = $this->fields[$k];
10315 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
10316 $values[$k] = $this->quote($v, $value);
10317 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
10318 $tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
10319 }
10320
10321 // Clean and check mandatory fields
10322 foreach ($keys as $key) {
10323 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
10324 $values[$key] = ''; // This is an implicit foreign key field
10325 }
10326 if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
10327 $values[$key] = ''; // This is an explicit foreign key field
10328 }
10329
10330 //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
10331 /*
10332 if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
10333 {
10334 $error++;
10335 $this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
10336 }*/
10337 }
10338
10339 $sql = 'UPDATE '.$this->db->prefix().$this->table_element.' SET '.implode(', ', $tmp).' WHERE rowid='.((int) $this->id);
10340
10341 $this->db->begin();
10342
10343 if (!$error) {
10344 $res = $this->db->query($sql);
10345 if (!$res) {
10346 $error++;
10347 $this->errors[] = $this->db->lasterror();
10348 }
10349 }
10350
10351 // Update extrafield
10352 if (!$error) {
10353 $result = $this->insertExtraFields(); // This delete and reinsert extrafields
10354 if ($result < 0) {
10355 $error++;
10356 }
10357 }
10358
10359 // Triggers
10360 if (!$error && !$notrigger) {
10361 // Call triggers
10362 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
10363 if ($result < 0) {
10364 $error++;
10365 } //Do also here what you must do to rollback action if trigger fail
10366 // End call triggers
10367 }
10368
10369 // Commit or rollback
10370 if ($error) {
10371 $this->db->rollback();
10372 return -1;
10373 } else {
10374 $this->db->commit();
10375 return $this->id;
10376 }
10377 }
10378
10387 public function deleteCommon(User $user, $notrigger = 0, $forcechilddeletion = 0)
10388 {
10389 dol_syslog(get_class($this)."::deleteCommon delete", LOG_DEBUG);
10390
10391 $error = 0;
10392
10393 $this->db->begin();
10394
10395 if ($forcechilddeletion) { // Force also delete of childtables that should lock deletion in standard case when option force is off
10396 foreach ($this->childtables as $table) {
10397 $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
10398 $resql = $this->db->query($sql);
10399 if (!$resql) {
10400 $this->error = $this->db->lasterror();
10401 $this->errors[] = $this->error;
10402 $this->db->rollback();
10403 return -1;
10404 }
10405 }
10406 } elseif (!empty($this->childtables)) { // If object has children linked with a foreign key field, we check all child tables.
10407 $objectisused = $this->isObjectUsed($this->id);
10408 if (!empty($objectisused)) {
10409 dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
10410 $this->error = 'ErrorRecordHasChildren';
10411 $this->errors[] = $this->error;
10412 $this->db->rollback();
10413 return 0;
10414 }
10415 }
10416
10417 // Delete cascade first
10418 if (is_array($this->childtablesoncascade) && !empty($this->childtablesoncascade)) {
10419 foreach ($this->childtablesoncascade as $tabletodelete) {
10420 $deleteFromObject = explode(':', $tabletodelete, 4);
10421 if (count($deleteFromObject) >= 2) {
10422 $className = str_replace('@', '', $deleteFromObject[0]);
10423 $filePath = $deleteFromObject[1];
10424 $columnName = $deleteFromObject[2];
10425 $filter = '';
10426 if (!empty($deleteFromObject[3])) {
10427 $filter = $deleteFromObject[3];
10428 }
10429 if (dol_include_once($filePath)) {
10430 $childObject = new $className($this->db);
10431 if (method_exists($childObject, 'deleteByParentField')) {
10432 '@phan-var-force CommonObject $childObject';
10433 $result = $childObject->deleteByParentField($this->id, $columnName, $filter);
10434 if ($result < 0) {
10435 $error++;
10436 $this->errors[] = $childObject->error;
10437 break;
10438 }
10439 } else {
10440 $error++;
10441 $this->errors[] = "You defined a cascade delete on an object $className/$this->id but there is no method deleteByParentField for it";
10442 break;
10443 }
10444 } else {
10445 $error++;
10446 $this->errors[] = 'Cannot include child class file '.$filePath;
10447 break;
10448 }
10449 } else {
10450 // Delete record in child table
10451 $sql = "DELETE FROM ".$this->db->prefix().$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
10452
10453 $resql = $this->db->query($sql);
10454 if (!$resql) {
10455 $error++;
10456 $this->error = $this->db->lasterror();
10457 $this->errors[] = $this->error;
10458 break;
10459 }
10460 }
10461 }
10462 }
10463
10464 if (!$error) {
10465 if (!$notrigger) {
10466 // Call triggers
10467 $result = $this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
10468 if ($result < 0) {
10469 $error++;
10470 } // Do also here what you must do to rollback action if trigger fail
10471 // End call triggers
10472 }
10473 }
10474
10475 // Delete llx_ecm_files
10476 if (!$error) {
10477 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
10478 if (!$res) {
10479 $error++;
10480 }
10481 }
10482
10483 // Delete linked object
10484 $res = $this->deleteObjectLinked();
10485 if ($res < 0) {
10486 $error++;
10487 }
10488
10489 if (!$error && !empty($this->isextrafieldmanaged)) {
10490 $result = $this->deleteExtraFields();
10491 if ($result < 0) {
10492 $error++;
10493 }
10494 }
10495
10496 if (!$error) {
10497 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.' WHERE rowid='.((int) $this->id);
10498
10499 $resql = $this->db->query($sql);
10500 if (!$resql) {
10501 $error++;
10502 $this->errors[] = $this->db->lasterror();
10503 }
10504 }
10505
10506 // Commit or rollback
10507 if ($error) {
10508 $this->db->rollback();
10509 return -1;
10510 } else {
10511 $this->db->commit();
10512 return 1;
10513 }
10514 }
10515
10527 public function deleteByParentField($parentId = 0, $parentField = '', $filter = '', $filtermode = "AND")
10528 {
10529 global $user;
10530
10531 $error = 0;
10532 $deleted = 0;
10533
10534 if (!empty($parentId) && !empty($parentField)) {
10535 $this->db->begin();
10536
10537 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
10538 $sql .= " WHERE ".$this->db->sanitize($parentField)." = ".(int) $parentId;
10539
10540 // Manage filter
10541 $errormessage = '';
10542 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
10543 if ($errormessage) {
10544 $this->errors[] = $errormessage;
10545 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
10546 return -1;
10547 }
10548
10549 $resql = $this->db->query($sql);
10550 if (!$resql) {
10551 $this->errors[] = $this->db->lasterror();
10552 $error++;
10553 } else {
10554 while ($obj = $this->db->fetch_object($resql)) {
10555 $result = $this->fetch($obj->rowid); // @phpstan-ignore-line
10556 if ($result < 0) {
10557 $error++;
10558 $this->errors[] = $this->error;
10559 } else {
10560 $result = $this->delete($user); // @phpstan-ignore-line
10561 if ($result < 0) {
10562 $error++;
10563 $this->errors[] = $this->error;
10564 } else {
10565 $deleted++;
10566 }
10567 }
10568 }
10569 }
10570
10571 if (empty($error)) {
10572 $this->db->commit();
10573 return $deleted;
10574 } else {
10575 $this->error = implode(', ', $this->errors);
10576 $this->db->rollback();
10577 return $error * -1;
10578 }
10579 }
10580
10581 return $deleted;
10582 }
10583
10592 public function deleteLineCommon(User $user, $idline, $notrigger = 0)
10593 {
10594 $error = 0;
10595
10596 $tmpforobjectclass = get_class($this);
10597 $tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line';
10598
10599 $this->db->begin();
10600
10601 // Call trigger
10602 $result = $this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user);
10603 if ($result < 0) {
10604 $error++;
10605 }
10606 // End call triggers
10607
10608 if (empty($error)) {
10609 $sql = "DELETE FROM ".$this->db->prefix().$this->table_element_line;
10610 $sql .= " WHERE rowid = ".((int) $idline);
10611
10612 $resql = $this->db->query($sql);
10613 if (!$resql) {
10614 $this->error = "Error ".$this->db->lasterror();
10615 $error++;
10616 }
10617 }
10618
10619 if (empty($error)) {
10620 // Remove extrafields
10621 $tmpobjectline = new $tmpforobjectlineclass($this->db);
10622 '@phan-var-force CommonObjectLine $tmpobjectline';
10623 if (!isset($tmpobjectline->isextrafieldmanaged) || !empty($tmpobjectline->isextrafieldmanaged)) {
10624 $tmpobjectline->id = $idline;
10625 $result = $tmpobjectline->deleteExtraFields();
10626 if ($result < 0) {
10627 $error++;
10628 $this->error = "Error ".get_class($this)."::deleteLineCommon deleteExtraFields error -4 ".$tmpobjectline->error;
10629 }
10630 }
10631 }
10632
10633 if (empty($error)) {
10634 $this->db->commit();
10635 return 1;
10636 } else {
10637 dol_syslog(get_class($this)."::deleteLineCommon ERROR:".$this->error, LOG_ERR);
10638 $this->db->rollback();
10639 return -1;
10640 }
10641 }
10642
10643
10653 public function setStatusCommon($user, $status, $notrigger = 0, $triggercode = '')
10654 {
10655 $error = 0;
10656
10657 $this->db->begin();
10658
10659 $statusfield = 'status';
10660 if (in_array($this->element, array('don', 'donation', 'shipping'))) {
10661 $statusfield = 'fk_statut';
10662 }
10663
10664 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
10665 $sql .= " SET ".$statusfield." = ".((int) $status);
10666 $sql .= " WHERE rowid = ".((int) $this->id);
10667
10668 if ($this->db->query($sql)) {
10669 if (!$error) {
10670 $this->oldcopy = clone $this;
10671 }
10672
10673 if (!$error && !$notrigger) {
10674 // Call trigger
10675 $result = $this->call_trigger($triggercode, $user);
10676 if ($result < 0) {
10677 $error++;
10678 }
10679 }
10680
10681 if (!$error) {
10682 $this->status = $status;
10683 if (property_exists($this, 'statut')) { // For backward compatibility
10684 $this->statut = $status;
10685 }
10686 $this->db->commit();
10687 return 1;
10688 } else {
10689 $this->db->rollback();
10690 return -1;
10691 }
10692 } else {
10693 $this->error = $this->db->error();
10694 $this->db->rollback();
10695 return -1;
10696 }
10697 }
10698
10708 public function setSignedStatusCommon($user, $status, $notrigger = 0, $triggercode = '')
10709 {
10710 $error = 0;
10711
10712 $this->db->begin();
10713
10714 $statusfield = 'signed_status';
10715
10716 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
10717 $sql .= " SET ".$statusfield." = ".((int) $status);
10718 $sql .= " WHERE rowid = ".((int) $this->id);
10719
10720 if ($this->db->query($sql)) {
10721 if (!$error) {
10722 $this->oldcopy = clone $this;
10723 }
10724
10725 if (!$error && !$notrigger) {
10726 // Call trigger
10727 $result = $this->call_trigger($triggercode, $user);
10728 if ($result < 0) {
10729 $error++;
10730 }
10731 }
10732
10733 if (!$error) {
10734 $this->status = $status;
10735 $this->db->commit();
10736 return 1;
10737 } else {
10738 $this->db->rollback();
10739 return -1;
10740 }
10741 } else {
10742 $this->error = $this->db->error();
10743 $this->db->rollback();
10744 return -1;
10745 }
10746 }
10747
10748
10755 public function initAsSpecimenCommon()
10756 {
10757 global $user;
10758
10759 $this->id = 0;
10760 $this->specimen = 1;
10761 $fields = array(
10762 'label' => 'This is label',
10763 'ref' => 'ABCD1234',
10764 'description' => 'This is a description',
10765 'qty' => 123.12,
10766 'note_public' => 'Public note',
10767 'note_private' => 'Private note',
10768 'date_creation' => (dol_now() - 3600 * 48),
10769 'date_modification' => (dol_now() - 3600 * 24),
10770 'fk_user_creat' => $user->id,
10771 'fk_user_modif' => $user->id,
10772 'date' => dol_now(),
10773 );
10774 foreach ($fields as $key => $value) {
10775 if (array_key_exists($key, $this->fields)) {
10776 $this->{$key} = $value; // @phpstan-ignore-line
10777 }
10778 }
10779
10780 // Force values to default values when known
10781 if (property_exists($this, 'fields')) {
10782 foreach ($this->fields as $key => $value) {
10783 // If fields are already set, do nothing
10784 if (array_key_exists($key, $fields)) {
10785 continue;
10786 }
10787
10788 if (!empty($value['default'])) {
10789 $this->$key = $value['default'];
10790 }
10791 }
10792 }
10793
10794 return 1;
10795 }
10796
10797
10798 /* Part for comments */
10799
10805 public function fetchComments()
10806 {
10807 require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
10808
10809 $comment = new Comment($this->db);
10810 $result = $comment->fetchAllFor($this->element, $this->id);
10811 if ($result < 0) {
10812 $this->errors = array_merge($this->errors, $comment->errors);
10813 return -1;
10814 } else {
10815 $this->comments = $comment->comments;
10816 }
10817 return count($this->comments);
10818 }
10819
10825 public function getNbComments()
10826 {
10827 return count($this->comments);
10828 }
10829
10836 public function trimParameters($parameters)
10837 {
10838 if (!is_array($parameters)) {
10839 return;
10840 }
10841 foreach ($parameters as $parameter) {
10842 if (isset($this->$parameter)) {
10843 $this->$parameter = trim($this->$parameter);
10844 }
10845 }
10846 }
10847
10848 /* Part for categories/tags */
10849
10860 public function getCategoriesCommon($type_categ)
10861 {
10862 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10863
10864 // Get current categories
10865 $c = new Categorie($this->db);
10866 $existing = $c->containing($this->id, $type_categ, 'id');
10867
10868 return $existing;
10869 }
10870
10883 public function setCategoriesCommon($categories, $type_categ = '', $remove_existing = true)
10884 {
10885 // Handle single category
10886 if (!is_array($categories)) {
10887 $categories = array($categories);
10888 }
10889
10890 dol_syslog(get_class($this)."::setCategoriesCommon Object Id:".$this->id.' type_categ:'.$type_categ.' nb tag add:'.count($categories), LOG_DEBUG);
10891
10892 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10893
10894 if (empty($type_categ)) {
10895 dol_syslog(__METHOD__.': Type '.$type_categ.'is an unknown category type. Done nothing.', LOG_ERR);
10896 return -1;
10897 }
10898
10899 // Get current categories
10900 $c = new Categorie($this->db);
10901 $existing = $c->containing($this->id, $type_categ, 'id');
10902 if ($remove_existing) {
10903 // Diff
10904 if (is_array($existing)) {
10905 $to_del = array_diff($existing, $categories);
10906 $to_add = array_diff($categories, $existing);
10907 } else {
10908 $to_del = array(); // Nothing to delete
10909 $to_add = $categories;
10910 }
10911 } else {
10912 $to_del = array(); // Nothing to delete
10913 $to_add = array_diff($categories, $existing);
10914 }
10915
10916 $error = 0;
10917 $ok = 0;
10918
10919 // Process
10920 foreach ($to_del as $del) {
10921 if ($c->fetch($del) > 0) {
10922 $result = $c->del_type($this, $type_categ);
10923 if ($result < 0) {
10924 $error++;
10925 $this->error = $c->error;
10926 $this->errors = $c->errors;
10927 break;
10928 } else {
10929 $ok += $result;
10930 }
10931 }
10932 }
10933 foreach ($to_add as $add) {
10934 if ($c->fetch($add) > 0) {
10935 $result = $c->add_type($this, $type_categ);
10936 if ($result < 0) {
10937 $error++;
10938 $this->error = $c->error;
10939 $this->errors = $c->errors;
10940 break;
10941 } else {
10942 $ok += $result;
10943 }
10944 }
10945 }
10946
10947 return $error ? (-1 * $error) : $ok;
10948 }
10949
10958 public function cloneCategories($fromId, $toId, $type = '')
10959 {
10960 $this->db->begin();
10961
10962 if (empty($type)) {
10963 $type = $this->table_element;
10964 }
10965
10966 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10967 $categorystatic = new Categorie($this->db);
10968
10969 $sql = "INSERT INTO ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type])." (fk_categorie, fk_product)";
10970 $sql .= " SELECT fk_categorie, $toId FROM ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type]);
10971 $sql .= " WHERE fk_product = ".((int) $fromId);
10972
10973 if (!$this->db->query($sql)) {
10974 $this->error = $this->db->lasterror();
10975 $this->db->rollback();
10976 return -1;
10977 }
10978
10979 $this->db->commit();
10980 return 1;
10981 }
10982
10989 public function deleteEcmFiles($mode = 0)
10990 {
10991 global $conf;
10992
10993 $this->db->begin();
10994
10995 // Delete in database with mode 0
10996 if ($mode == 0) {
10997 switch ($this->element) {
10998 case 'propal':
10999 $element = 'propale';
11000 break;
11001 case 'product':
11002 $element = 'produit';
11003 break;
11004 case 'order_supplier':
11005 $element = 'fournisseur/commande';
11006 break;
11007 case 'invoice_supplier':
11008 // Special cases that need to use get_exdir to get real dir of object
11009 // In future, all object should use this to define path of documents.
11010 $element = 'fournisseur/facture/'.get_exdir($this->id, 2, 0, 1, $this, 'invoice_supplier');
11011 break;
11012 case 'shipping':
11013 $element = 'expedition/sending';
11014 break;
11015 case 'task':
11016 case 'project_task':
11017 require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
11018
11019 $project_result = $this->fetch_projet();
11020 if ($project_result >= 0) {
11021 $element = 'projet/'.dol_sanitizeFileName($this->project->ref).'/';
11022 }
11023 // no break
11024 default:
11025 $element = $this->element;
11026 }
11027 '@phan-var-force string $element';
11028
11029 // Delete ecm_files_extrafields with mode 0 (using name)
11030 $sql = "DELETE FROM ".$this->db->prefix()."ecm_files_extrafields WHERE fk_object IN (";
11031 $sql .= " SELECT rowid FROM ".$this->db->prefix()."ecm_files WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
11032 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
11033 $sql .= ")";
11034
11035 if (!$this->db->query($sql)) {
11036 $this->error = $this->db->lasterror();
11037 $this->db->rollback();
11038 return false;
11039 }
11040
11041 // Delete ecm_files with mode 0 (using name)
11042 $sql = "DELETE FROM ".$this->db->prefix()."ecm_files";
11043 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
11044 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
11045
11046 if (!$this->db->query($sql)) {
11047 $this->error = $this->db->lasterror();
11048 $this->db->rollback();
11049 return false;
11050 }
11051 }
11052
11053 // Delete in database with mode 1
11054 if ($mode == 1) {
11055 $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files_extrafields";
11056 $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).")";
11057 $resql = $this->db->query($sql);
11058 if (!$resql) {
11059 $this->error = $this->db->lasterror();
11060 $this->db->rollback();
11061 return false;
11062 }
11063
11064 $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files";
11065 $sql .= " WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? "" : "@".$this->module))."' AND src_object_id = ".((int) $this->id);
11066 $resql = $this->db->query($sql);
11067 if (!$resql) {
11068 $this->error = $this->db->lasterror();
11069 $this->db->rollback();
11070 return false;
11071 }
11072 }
11073
11074 $this->db->commit();
11075 return true;
11076 }
11077}
if( $user->socid > 0) if(! $user->hasRight('accounting', 'chartofaccount')) $object
Definition card.php:58
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition security.php:626
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:456
$object ref
Definition info.php:79
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.
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.
addThumbs($file)
Build thumb.
setSaveQuery()
Function to return the array of data key-value from the ->fields and all the ->properties of an objec...
setValuesForExtraLanguages($onlykey='')
Fill array_options property of object by extrafields value (using for data sent by forms)
insertExtraLanguages($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
setTransportMode($id)
Change the transport mode methods.
isArray($info)
Function test if type is array.
isInt($info)
Function test if type is integer.
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.
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.
setSignedStatusCommon($user, $status, $notrigger=0, $triggercode='')
Set to a signed status.
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.
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.
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.
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:241
dol_meta_create($object)
Create a meta file with document file into same directory.
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
completeFileArrayWithDatabaseInfo(&$filearray, $relativedir)
Complete $filearray with data from database.
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...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
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.
dol_print_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0, $morecss='')
Format phone numbers according to country.
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.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
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.
yn($yesno, $case=1, $color=0)
Return yes or no in current language.
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 '.
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.
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_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0)
Show EMail link formatted for HTML output.
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.
getImageFileNameForSize($file, $extName, $extImgTarget='')
Return the filename of file to get the thumbs.
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.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_sanitizeFileName($str, $newstr='_', $unaccent=1)
Clean a string to use it as a file name.
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...
getDolGlobalString($key, $default='')
Return 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.
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:88
$conf db user
Active Directory does not allow anonymous connections.
Definition repair.php:143
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:139
getRandomPassword($generic=false, $replaceambiguouschars=null, $length=32)
Return a generated password using default module.
dolEncrypt($chain, $key='', $ciphering='AES-256-CTR', $forceseed='')
Encode a string with a symmetric encryption.
dolDecrypt($chain, $key='')
Decode a string with a symmetric encryption.
dol_hash($chain, $type='0', $nosalt=0)
Returns a hash (non reversible encryption) of a string.
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e e e e e statut
Definition invoice.php:1929
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall TAKEPOS_SHOW_SUBPRICE right right right takeposterminal SELECT e rowid
Definition invoice.php:1929