dolibarr 20.0.2
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, tc.module";
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->module ?? $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 false,
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 false,
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 (!empty($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 $fieldup = 'value_unit';
3859 $base_price_type = 'TTC';
3860 }
3861
3862 $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,";
3863 $sql .= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
3864 if ($this->table_element_line == 'facturedet') {
3865 $sql .= ', situation_percent';
3866 }
3867 $sql .= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
3868 $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
3869 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3870 if ($exclspec) {
3871 $product_field = 'product_type';
3872 if ($this->table_element_line == 'contratdet') {
3873 $product_field = ''; // contratdet table has no product_type field
3874 }
3875 if ($product_field) {
3876 $sql .= " AND ".$product_field." <> 9";
3877 }
3878 }
3879 $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
3880
3881 dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3882
3883 $resql = $this->db->query($sql);
3884 if ($resql) {
3885 $this->total_ht = 0;
3886 $this->total_tva = 0;
3887 $this->total_localtax1 = 0;
3888 $this->total_localtax2 = 0;
3889 $this->total_ttc = 0;
3890 $total_ht_by_vats = array();
3891 $total_tva_by_vats = array();
3892 $total_ttc_by_vats = array();
3893 $this->multicurrency_total_ht = 0;
3894 $this->multicurrency_total_tva = 0;
3895 $this->multicurrency_total_ttc = 0;
3896
3897 $this->db->begin();
3898
3899 $num = $this->db->num_rows($resql);
3900 $i = 0;
3901 while ($i < $num) {
3902 $obj = $this->db->fetch_object($resql);
3903
3904 // Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
3905 $parameters = array('fk_element' => $obj->rowid);
3906 $reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3907
3908 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'
3909 // This part of code is to fix data. We should not call it too often.
3910 $localtax_array = array($obj->localtax1_type, $obj->localtax1_tx, $obj->localtax2_type, $obj->localtax2_tx);
3911 $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);
3912
3913 $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.
3914 $diff_on_current_total = price2num($obj->total_ttc - $obj->total_ht - $obj->total_tva - $obj->total_localtax1 - $obj->total_localtax2, 'MT', 1);
3915 //var_dump($obj->total_ht.' '.$obj->total_tva.' '.$obj->total_localtax1.' '.$obj->total_localtax2.' => '.$obj->total_ttc);
3916 //var_dump($diff_when_using_price_ht.' '.$diff_on_current_total);
3917
3918 if ($diff_on_current_total) {
3919 // This should not happen, we should always have in table: total_ttc = total_ht + total_vat + total_localtax1 + total_localtax2
3920 $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line;
3921 $sqlfix .= " SET ".$fieldtva." = ".price2num((float) $tmpcal[1]).", total_ttc = ".price2num((float) $tmpcal[2]);
3922 $sqlfix .= ", multicurrency_total_tva = ".price2num((float) $tmpcal[17]).", multicurrency_total_ttc = ".price2num((float) $tmpcal[18]);
3923 $sqlfix .= " WHERE rowid = ".((int) $obj->rowid);
3924 dol_syslog('Warn1: We found inconsistent data into detailed line (diff_on_current_total = '.$diff_on_current_total.') for line rowid = '.$obj->rowid." (ht=".$obj->total_ht." vat=".$obj->total_tva." tax1=".$obj->total_localtax1." tax2=".$obj->total_localtax2." ttc=".$obj->total_ttc."). We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix, LOG_WARNING);
3925 $resqlfix = $this->db->query($sqlfix);
3926 if (!$resqlfix) {
3927 dol_print_error($this->db, 'Failed to update line');
3928 }
3929 $obj->total_tva = $tmpcal[1];
3930 $obj->total_ttc = $tmpcal[2];
3931 $obj->multicurrency_total_tva = $tmpcal[17];
3932 $obj->multicurrency_total_ttc = $tmpcal[18];
3933 } elseif ($diff_when_using_price_ht) {
3934 // If total_ht calculated from unit price is different than the one in database, we do nothing, this may be a regular case to have also a different VAT, that can be explained
3935 // because price was entered included tax and we round the unit price without tax to store it in database (so recalculation will give different results).
3936 if ((float) $tmpcal[0] == (float) $obj->total_ht) {
3937 // After calculation from HT, total is consistent and total_ht is same, but we have found a difference between VAT part calculated from unit price and the VAT part into database,
3938 // and we ask to force the use of rounding on line (like done on calculation) so this should not happen, so we force the update of line to fix.
3939
3940 // This part of code must be called only to fix corrupted data due to the use of the feature to round total instead of rounding lines.
3941 $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line;
3942 $sqlfix .= " SET ".$fieldtva." = ".price2num((float) $tmpcal[1]).", total_ttc = ".price2num((float) $tmpcal[2]);
3943 $sqlfix .= ", multicurrency_total_tva = ".price2num((float) $tmpcal[17]).", multicurrency_total_ttc = ".price2num((float) $tmpcal[18]);
3944 $sqlfix .= " WHERE rowid = ".((int) $obj->rowid);
3945 dol_syslog('Warn2: We found a line with different rounding data into detailed line (diff_when_using_price_ht = '.$diff_when_using_price_ht.' and diff_on_current_total = '.$diff_on_current_total.') for line rowid = '.$obj->rowid." (total vat of line calculated=".$tmpcal[1].", database=".$obj->total_tva."). We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix);
3946 $resqlfix = $this->db->query($sqlfix);
3947 if (!$resqlfix) {
3948 dol_print_error($this->db, 'Failed to update line');
3949 }
3950 $obj->total_tva = $tmpcal[1];
3951 $obj->total_ttc = $tmpcal[2];
3952 $obj->multicurrency_total_tva = $tmpcal[17];
3953 $obj->multicurrency_total_ttc = $tmpcal[18];
3954 }
3955 }
3956 }
3957
3958 $this->total_ht += $obj->total_ht; // The field visible at end of line detail
3959 $this->total_tva += $obj->total_tva;
3960 $this->total_localtax1 += $obj->total_localtax1;
3961 $this->total_localtax2 += $obj->total_localtax2;
3962 $this->total_ttc += $obj->total_ttc;
3963 $this->multicurrency_total_ht += $obj->multicurrency_total_ht; // The field visible at end of line detail
3964 $this->multicurrency_total_tva += $obj->multicurrency_total_tva;
3965 $this->multicurrency_total_ttc += $obj->multicurrency_total_ttc;
3966
3967 if (!isset($total_ht_by_vats[$obj->vatrate])) {
3968 $total_ht_by_vats[$obj->vatrate] = 0;
3969 }
3970 if (!isset($total_tva_by_vats[$obj->vatrate])) {
3971 $total_tva_by_vats[$obj->vatrate] = 0;
3972 }
3973 if (!isset($total_ttc_by_vats[$obj->vatrate])) {
3974 $total_ttc_by_vats[$obj->vatrate] = 0;
3975 }
3976 $total_ht_by_vats[$obj->vatrate] += $obj->total_ht;
3977 $total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
3978 $total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
3979
3980 if ($forcedroundingmode == '1') { // Check if we need adjustment onto line for vat. TODO This works on the company currency but not on foreign currency
3981 $tmpvat = price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
3982 $diff = price2num($total_tva_by_vats[$obj->vatrate] - (float) $tmpvat, 'MT', 1);
3983 //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";
3984 if ($diff) {
3985 $maxdiff = (10 * pow(10, -1 * getDolGlobalInt('MAIN_MAX_DECIMALS_TOT', 0)));
3986 if (abs((float) $diff) > $maxdiff) {
3987 // If error is more than 10 times the accuracy of rounding. This should not happen.
3988 $errmsg = 'We found a rounding difference after line '.($obj->rowid).' between HT*VAT='.$tmpvat.' and total in database='.$total_tva_by_vats[$obj->vatrate].' (calculated with UP*qty) but diff='.$diff.' is too high (> '.$maxdiff.') to be corrected. Some data in your lines may be corrupted. Try to edit each line manually to fix this before restarting.';
3989 dol_syslog($errmsg, LOG_WARNING);
3990 $this->error = $errmsg;
3991 $error++;
3992 break;
3993 }
3994 $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);
3995 dol_syslog('We found a difference of '.$diff.' for line rowid = '.$obj->rowid.' between TotalHT('.$total_ht_by_vats[$obj->vatrate].')*VATrate('.$obj->vatrate.')='.$tmpvat.' and total in database='.$total_tva_by_vats[$obj->vatrate]." (calculated with UP*qty). We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix);
3996
3997 $resqlfix = $this->db->query($sqlfix);
3998
3999 if (!$resqlfix) {
4000 dol_print_error($this->db, 'Failed to update line');
4001 }
4002
4003 $this->total_tva = (float) price2num($this->total_tva - (float) $diff, '', 1);
4004 $this->total_ttc = (float) price2num($this->total_ttc - (float) $diff, '', 1);
4005 $total_tva_by_vats[$obj->vatrate] = (float) price2num($total_tva_by_vats[$obj->vatrate] - (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 $i++;
4011 }
4012
4013 // Add revenue stamp to total
4014 $this->total_ttc += isset($this->revenuestamp) ? $this->revenuestamp : 0;
4015 $this->multicurrency_total_ttc += isset($this->revenuestamp) ? ($this->revenuestamp * $multicurrency_tx) : 0;
4016
4017 // Situations totals
4018 if (!empty($this->situation_cycle_ref) && !empty($this->situation_counter) && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits')) {
4019 include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
4020 if ($this->type != Facture::TYPE_CREDIT_NOTE) { // @phpstan-ignore-line
4021 if (getDolGlobalInt('INVOICE_USE_SITUATION') != 2) {
4022 $prev_sits = $this->get_prev_sits();
4023
4024 foreach ($prev_sits as $sit) { // $sit is an object Facture loaded with a fetch.
4025 $this->total_ht -= $sit->total_ht;
4026 $this->total_tva -= $sit->total_tva;
4027 $this->total_localtax1 -= $sit->total_localtax1;
4028 $this->total_localtax2 -= $sit->total_localtax2;
4029 $this->total_ttc -= $sit->total_ttc;
4030 $this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
4031 $this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
4032 $this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
4033 }
4034 }
4035 }
4036 }
4037
4038 // Clean total
4039 $this->total_ht = (float) price2num($this->total_ht);
4040 $this->total_tva = (float) price2num($this->total_tva);
4041 $this->total_localtax1 = (float) price2num($this->total_localtax1);
4042 $this->total_localtax2 = (float) price2num($this->total_localtax2);
4043 $this->total_ttc = (float) price2num($this->total_ttc);
4044
4045 $this->db->free($resql);
4046
4047 // Now update global fields total_ht, total_ttc, total_tva, total_localtax1, total_localtax2, multicurrency_total_* of main object
4048 $fieldht = 'total_ht';
4049 $fieldtva = 'tva';
4050 $fieldlocaltax1 = 'localtax1';
4051 $fieldlocaltax2 = 'localtax2';
4052 $fieldttc = 'total_ttc';
4053 // Specific code for backward compatibility with old field names
4054 if (in_array($this->element, array('propal', 'commande', 'facture', 'facturerec', 'supplier_proposal', 'order_supplier', 'facture_fourn', 'invoice_supplier', 'invoice_supplier_rec', 'expensereport'))) {
4055 $fieldtva = 'total_tva';
4056 }
4057
4058 if (!$error && empty($nodatabaseupdate)) {
4059 $sql = "UPDATE ".$this->db->prefix().$this->table_element.' SET';
4060 $sql .= " ".$fieldht." = ".((float) price2num($this->total_ht, 'MT', 1)).",";
4061 $sql .= " ".$fieldtva." = ".((float) price2num($this->total_tva, 'MT', 1)).",";
4062 $sql .= " ".$fieldlocaltax1." = ".((float) price2num($this->total_localtax1, 'MT', 1)).",";
4063 $sql .= " ".$fieldlocaltax2." = ".((float) price2num($this->total_localtax2, 'MT', 1)).",";
4064 $sql .= " ".$fieldttc." = ".((float) price2num($this->total_ttc, 'MT', 1));
4065 $sql .= ", multicurrency_total_ht = ".((float) price2num($this->multicurrency_total_ht, 'MT', 1));
4066 $sql .= ", multicurrency_total_tva = ".((float) price2num($this->multicurrency_total_tva, 'MT', 1));
4067 $sql .= ", multicurrency_total_ttc = ".((float) price2num($this->multicurrency_total_ttc, 'MT', 1));
4068 $sql .= " WHERE rowid = ".((int) $this->id);
4069
4070 dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
4071 $resql = $this->db->query($sql);
4072
4073 if (!$resql) {
4074 $error++;
4075 $this->error = $this->db->lasterror();
4076 $this->errors[] = $this->db->lasterror();
4077 }
4078 }
4079
4080 if (!$error) {
4081 $this->db->commit();
4082 return 1;
4083 } else {
4084 $this->db->rollback();
4085 return -1;
4086 }
4087 } else {
4088 dol_print_error($this->db, 'Bad request in update_price');
4089 return -1;
4090 }
4091 }
4092
4093 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4104 public function add_object_linked($origin = null, $origin_id = null, $f_user = null, $notrigger = 0)
4105 {
4106 // phpcs:enable
4107 global $user, $hookmanager, $action;
4108 $origin = (!empty($origin) ? $origin : $this->origin);
4109 $origin_id = (!empty($origin_id) ? $origin_id : $this->origin_id);
4110 $f_user = isset($f_user) ? $f_user : $user;
4111
4112 // Special case
4113 if ($origin == 'order') {
4114 $origin = 'commande';
4115 }
4116 if ($origin == 'invoice') {
4117 $origin = 'facture';
4118 }
4119 if ($origin == 'invoice_template') {
4120 $origin = 'facturerec';
4121 }
4122 if ($origin == 'supplierorder') {
4123 $origin = 'order_supplier';
4124 }
4125
4126 // Add module part to target type
4127 $targettype = $this->getElementType();
4128
4129 $parameters = array('targettype' => $targettype);
4130 // Hook for explicitly set the targettype if it must be different than $this->element
4131 $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4132 if ($reshook > 0) {
4133 if (!empty($hookmanager->resArray['targettype'])) {
4134 $targettype = $hookmanager->resArray['targettype'];
4135 }
4136 }
4137
4138 $this->db->begin();
4139 $error = 0;
4140
4141 $sql = "INSERT INTO " . $this->db->prefix() . "element_element (";
4142 $sql .= "fk_source";
4143 $sql .= ", sourcetype";
4144 $sql .= ", fk_target";
4145 $sql .= ", targettype";
4146 $sql .= ") VALUES (";
4147 $sql .= ((int) $origin_id);
4148 $sql .= ", '" . $this->db->escape($origin) . "'";
4149 $sql .= ", " . ((int) $this->id);
4150 $sql .= ", '" . $this->db->escape($targettype) . "'";
4151 $sql .= ")";
4152
4153 dol_syslog(get_class($this) . "::add_object_linked", LOG_DEBUG);
4154 if ($this->db->query($sql)) {
4155 if (!$notrigger) {
4156 // Call trigger
4157 $this->context['link_origin'] = $origin;
4158 $this->context['link_origin_id'] = $origin_id;
4159 $result = $this->call_trigger('OBJECT_LINK_INSERT', $f_user);
4160 if ($result < 0) {
4161 $error++;
4162 }
4163 // End call triggers
4164 }
4165 } else {
4166 $this->error = $this->db->lasterror();
4167 $error++;
4168 }
4169
4170 if (!$error) {
4171 $this->db->commit();
4172 return 1;
4173 } else {
4174 $this->db->rollback();
4175 return 0;
4176 }
4177 }
4178
4184 public function getElementType()
4185 {
4186 // 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.
4187 // 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).
4188 $coreModule = array('knowledgemanagement', 'partnership', 'workstation', 'ticket', 'recruitment', 'eventorganization', 'asset');
4189 // Add module part to target type if object has $module property and isn't in core modules.
4190 return ((!empty($this->module) && !in_array($this->module, $coreModule)) ? $this->module.'_' : '').$this->element;
4191 }
4192
4193
4216 public function fetchObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1)
4217 {
4218 global $conf, $hookmanager, $action;
4219
4220 // Important for pdf generation time reduction
4221 // This boolean is true if $this->linkedObjects has already been loaded with all objects linked without filter
4222 // If you need to force the reload, you can call clearObjectLinkedCache() before calling fetchObjectLinked()
4223 if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
4224 return 1;
4225 }
4226
4227 $this->linkedObjectsIds = array();
4228 $this->linkedObjects = array();
4229
4230 $justsource = false;
4231 $justtarget = false;
4232 $withtargettype = false;
4233 $withsourcetype = false;
4234
4235 $parameters = array('sourcetype' => $sourcetype, 'sourceid' => $sourceid, 'targettype' => $targettype, 'targetid' => $targetid);
4236 // Hook for explicitly set the targettype if it must be differtent than $this->element
4237 $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4238 if ($reshook > 0) {
4239 if (!empty($hookmanager->resArray['sourcetype'])) {
4240 $sourcetype = $hookmanager->resArray['sourcetype'];
4241 }
4242 if (!empty($hookmanager->resArray['sourceid'])) {
4243 $sourceid = $hookmanager->resArray['sourceid'];
4244 }
4245 if (!empty($hookmanager->resArray['targettype'])) {
4246 $targettype = $hookmanager->resArray['targettype'];
4247 }
4248 if (!empty($hookmanager->resArray['targetid'])) {
4249 $targetid = $hookmanager->resArray['targetid'];
4250 }
4251 }
4252
4253 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid)) {
4254 $justsource = true; // the source (id and type) is a search criteria
4255 if (!empty($targettype)) {
4256 $withtargettype = true;
4257 }
4258 }
4259 if (!empty($targetid) && !empty($targettype) && empty($sourceid)) {
4260 $justtarget = true; // the target (id and type) is a search criteria
4261 if (!empty($sourcetype)) {
4262 $withsourcetype = true;
4263 }
4264 }
4265
4266 $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
4267 $targetid = (!empty($targetid) ? $targetid : $this->id);
4268 $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->getElementType());
4269 $targettype = (!empty($targettype) ? $targettype : $this->getElementType());
4270
4271 /*if (empty($sourceid) && empty($targetid))
4272 {
4273 dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
4274 return -1;
4275 }*/
4276
4277 // Links between objects are stored in table element_element
4278 $sql = "SELECT rowid, fk_source, sourcetype, fk_target, targettype";
4279 $sql .= " FROM ".$this->db->prefix()."element_element";
4280 $sql .= " WHERE ";
4281 if ($justsource || $justtarget) {
4282 if ($justsource) {
4283 $sql .= "fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."'";
4284 if ($withtargettype) {
4285 $sql .= " AND targettype = '".$this->db->escape($targettype)."'";
4286 }
4287 } elseif ($justtarget) {
4288 $sql .= "fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."'";
4289 if ($withsourcetype) {
4290 $sql .= " AND sourcetype = '".$this->db->escape($sourcetype)."'";
4291 }
4292 }
4293 } else {
4294 $sql .= "(fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."')";
4295 $sql .= " ".$clause." (fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."')";
4296 if ($loadalsoobjects && $this->id > 0 && $sourceid == $this->id && $sourcetype == $this->element && $targetid == $this->id && $targettype == $this->element && $clause == 'OR') {
4297 $this->linkedObjectsFullLoaded[$this->id] = true;
4298 }
4299 }
4300 $sql .= " ORDER BY ".$orderby;
4301
4302 dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
4303 $resql = $this->db->query($sql);
4304 if ($resql) {
4305 $num = $this->db->num_rows($resql);
4306 $i = 0;
4307 while ($i < $num) {
4308 $obj = $this->db->fetch_object($resql);
4309 if ($justsource || $justtarget) {
4310 if ($justsource) {
4311 $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
4312 } elseif ($justtarget) {
4313 $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
4314 }
4315 } else {
4316 if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype) {
4317 $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
4318 }
4319 if ($obj->fk_target == $targetid && $obj->targettype == $targettype) {
4320 $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
4321 }
4322 }
4323 $i++;
4324 }
4325
4326 if (!empty($this->linkedObjectsIds)) {
4327 $tmparray = $this->linkedObjectsIds;
4328 foreach ($tmparray as $objecttype => $objectids) { // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
4329 $element_properties = getElementProperties($objecttype);
4330 $element = $element_properties['element'];
4331 $classPath = $element_properties['classpath'];
4332 $classFile = $element_properties['classfile'];
4333 $className = $element_properties['classname'];
4334 $module = $element_properties['module'];
4335
4336 // Here $module, $classFile and $className are set, we can use them.
4337 if (isModEnabled($module) && (($element != $this->element) || $alsosametype)) {
4338 if ($loadalsoobjects && (is_numeric($loadalsoobjects) || ($loadalsoobjects === $objecttype))) {
4339 dol_include_once('/'.$classPath.'/'.$classFile.'.class.php');
4340 if (class_exists($className)) {
4341 foreach ($objectids as $i => $objectid) { // $i is rowid into llx_element_element
4342 $object = new $className($this->db);
4343 $ret = $object->fetch($objectid);
4344 if ($ret >= 0) {
4345 $this->linkedObjects[$objecttype][$i] = $object;
4346 }
4347 }
4348 }
4349 }
4350 } else {
4351 unset($this->linkedObjectsIds[$objecttype]);
4352 }
4353 }
4354 }
4355 return 1;
4356 } else {
4357 dol_print_error($this->db);
4358 return -1;
4359 }
4360 }
4361
4368 public function clearObjectLinkedCache()
4369 {
4370 if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
4371 unset($this->linkedObjectsFullLoaded[$this->id]);
4372 }
4373
4374 return 1;
4375 }
4376
4389 public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $f_user = null, $notrigger = 0)
4390 {
4391 global $user;
4392 $updatesource = false;
4393 $updatetarget = false;
4394 $f_user = isset($f_user) ? $f_user : $user;
4395
4396 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4397 $updatesource = true;
4398 } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4399 $updatetarget = true;
4400 }
4401
4402 $this->db->begin();
4403 $error = 0;
4404
4405 $sql = "UPDATE " . $this->db->prefix() . "element_element SET ";
4406 if ($updatesource) {
4407 $sql .= "fk_source = " . ((int) $sourceid);
4408 $sql .= ", sourcetype = '" . $this->db->escape($sourcetype) . "'";
4409 $sql .= " WHERE fk_target = " . ((int) $this->id);
4410 $sql .= " AND targettype = '" . $this->db->escape($this->element) . "'";
4411 } elseif ($updatetarget) {
4412 $sql .= "fk_target = " . ((int) $targetid);
4413 $sql .= ", targettype = '" . $this->db->escape($targettype) . "'";
4414 $sql .= " WHERE fk_source = " . ((int) $this->id);
4415 $sql .= " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4416 }
4417
4418 dol_syslog(get_class($this) . "::updateObjectLinked", LOG_DEBUG);
4419 if ($this->db->query($sql)) {
4420 if (!$notrigger) {
4421 // Call trigger
4422 $this->context['link_source_id'] = $sourceid;
4423 $this->context['link_source_type'] = $sourcetype;
4424 $this->context['link_target_id'] = $targetid;
4425 $this->context['link_target_type'] = $targettype;
4426 $result = $this->call_trigger('OBJECT_LINK_MODIFY', $f_user);
4427 if ($result < 0) {
4428 $error++;
4429 }
4430 // End call triggers
4431 }
4432 } else {
4433 $this->error = $this->db->lasterror();
4434 $error++;
4435 }
4436
4437 if (!$error) {
4438 $this->db->commit();
4439 return 1;
4440 } else {
4441 $this->db->rollback();
4442 return -1;
4443 }
4444 }
4445
4459 public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = 0, $f_user = null, $notrigger = 0)
4460 {
4461 global $user;
4462 $deletesource = false;
4463 $deletetarget = false;
4464 $f_user = isset($f_user) ? $f_user : $user;
4465
4466 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4467 $deletesource = true;
4468 } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4469 $deletetarget = true;
4470 }
4471
4472 $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
4473 $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
4474 $targetid = (!empty($targetid) ? $targetid : $this->id);
4475 $targettype = (!empty($targettype) ? $targettype : $this->element);
4476 $this->db->begin();
4477 $error = 0;
4478
4479 if (!$notrigger) {
4480 // Call trigger
4481 $this->context['link_id'] = $rowid;
4482 $this->context['link_source_id'] = $sourceid;
4483 $this->context['link_source_type'] = $sourcetype;
4484 $this->context['link_target_id'] = $targetid;
4485 $this->context['link_target_type'] = $targettype;
4486 $result = $this->call_trigger('OBJECT_LINK_DELETE', $f_user);
4487 if ($result < 0) {
4488 $error++;
4489 }
4490 // End call triggers
4491 }
4492
4493 if (!$error) {
4494 $sql = "DELETE FROM " . $this->db->prefix() . "element_element";
4495 $sql .= " WHERE";
4496 if ($rowid > 0) {
4497 $sql .= " rowid = " . ((int) $rowid);
4498 } else {
4499 if ($deletesource) {
4500 $sql .= " fk_source = " . ((int) $sourceid) . " AND sourcetype = '" . $this->db->escape($sourcetype) . "'";
4501 $sql .= " AND fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "'";
4502 } elseif ($deletetarget) {
4503 $sql .= " fk_target = " . ((int) $targetid) . " AND targettype = '" . $this->db->escape($targettype) . "'";
4504 $sql .= " AND fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4505 } else {
4506 $sql .= " (fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "')";
4507 $sql .= " OR";
4508 $sql .= " (fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "')";
4509 }
4510 }
4511
4512 dol_syslog(get_class($this) . "::deleteObjectLinked", LOG_DEBUG);
4513 if (!$this->db->query($sql)) {
4514 $this->error = $this->db->lasterror();
4515 $this->errors[] = $this->error;
4516 $error++;
4517 }
4518 }
4519
4520 if (!$error) {
4521 $this->db->commit();
4522 return 1;
4523 } else {
4524 $this->db->rollback();
4525 return 0;
4526 }
4527 }
4528
4538 public static function getAllItemsLinkedByObjectID($fk_object_where, $field_select, $field_where, $table_element)
4539 {
4540 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4541 return -1;
4542 }
4543 if (!preg_match('/^[_a-zA-Z0-9]+$/', $field_select)) {
4544 dol_syslog('Invalid value $field_select for parameter '.$field_select.' in call to getAllItemsLinkedByObjectID(). Must be a single field name.', LOG_ERR);
4545 }
4546
4547 global $db;
4548
4549 $sql = "SELECT ".$field_select." FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4550 $resql = $db->query($sql);
4551
4552 $TRes = array();
4553 if (!empty($resql)) {
4554 while ($res = $db->fetch_object($resql)) {
4555 $TRes[] = $res->{$field_select};
4556 }
4557 }
4558
4559 return $TRes;
4560 }
4561
4570 public static function getCountOfItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
4571 {
4572 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4573 return -1;
4574 }
4575
4576 global $db;
4577
4578 $sql = "SELECT COUNT(*) as nb FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4579 $resql = $db->query($sql);
4580 $n = 0;
4581 if ($resql) {
4582 $res = $db->fetch_object($resql);
4583 if ($res) {
4584 $n = $res->nb;
4585 }
4586 }
4587
4588 return $n;
4589 }
4590
4599 public static function deleteAllItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
4600 {
4601 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4602 return -1;
4603 }
4604
4605 global $db;
4606
4607 $sql = "DELETE FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4608 $resql = $db->query($sql);
4609
4610 if (empty($resql)) {
4611 return 0;
4612 }
4613
4614 return 1;
4615 }
4616
4627 public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '', $fieldstatus = 'fk_statut')
4628 {
4629 global $user;
4630
4631 $savElementId = $elementId; // To be used later to know if we were using the method using the id of this or not.
4632
4633 $elementId = (!empty($elementId) ? $elementId : $this->id);
4634 $elementTable = (!empty($elementType) ? $elementType : $this->table_element);
4635
4636 $this->db->begin();
4637
4638 if ($elementTable == 'facture_rec') {
4639 $fieldstatus = "suspended";
4640 }
4641 if ($elementTable == 'mailing') {
4642 $fieldstatus = "statut";
4643 }
4644 if ($elementTable == 'cronjob') {
4645 $fieldstatus = "status";
4646 }
4647 if ($elementTable == 'user') {
4648 $fieldstatus = "statut";
4649 }
4650 if ($elementTable == 'expensereport') {
4651 $fieldstatus = "fk_statut";
4652 }
4653 if ($elementTable == 'receptiondet_batch') {
4654 $fieldstatus = "status";
4655 }
4656 if ($elementTable == 'prelevement_bons') {
4657 $fieldstatus = "statut";
4658 }
4659 if (isset($this->fields) && is_array($this->fields) && array_key_exists('status', $this->fields)) {
4660 $fieldstatus = 'status';
4661 }
4662
4663 $sql = "UPDATE ".$this->db->prefix().$this->db->sanitize($elementTable);
4664 $sql .= " SET ".$this->db->sanitize($fieldstatus)." = ".((int) $status);
4665 // If status = 1 = validated, update also fk_user_valid
4666 // TODO Replace the test on $elementTable by doing a test on existence of the field in $this->fields
4667 if ($status == 1 && in_array($elementTable, array('expensereport', 'inventory'))) {
4668 $sql .= ", fk_user_valid = ".((int) $user->id);
4669 }
4670 if ($status == 1 && in_array($elementTable, array('expensereport'))) {
4671 $sql .= ", date_valid = '".$this->db->idate(dol_now())."'";
4672 }
4673 if ($status == 1 && in_array($elementTable, array('inventory'))) {
4674 $sql .= ", date_validation = '".$this->db->idate(dol_now())."'";
4675 }
4676 $sql .= " WHERE rowid = ".((int) $elementId);
4677 $sql .= " AND ".$fieldstatus." <> ".((int) $status); // We avoid update if status already correct
4678
4679 dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
4680 $resql = $this->db->query($sql);
4681 if ($resql) {
4682 $error = 0;
4683
4684 $nb_rows_affected = $this->db->affected_rows($resql); // should be 1 or 0 if status was already correct
4685
4686 if ($nb_rows_affected > 0) {
4687 if (empty($trigkey)) {
4688 // Try to guess trigkey (for backward compatibility, now we should have trigkey defined into the call of setStatus)
4689 if ($this->element == 'supplier_proposal' && $status == 2) {
4690 $trigkey = 'SUPPLIER_PROPOSAL_SIGN'; // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
4691 }
4692 if ($this->element == 'supplier_proposal' && $status == 3) {
4693 $trigkey = 'SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
4694 }
4695 if ($this->element == 'supplier_proposal' && $status == 4) {
4696 $trigkey = 'SUPPLIER_PROPOSAL_CLOSE'; // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
4697 }
4698 if ($this->element == 'fichinter' && $status == 3) {
4699 $trigkey = 'FICHINTER_CLASSIFY_DONE';
4700 }
4701 if ($this->element == 'fichinter' && $status == 2) {
4702 $trigkey = 'FICHINTER_CLASSIFY_BILLED';
4703 }
4704 if ($this->element == 'fichinter' && $status == 1) {
4705 $trigkey = 'FICHINTER_CLASSIFY_UNBILLED';
4706 }
4707 }
4708
4709 $this->context = array_merge($this->context, array('newstatus' => $status));
4710
4711 if ($trigkey) {
4712 // Call trigger
4713 $result = $this->call_trigger($trigkey, $user);
4714 if ($result < 0) {
4715 $error++;
4716 }
4717 // End call triggers
4718 }
4719 } else {
4720 // The status was probably already good. We do nothing more, no triggers.
4721 }
4722
4723 if (!$error) {
4724 $this->db->commit();
4725
4726 if (empty($savElementId)) {
4727 // If the element we update is $this (so $elementId was provided as null)
4728 if ($fieldstatus == 'tosell') {
4729 $this->status = $status;
4730 } elseif ($fieldstatus == 'tobuy') {
4731 $this->status_buy = $status; // @phpstan-ignore-line
4732 } else {
4733 $this->status = $status;
4734 }
4735 }
4736
4737 return 1;
4738 } else {
4739 $this->db->rollback();
4740 dol_syslog(get_class($this)."::setStatut ".$this->error, LOG_ERR);
4741 return -1;
4742 }
4743 } else {
4744 $this->error = $this->db->lasterror();
4745 $this->db->rollback();
4746 return -1;
4747 }
4748 }
4749
4750
4758 public function getCanvas($id = 0, $ref = '')
4759 {
4760 global $conf;
4761
4762 if (empty($id) && empty($ref)) {
4763 return 0;
4764 }
4765 if (getDolGlobalString('MAIN_DISABLE_CANVAS')) {
4766 return 0; // To increase speed. Not enabled by default.
4767 }
4768
4769 // Clean parameters
4770 $ref = trim($ref);
4771
4772 $sql = "SELECT rowid, canvas";
4773 $sql .= " FROM ".$this->db->prefix().$this->table_element;
4774 $sql .= " WHERE entity IN (".getEntity($this->element).")";
4775 if (!empty($id)) {
4776 $sql .= " AND rowid = ".((int) $id);
4777 }
4778 if (!empty($ref)) {
4779 $sql .= " AND ref = '".$this->db->escape($ref)."'";
4780 }
4781
4782 $resql = $this->db->query($sql);
4783 if ($resql) {
4784 $obj = $this->db->fetch_object($resql);
4785 if ($obj) {
4786 $this->canvas = $obj->canvas;
4787 return 1;
4788 } else {
4789 return 0;
4790 }
4791 } else {
4792 dol_print_error($this->db);
4793 return -1;
4794 }
4795 }
4796
4797
4804 public function getSpecialCode($lineid)
4805 {
4806 $sql = "SELECT special_code FROM ".$this->db->prefix().$this->table_element_line;
4807 $sql .= " WHERE rowid = ".((int) $lineid);
4808 $resql = $this->db->query($sql);
4809 if ($resql) {
4810 $row = $this->db->fetch_row($resql);
4811 return (!empty($row[0]) ? $row[0] : 0);
4812 }
4813
4814 return 0;
4815 }
4816
4825 public function isObjectUsed($id = 0, $entity = 0)
4826 {
4827 global $langs;
4828
4829 if (empty($id)) {
4830 $id = $this->id;
4831 }
4832
4833 // Check parameters
4834 if (!isset($this->childtables) || !is_array($this->childtables) || count($this->childtables) == 0) {
4835 dol_print_error(null, 'Called isObjectUsed on a class with property this->childtables not defined');
4836 return -1;
4837 }
4838
4839 $arraytoscan = $this->childtables; // array('tablename'=>array('fk_element'=>'parentfield'), ...) or array('tablename'=>array('parent'=>table_parent, 'parentkey'=>'nameoffieldforparentfkkey'), ...)
4840 // For backward compatibility, we check if array is old format array('tablename1', 'tablename2', ...)
4841 $tmparray = array_keys($this->childtables);
4842 if (is_numeric($tmparray[0])) {
4843 $arraytoscan = array_flip($this->childtables);
4844 }
4845
4846 // Test if child exists
4847 $haschild = 0;
4848 foreach ($arraytoscan as $table => $element) {
4849 //print $id.'-'.$table.'-'.$elementname.'<br>';
4850 // Check if element can be deleted
4851 $sql = "SELECT COUNT(*) as nb";
4852 $sql .= " FROM ".$this->db->prefix().$table." as c";
4853 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4854 $sql .= ", ".$this->db->prefix().$element['parent']." as p";
4855 }
4856 if (!empty($element['fk_element'])) {
4857 $sql .= " WHERE c.".$element['fk_element']." = ".((int) $id);
4858 } else {
4859 $sql .= " WHERE c.".$this->fk_element." = ".((int) $id);
4860 }
4861 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4862 $sql .= " AND c.".$element['parentkey']." = p.rowid";
4863 }
4864 if (!empty($element['parent']) && !empty($element['parenttypefield']) && !empty($element['parenttypevalue'])) {
4865 $sql .= " AND c.".$element['parenttypefield']." = '".$this->db->escape($element['parenttypevalue'])."'";
4866 }
4867 if (!empty($entity)) {
4868 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4869 $sql .= " AND p.entity = ".((int) $entity);
4870 } else {
4871 $sql .= " AND c.entity = ".((int) $entity);
4872 }
4873 }
4874
4875 $resql = $this->db->query($sql);
4876 if ($resql) {
4877 $obj = $this->db->fetch_object($resql);
4878 if ($obj->nb > 0) {
4879 $langs->load("errors");
4880 //print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
4881 $haschild += $obj->nb;
4882 if (is_numeric($element)) { // very old usage array('table1', 'table2', ...)
4883 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $table);
4884 } elseif (is_string($element)) { // old usage array('table1' => 'TranslateKey1', 'table2' => 'TranslateKey2', ...)
4885 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element));
4886 } else { // new usage: $element['name']=Translation key
4887 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element['name']));
4888 }
4889 break; // We found at least one, we stop here
4890 }
4891 } else {
4892 $this->errors[] = $this->db->lasterror();
4893 return -1;
4894 }
4895 }
4896 if ($haschild > 0) {
4897 $this->errors[] = "ErrorRecordHasChildren";
4898 return $haschild;
4899 } else {
4900 return 0;
4901 }
4902 }
4903
4910 public function hasProductsOrServices($predefined = -1)
4911 {
4912 $nb = 0;
4913
4914 foreach ($this->lines as $key => $val) {
4915 $qualified = 0;
4916 if ($predefined == -1) {
4917 $qualified = 1;
4918 }
4919 if ($predefined == 1 && $val->fk_product > 0) {
4920 $qualified = 1;
4921 }
4922 if ($predefined == 0 && $val->fk_product <= 0) {
4923 $qualified = 1;
4924 }
4925 if ($predefined == 2 && $val->fk_product > 0 && $val->product_type == 0) {
4926 $qualified = 1;
4927 }
4928 if ($predefined == 3 && $val->fk_product > 0 && $val->product_type == 1) {
4929 $qualified = 1;
4930 }
4931 if ($qualified) {
4932 $nb++;
4933 }
4934 }
4935 dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
4936 return $nb;
4937 }
4938
4944 public function getTotalDiscount()
4945 {
4946 if (!empty($this->table_element_line)) {
4947 $total_discount = 0.00;
4948
4949 $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
4950 $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
4951 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
4952
4953 dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
4954 $resql = $this->db->query($sql);
4955 if ($resql) {
4956 $num = $this->db->num_rows($resql);
4957 $i = 0;
4958 while ($i < $num) {
4959 $obj = $this->db->fetch_object($resql);
4960
4961 $pu_ht = $obj->pu_ht;
4962 $qty = $obj->qty;
4963 $total_ht = $obj->total_ht;
4964
4965 $total_discount_line = (float) price2num(($pu_ht * $qty) - $total_ht, 'MT');
4966 $total_discount += $total_discount_line;
4967
4968 $i++;
4969 }
4970 }
4971
4972 //print $total_discount; exit;
4973 return (float) price2num($total_discount);
4974 }
4975
4976 return null;
4977 }
4978
4979
4986 public function getTotalWeightVolume()
4987 {
4988 $totalWeight = 0;
4989 $totalVolume = 0;
4990 // defined for shipment only
4991 $totalOrdered = 0;
4992 // defined for shipment only
4993 $totalToShip = 0;
4994
4995 if (empty($this->lines)) {
4996 return array('weight' => $totalWeight, 'volume' => $totalVolume, 'ordered' => $totalOrdered, 'toship' => $totalToShip);
4997 }
4998
4999 foreach ($this->lines as $line) {
5000 if (isset($line->qty_asked)) {
5001 $totalOrdered += $line->qty_asked; // defined for shipment only
5002 }
5003 if (isset($line->qty_shipped)) {
5004 $totalToShip += $line->qty_shipped; // defined for shipment only
5005 } elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty)) {
5006 if (empty($totalToShip)) {
5007 $totalToShip = 0;
5008 }
5009 $totalToShip += $line->qty; // defined for reception only
5010 }
5011
5012 // Define qty, weight, volume, weight_units, volume_units
5013 if ($this->element == 'shipping') {
5014 // for shipments
5015 $qty = $line->qty_shipped ? $line->qty_shipped : 0;
5016 } else {
5017 $qty = $line->qty ? $line->qty : 0;
5018 }
5019
5020 $weight = !empty($line->weight) ? $line->weight : 0;
5021 ($weight == 0 && !empty($line->product->weight)) ? $weight = $line->product->weight : 0;
5022 $volume = !empty($line->volume) ? $line->volume : 0;
5023 ($volume == 0 && !empty($line->product->volume)) ? $volume = $line->product->volume : 0;
5024
5025 $weight_units = !empty($line->weight_units) ? $line->weight_units : 0;
5026 ($weight_units == 0 && !empty($line->product->weight_units)) ? $weight_units = $line->product->weight_units : 0;
5027 $volume_units = !empty($line->volume_units) ? $line->volume_units : 0;
5028 ($volume_units == 0 && !empty($line->product->volume_units)) ? $volume_units = $line->product->volume_units : 0;
5029
5030 $weightUnit = 0;
5031 $volumeUnit = 0;
5032 if (!empty($weight_units)) {
5033 $weightUnit = $weight_units;
5034 }
5035 if (!empty($volume_units)) {
5036 $volumeUnit = $volume_units;
5037 }
5038
5039 if (empty($totalWeight)) {
5040 $totalWeight = 0; // Avoid warning because $totalWeight is ''
5041 }
5042 if (empty($totalVolume)) {
5043 $totalVolume = 0; // Avoid warning because $totalVolume is ''
5044 }
5045
5046 //var_dump($line->volume_units);
5047 if ($weight_units < 50) { // < 50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
5048 $trueWeightUnit = pow(10, $weightUnit);
5049 $totalWeight += $weight * $qty * $trueWeightUnit;
5050 } else {
5051 if ($weight_units == 99) {
5052 // conversion 1 Pound = 0.45359237 KG
5053 $trueWeightUnit = 0.45359237;
5054 $totalWeight += $weight * $qty * $trueWeightUnit;
5055 } elseif ($weight_units == 98) {
5056 // conversion 1 Ounce = 0.0283495 KG
5057 $trueWeightUnit = 0.0283495;
5058 $totalWeight += $weight * $qty * $trueWeightUnit;
5059 } else {
5060 $totalWeight += $weight * $qty; // This may be wrong if we mix different units
5061 }
5062 }
5063 if ($volume_units < 50) { // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
5064 //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
5065 $trueVolumeUnit = pow(10, $volumeUnit);
5066 //print $line->volume;
5067 $totalVolume += $volume * $qty * $trueVolumeUnit;
5068 } else {
5069 $totalVolume += $volume * $qty; // This may be wrong if we mix different units
5070 }
5071 }
5072
5073 return array('weight' => $totalWeight, 'volume' => $totalVolume, 'ordered' => $totalOrdered, 'toship' => $totalToShip);
5074 }
5075
5076
5082 public function setExtraParameters()
5083 {
5084 $this->db->begin();
5085
5086 $extraparams = (!empty($this->extraparams) ? json_encode($this->extraparams) : null);
5087
5088 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
5089 $sql .= " SET extraparams = ".(!empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
5090 $sql .= " WHERE rowid = ".((int) $this->id);
5091
5092 dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
5093 $resql = $this->db->query($sql);
5094 if (!$resql) {
5095 $this->error = $this->db->lasterror();
5096 $this->db->rollback();
5097 return -1;
5098 } else {
5099 $this->db->commit();
5100 return 1;
5101 }
5102 }
5103
5104
5105 // --------------------
5106 // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
5107 // --------------------
5108
5109 /* This is to show add lines */
5110
5120 public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl')
5121 {
5122 global $conf, $user, $langs, $object, $hookmanager, $extrafields, $form;
5123
5124 // Line extrafield
5125 if (!is_object($extrafields)) {
5126 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5127 $extrafields = new ExtraFields($this->db);
5128 }
5129 $extrafields->fetch_name_optionals_label($this->table_element_line);
5130
5131 // Output template part (modules that overwrite templates must declare this into descriptor)
5132 // Use global variables + $dateSelector + $seller and $buyer
5133 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'.
5134 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5135 foreach ($dirtpls as $module => $reldir) {
5136 if (!empty($module)) {
5137 $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
5138 } else {
5139 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php';
5140 }
5141
5142 if (empty($conf->file->strict_mode)) {
5143 $res = @include $tpl;
5144 } else {
5145 $res = include $tpl; // for debug
5146 }
5147 if ($res) {
5148 break;
5149 }
5150 }
5151 }
5152
5153
5154
5155 /* This is to show array of line of details */
5156
5157
5172 public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl')
5173 {
5174 global $conf, $hookmanager, $langs, $user, $form, $extrafields, $object;
5175 // TODO We should not use global var for this
5176 global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
5177
5178 // Define $usemargins (used by objectline_xxx.tpl.php files)
5179 $usemargins = 0;
5180 if (isModEnabled('margin') && !empty($this->element) && in_array($this->element, array('facture', 'facturerec', 'propal', 'commande'))) {
5181 $usemargins = 1;
5182 }
5183
5184 $num = count($this->lines);
5185
5186 // Line extrafield
5187 if (!is_object($extrafields)) {
5188 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5189 $extrafields = new ExtraFields($this->db);
5190 }
5191 $extrafields->fetch_name_optionals_label($this->table_element_line);
5192
5193 $parameters = array('num' => $num, 'dateSelector' => $dateSelector, 'seller' => $seller, 'buyer' => $buyer, 'selected' => $selected, 'table_element_line' => $this->table_element_line);
5194 $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5195 if (empty($reshook)) {
5196 // Output template part (modules that overwrite templates must declare this into descriptor)
5197 // Use global variables + $dateSelector + $seller and $buyer
5198 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook.
5199 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5200 foreach ($dirtpls as $module => $reldir) {
5201 $res = 0;
5202 if (!empty($module)) {
5203 $tpl = dol_buildpath($reldir.'/objectline_title.tpl.php');
5204 } else {
5205 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php';
5206 }
5207 if (file_exists($tpl)) {
5208 if (empty($conf->file->strict_mode)) {
5209 $res = @include $tpl;
5210 } else {
5211 $res = include $tpl; // for debug
5212 }
5213 }
5214 if ($res) {
5215 break;
5216 }
5217 }
5218 }
5219
5220 $i = 0;
5221
5222 print "<!-- begin printObjectLines() --><tbody>\n";
5223 foreach ($this->lines as $line) {
5224 //Line extrafield
5225 $line->fetch_optionals();
5226
5227 //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line)))
5228 if (is_object($hookmanager)) { // Old code is commented on preceding line.
5229 if (empty($line->fk_parent_line)) {
5230 $parameters = array('line' => $line, 'num' => $num, 'i' => $i, 'dateSelector' => $dateSelector, 'seller' => $seller, 'buyer' => $buyer, 'selected' => $selected, 'table_element_line' => $line->table_element, 'defaulttpldir' => $defaulttpldir);
5231 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5232 } else {
5233 $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);
5234 $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5235 }
5236 }
5237 if (empty($reshook)) {
5238 $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafields, $defaulttpldir);
5239 }
5240
5241 $i++;
5242 }
5243 print "</tbody><!-- end printObjectLines() -->\n";
5244 }
5245
5263 public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/core/tpl')
5264 {
5265 global $conf, $langs, $user, $object, $hookmanager;
5266 global $form;
5267 global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
5268
5269 $object_rights = $this->getRights();
5270
5271 // var used into tpl
5272 $text = '';
5273 $description = '';
5274
5275 // Line in view mode
5276 if ($action != 'editline' || $selected != $line->id) {
5277 // Product
5278 if (!empty($line->fk_product) && $line->fk_product > 0) {
5279 $product_static = new Product($this->db);
5280 $product_static->fetch($line->fk_product);
5281
5282 $product_static->ref = $line->ref; //can change ref in hook
5283 $product_static->label = !empty($line->label) ? $line->label : ""; //can change label in hook
5284
5285 $text = $product_static->getNomUrl(1);
5286
5287 // Define output language and label
5288 if (getDolGlobalInt('MAIN_MULTILANGS')) {
5289 if (property_exists($this, 'socid') && !is_object($this->thirdparty)) {
5290 dol_print_error(null, 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
5291 return;
5292 }
5293
5294 $prod = new Product($this->db);
5295 $prod->fetch($line->fk_product);
5296
5297 $outputlangs = $langs;
5298 $newlang = '';
5299 if (empty($newlang) && GETPOST('lang_id', 'aZ09')) {
5300 $newlang = GETPOST('lang_id', 'aZ09');
5301 }
5302 if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE') && empty($newlang) && is_object($this->thirdparty)) {
5303 $newlang = $this->thirdparty->default_lang; // To use language of customer
5304 }
5305 if (!empty($newlang)) {
5306 $outputlangs = new Translate("", $conf);
5307 $outputlangs->setDefaultLang($newlang);
5308 }
5309
5310 $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
5311 } else {
5312 $label = $line->product_label;
5313 }
5314
5315 $text .= ' - '.(!empty($line->label) ? $line->label : $label);
5316 $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.
5317 }
5318
5319 $line->pu_ttc = price2num((!empty($line->subprice) ? $line->subprice : 0) * (1 + ((!empty($line->tva_tx) ? $line->tva_tx : 0) / 100)), 'MU');
5320
5321 // Output template part (modules that overwrite templates must declare this into descriptor)
5322 // Use global variables + $dateSelector + $seller and $buyer
5323 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5324 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5325 foreach ($dirtpls as $module => $reldir) {
5326 $res = 0;
5327 if (!empty($module)) {
5328 $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
5329 } else {
5330 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php';
5331 }
5332 //var_dump($tpl);
5333 if (file_exists($tpl)) {
5334 if (empty($conf->file->strict_mode)) {
5335 $res = @include $tpl;
5336 } else {
5337 $res = include $tpl; // for debug
5338 }
5339 }
5340 if ($res) {
5341 break;
5342 }
5343 }
5344 }
5345
5346 // Line in update mode
5347 if ($this->status == 0 && $action == 'editline' && $selected == $line->id) {
5348 $label = (!empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
5349
5350 $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU');
5351
5352 // Output template part (modules that overwrite templates must declare this into descriptor)
5353 // Use global variables + $dateSelector + $seller and $buyer
5354 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5355 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5356 foreach ($dirtpls as $module => $reldir) {
5357 if (!empty($module)) {
5358 $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
5359 } else {
5360 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php';
5361 }
5362
5363 if (empty($conf->file->strict_mode)) {
5364 $res = @include $tpl;
5365 } else {
5366 $res = include $tpl; // for debug
5367 }
5368 if ($res) {
5369 break;
5370 }
5371 }
5372 }
5373 }
5374
5375
5376 /* This is to show array of line of details of source object */
5377
5378
5389 public function printOriginLinesList($restrictlist = '', $selectedLines = array())
5390 {
5391 global $langs, $hookmanager, $form, $action;
5392
5393 print '<tr class="liste_titre">';
5394 print '<td class="linecolref">'.$langs->trans('Ref').'</td>';
5395 print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
5396 print '<td class="linecolvat right">'.$langs->trans('VATRate').'</td>';
5397 print '<td class="linecoluht right">'.$langs->trans('PriceUHT').'</td>';
5398 if (isModEnabled("multicurrency")) {
5399 print '<td class="linecoluht_currency right">'.$langs->trans('PriceUHTCurrency').'</td>';
5400 }
5401 print '<td class="linecolqty right">'.$langs->trans('Qty').'</td>';
5402 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5403 print '<td class="linecoluseunit left">'.$langs->trans('Unit').'</td>';
5404 }
5405 print '<td class="linecoldiscount right">'.$langs->trans('ReductionShort').'</td>';
5406 print '<td class="linecolht right">'.$langs->trans('TotalHT').'</td>';
5407 print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
5408 print '</tr>';
5409 $i = 0;
5410
5411 if (!empty($this->lines)) {
5412 foreach ($this->lines as $line) {
5413 $reshook = 0;
5414 //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line))) {
5415 if (is_object($hookmanager)) { // Old code is commented on preceding line.
5416 $parameters = array('line' => $line, 'i' => $i, 'restrictlist' => $restrictlist, 'selectedLines' => $selectedLines);
5417 if (!empty($line->fk_parent_line)) {
5418 $parameters['fk_parent_line'] = $line->fk_parent_line;
5419 }
5420 $reshook = $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5421 }
5422 if (empty($reshook)) {
5423 $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines);
5424 }
5425
5426 $i++;
5427 }
5428 }
5429 }
5430
5444 public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
5445 {
5446 global $langs, $conf;
5447
5448 //var_dump($line);
5449 if (!empty($line->date_start)) {
5450 $date_start = $line->date_start;
5451 } else {
5452 $date_start = $line->date_debut_prevue;
5453 if ($line->date_debut_reel) {
5454 $date_start = $line->date_debut_reel;
5455 }
5456 }
5457 if (!empty($line->date_end)) {
5458 $date_end = $line->date_end;
5459 } else {
5460 $date_end = $line->date_fin_prevue;
5461 if ($line->date_fin_reel) {
5462 $date_end = $line->date_fin_reel;
5463 }
5464 }
5465
5466 $this->tpl['id'] = $line->id;
5467
5468 $this->tpl['label'] = '';
5469 if (!empty($line->fk_parent_line)) {
5470 $this->tpl['label'] .= img_picto('', 'rightarrow');
5471 }
5472
5473 if (($line->info_bits & 2) == 2) { // TODO Not sure this is used for source object
5474 $discount = new DiscountAbsolute($this->db);
5475 if (property_exists($this, 'socid')) {
5476 $discount->fk_soc = $this->socid;
5477 $discount->socid = $this->socid;
5478 }
5479 $this->tpl['label'] .= $discount->getNomUrl(0, 'discount');
5480 } elseif (!empty($line->fk_product)) {
5481 $productstatic = new Product($this->db);
5482 $productstatic->id = $line->fk_product;
5483 $productstatic->ref = $line->ref;
5484 $productstatic->type = $line->fk_product_type;
5485 if (empty($productstatic->ref)) {
5486 $line->fetch_product();
5487 $productstatic = $line->product;
5488 }
5489
5490 $this->tpl['label'] .= $productstatic->getNomUrl(1);
5491 $this->tpl['label'] .= ' - '.(!empty($line->label) ? $line->label : $line->product_label);
5492 // Dates
5493 if ($line->product_type == 1 && ($date_start || $date_end)) {
5494 $this->tpl['label'] .= get_date_range($date_start, $date_end);
5495 }
5496 } else {
5497 $this->tpl['label'] .= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
5498 if (!empty($line->desc)) {
5499 $this->tpl['label'] .= $line->desc;
5500 } else {
5501 $this->tpl['label'] .= ($line->label ? '&nbsp;'.$line->label : '');
5502 }
5503
5504 // Dates
5505 if ($line->product_type == 1 && ($date_start || $date_end)) {
5506 $this->tpl['label'] .= get_date_range($date_start, $date_end);
5507 }
5508 }
5509
5510 if (!empty($line->desc)) {
5511 if ($line->desc == '(CREDIT_NOTE)') { // TODO Not sure this is used for source object
5512 $discount = new DiscountAbsolute($this->db);
5513 $discount->fetch($line->fk_remise_except);
5514 $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
5515 } elseif ($line->desc == '(DEPOSIT)') { // TODO Not sure this is used for source object
5516 $discount = new DiscountAbsolute($this->db);
5517 $discount->fetch($line->fk_remise_except);
5518 $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
5519 } elseif ($line->desc == '(EXCESS RECEIVED)') {
5520 $discount = new DiscountAbsolute($this->db);
5521 $discount->fetch($line->fk_remise_except);
5522 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
5523 } elseif ($line->desc == '(EXCESS PAID)') {
5524 $discount = new DiscountAbsolute($this->db);
5525 $discount->fetch($line->fk_remise_except);
5526 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
5527 } else {
5528 $this->tpl['description'] = dol_trunc($line->desc, 60);
5529 }
5530 } else {
5531 $this->tpl['description'] = '&nbsp;';
5532 }
5533
5534 // VAT Rate
5535 $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
5536 $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
5537 if (!empty($line->vat_src_code) && !preg_match('/\‍(/', $this->tpl['vat_rate'])) {
5538 $this->tpl['vat_rate'] .= ' ('.$line->vat_src_code.')';
5539 }
5540
5541 $this->tpl['price'] = price($line->subprice);
5542 $this->tpl['total_ht'] = price($line->total_ht);
5543 $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
5544 $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
5545 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5546 $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
5547 }
5548 $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
5549
5550 // Is the line strike or not
5551 $this->tpl['strike'] = 0;
5552 if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) {
5553 $this->tpl['strike'] = 1;
5554 }
5555
5556 // Output template part (modules that overwrite templates must declare this into descriptor)
5557 // Use global variables + $dateSelector + $seller and $buyer
5558 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5559 foreach ($dirtpls as $module => $reldir) {
5560 if (!empty($module)) {
5561 $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
5562 } else {
5563 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php';
5564 }
5565
5566 if (empty($conf->file->strict_mode)) {
5567 $res = @include $tpl;
5568 } else {
5569 $res = include $tpl; // for debug
5570 }
5571 if ($res) {
5572 break;
5573 }
5574 }
5575 }
5576
5577
5578 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5589 public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
5590 {
5591 // phpcs:enable
5592 $this->db->begin();
5593
5594 $sql = "INSERT INTO ".$this->db->prefix()."element_resources (";
5595 $sql .= "resource_id";
5596 $sql .= ", resource_type";
5597 $sql .= ", element_id";
5598 $sql .= ", element_type";
5599 $sql .= ", busy";
5600 $sql .= ", mandatory";
5601 $sql .= ") VALUES (";
5602 $sql .= ((int) $resource_id);
5603 $sql .= ", '".$this->db->escape($resource_type)."'";
5604 $sql .= ", '".$this->db->escape($this->id)."'";
5605 $sql .= ", '".$this->db->escape($this->element)."'";
5606 $sql .= ", '".$this->db->escape($busy)."'";
5607 $sql .= ", '".$this->db->escape($mandatory)."'";
5608 $sql .= ")";
5609
5610 dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
5611 if ($this->db->query($sql)) {
5612 $this->db->commit();
5613 return 1;
5614 } else {
5615 $this->error = $this->db->lasterror();
5616 $this->db->rollback();
5617 return 0;
5618 }
5619 }
5620
5621 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5630 public function delete_resource($rowid, $element, $notrigger = 0)
5631 {
5632 // phpcs:enable
5633 global $user;
5634
5635 $this->db->begin();
5636
5637 $sql = "DELETE FROM ".$this->db->prefix()."element_resources";
5638 $sql .= " WHERE rowid = ".((int) $rowid);
5639
5640 dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
5641
5642 $resql = $this->db->query($sql);
5643 if (!$resql) {
5644 $this->error = $this->db->lasterror();
5645 $this->db->rollback();
5646 return -1;
5647 } else {
5648 if (!$notrigger) {
5649 $result = $this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
5650 if ($result < 0) {
5651 $this->db->rollback();
5652 return -1;
5653 }
5654 }
5655 $this->db->commit();
5656 return 1;
5657 }
5658 }
5659
5660
5666 public function __clone()
5667 {
5668 // Force a copy of this->lines, otherwise it will point to same object.
5669 if (isset($this->lines) && is_array($this->lines)) {
5670 $nboflines = count($this->lines);
5671 for ($i = 0; $i < $nboflines; $i++) {
5672 if (is_object($this->lines[$i])) {
5673 $this->lines[$i] = clone $this->lines[$i];
5674 }
5675 }
5676 }
5677 }
5678
5692 protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
5693 {
5694 global $conf, $langs, $user, $hookmanager, $action;
5695
5696 $srctemplatepath = '';
5697
5698 $parameters = array('modelspath' => $modelspath, 'modele' => $modele, 'outputlangs' => $outputlangs, 'hidedetails' => $hidedetails, 'hidedesc' => $hidedesc, 'hideref' => $hideref, 'moreparams' => $moreparams);
5699 $reshook = $hookmanager->executeHooks('commonGenerateDocument', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5700
5701 if (!empty($reshook)) {
5702 return $reshook;
5703 }
5704
5705 dol_syslog("commonGenerateDocument modele=".$modele." outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
5706
5707 if (empty($modele)) {
5708 $this->error = 'BadValueForParameterModele';
5709 return -1;
5710 }
5711
5712 // Increase limit for PDF build
5713 $err = error_reporting();
5714 error_reporting(0);
5715 @set_time_limit(120);
5716 error_reporting($err);
5717
5718 // If selected model is a filename template (then $modele="modelname" or "modelname:filename")
5719 $tmp = explode(':', $modele, 2);
5720 $saved_model = $modele;
5721 if (!empty($tmp[1])) {
5722 $modele = $tmp[0];
5723 $srctemplatepath = $tmp[1];
5724 }
5725
5726 // Search template files
5727 $file = '';
5728 $classname = '';
5729 $filefound = '';
5730 $dirmodels = array('/');
5731 if (is_array($conf->modules_parts['models'])) {
5732 $dirmodels = array_merge($dirmodels, $conf->modules_parts['models']);
5733 }
5734 foreach ($dirmodels as $reldir) {
5735 foreach (array('doc', 'pdf') as $prefix) {
5736 if (in_array(get_class($this), array('Adherent'))) {
5737 // Member module use prefix_modele.class.php
5738 $file = $prefix."_".$modele.".class.php";
5739 } else {
5740 // Other module use prefix_modele.modules.php
5741 $file = $prefix."_".$modele.".modules.php";
5742 }
5743
5744 $file = dol_sanitizeFileName($file);
5745
5746 // We check if the file exists
5747 $file = dol_buildpath($reldir.$modelspath.$file, 0);
5748 if (file_exists($file)) {
5749 $filefound = $file;
5750 $classname = $prefix.'_'.$modele;
5751 break;
5752 }
5753 }
5754 if ($filefound) {
5755 break;
5756 }
5757 }
5758
5759 if ($filefound === '' || $classname === '') {
5760 $this->error = $langs->trans("Error").' Failed to load doc generator with modelpaths='.$modelspath.' - modele='.$modele;
5761 $this->errors[] = $this->error;
5762 dol_syslog($this->error, LOG_ERR);
5763 return -1;
5764 }
5765
5766 // Sanitize $filefound
5767 $filefound = dol_sanitizePathName($filefound);
5768
5769 // If generator was found
5770 global $db; // Required to solve a conception error making an include of some code that uses $db instead of $this->db just after.
5771
5772 require_once $filefound;
5773
5774 $obj = new $classname($this->db);
5775
5776 // If generator is ODT, we must have srctemplatepath defined, if not we set it.
5777 if ($obj->type == 'odt' && empty($srctemplatepath)) {
5778 $varfortemplatedir = $obj->scandir;
5779 if ($varfortemplatedir && getDolGlobalString($varfortemplatedir)) {
5780 $dirtoscan = getDolGlobalString($varfortemplatedir);
5781
5782 $listoffiles = array();
5783
5784 // Now we add first model found in directories scanned
5785 $listofdir = explode(',', $dirtoscan);
5786 foreach ($listofdir as $key => $tmpdir) {
5787 $tmpdir = trim($tmpdir);
5788 $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
5789 if (!$tmpdir) {
5790 unset($listofdir[$key]);
5791 continue;
5792 }
5793 if (is_dir($tmpdir)) {
5794 $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
5795 if (count($tmpfiles)) {
5796 $listoffiles = array_merge($listoffiles, $tmpfiles);
5797 }
5798 }
5799 }
5800
5801 if (count($listoffiles)) {
5802 foreach ($listoffiles as $record) {
5803 $srctemplatepath = $record['fullname'];
5804 break;
5805 }
5806 }
5807 }
5808
5809 if (empty($srctemplatepath)) {
5810 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
5811 return -1;
5812 }
5813 }
5814
5815 if ($obj->type == 'odt' && !empty($srctemplatepath)) {
5816 if (!dol_is_file($srctemplatepath)) {
5817 dol_syslog("Failed to locate template file ".$srctemplatepath, LOG_WARNING);
5818 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
5819 return -1;
5820 }
5821 }
5822
5823 // We save charset_output to restore it because write_file can change it if needed for
5824 // output format that does not support UTF8.
5825 $sav_charset_output = empty($outputlangs->charset_output) ? '' : $outputlangs->charset_output;
5826
5827 // update model_pdf in object
5828 $this->model_pdf = $saved_model;
5829
5830 if (in_array(get_class($this), array('Adherent'))) {
5831 '@phan-var-force Adherent $this';
5832 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, 'tmp_cards', $moreparams);
5833 } else {
5834 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
5835 }
5836 // After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
5837
5838 if ($resultwritefile > 0) {
5839 $outputlangs->charset_output = $sav_charset_output;
5840
5841 // We delete old preview
5842 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5843 dol_delete_preview($this);
5844
5845 // Index file in database
5846 if (!empty($obj->result['fullpath'])) {
5847 $destfull = $obj->result['fullpath'];
5848
5849 // Update the last_main_doc field into main object (if document generator has property ->update_main_doc_field set)
5850 $update_main_doc_field = 0;
5851 if (!empty($obj->update_main_doc_field)) {
5852 $update_main_doc_field = 1;
5853 }
5854
5855 // Check that the file exists, before indexing it.
5856 // Hint: It does not exist, if we create a PDF and auto delete the ODT File
5857 if (dol_is_file($destfull)) {
5858 $this->indexFile($destfull, $update_main_doc_field);
5859 }
5860 } else {
5861 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);
5862 }
5863
5864 // Success in building document. We build meta file.
5865 dol_meta_create($this);
5866
5867 return 1;
5868 } else {
5869 $outputlangs->charset_output = $sav_charset_output;
5870 $this->error = $obj->error;
5871 $this->errors = $obj->errors;
5872 dol_syslog("Error generating document for ".__CLASS__.". Error: ".$obj->error, LOG_ERR);
5873 return -1;
5874 }
5875 }
5876
5886 public function indexFile($destfull, $update_main_doc_field)
5887 {
5888 global $conf, $user;
5889
5890 $upload_dir = dirname($destfull);
5891 $destfile = basename($destfull);
5892 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
5893
5894 if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a tmp dir
5895 $filename = basename($destfile);
5896 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
5897 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
5898
5899 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
5900 $ecmfile = new EcmFiles($this->db);
5901 $result = $ecmfile->fetch(0, '', ($rel_dir ? $rel_dir.'/' : '').$filename);
5902
5903 // Set the public "share" key
5904 $setsharekey = false;
5905 if ($this->element == 'propal' || $this->element == 'proposal') {
5906 if (getDolGlobalInt("PROPOSAL_ALLOW_ONLINESIGN")) {
5907 $setsharekey = true; // feature to make online signature is not set or set to on (default)
5908 }
5909 if (getDolGlobalInt("PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD")) {
5910 $setsharekey = true;
5911 }
5912 }
5913 if ($this->element == 'commande' && getDolGlobalInt("ORDER_ALLOW_EXTERNAL_DOWNLOAD")) {
5914 $setsharekey = true;
5915 }
5916 if ($this->element == 'facture' && getDolGlobalInt("INVOICE_ALLOW_EXTERNAL_DOWNLOAD")) {
5917 $setsharekey = true;
5918 }
5919 if ($this->element == 'bank_account' && getDolGlobalInt("BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD")) {
5920 $setsharekey = true;
5921 }
5922 if ($this->element == 'product' && getDolGlobalInt("PRODUCT_ALLOW_EXTERNAL_DOWNLOAD")) {
5923 $setsharekey = true;
5924 }
5925 if ($this->element == 'contrat' && getDolGlobalInt("CONTRACT_ALLOW_EXTERNAL_DOWNLOAD")) {
5926 $setsharekey = true;
5927 }
5928 if ($this->element == 'fichinter' && getDolGlobalInt("FICHINTER_ALLOW_EXTERNAL_DOWNLOAD")) {
5929 $setsharekey = true;
5930 }
5931 if ($this->element == 'supplier_proposal' && getDolGlobalInt("SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD")) {
5932 $setsharekey = true;
5933 }
5934 if ($this->element == 'societe_rib' && getDolGlobalInt("SOCIETE_RIB_ALLOW_ONLINESIGN")) {
5935 $setsharekey = true;
5936 }
5937
5938 if ($setsharekey) {
5939 if (empty($ecmfile->share)) { // Because object not found or share not set yet
5940 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
5941 $ecmfile->share = getRandomPassword(true);
5942 }
5943 }
5944
5945 if ($result > 0) {
5946 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5947 $ecmfile->fullpath_orig = '';
5948 $ecmfile->gen_or_uploaded = 'generated';
5949 $ecmfile->description = ''; // indexed content
5950 $ecmfile->keywords = ''; // keyword content
5951 $result = $ecmfile->update($user);
5952 if ($result < 0) {
5953 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5954 return -1;
5955 }
5956 } else {
5957 $ecmfile->entity = $conf->entity;
5958 $ecmfile->filepath = $rel_dir;
5959 $ecmfile->filename = $filename;
5960 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5961 $ecmfile->fullpath_orig = '';
5962 $ecmfile->gen_or_uploaded = 'generated';
5963 $ecmfile->description = ''; // indexed content
5964 $ecmfile->keywords = ''; // keyword content
5965 $ecmfile->src_object_type = $this->table_element; // $this->table_name is 'myobject' or 'mymodule_myobject'.
5966 $ecmfile->src_object_id = $this->id;
5967
5968 $result = $ecmfile->create($user);
5969 if ($result < 0) {
5970 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5971 return -1;
5972 }
5973 }
5974
5975 /*$this->result['fullname']=$destfull;
5976 $this->result['filepath']=$ecmfile->filepath;
5977 $this->result['filename']=$ecmfile->filename;*/
5978 //var_dump($obj->update_main_doc_field);exit;
5979
5980 if ($update_main_doc_field && !empty($this->table_element)) {
5981 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath."/".$ecmfile->filename)."'";
5982 $sql .= " WHERE rowid = ".((int) $this->id);
5983
5984 $resql = $this->db->query($sql);
5985 if (!$resql) {
5986 dol_print_error($this->db);
5987 return -1;
5988 } else {
5989 $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
5990 }
5991 }
5992 }
5993
5994 return 1;
5995 }
5996
6004 public function addThumbs($file)
6005 {
6006 $file_osencoded = dol_osencode($file);
6007
6008 if (file_exists($file_osencoded)) {
6009 require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
6010
6011 $tmparraysize = getDefaultImageSizes();
6012 $maxwidthsmall = $tmparraysize['maxwidthsmall'];
6013 $maxheightsmall = $tmparraysize['maxheightsmall'];
6014 $maxwidthmini = $tmparraysize['maxwidthmini'];
6015 $maxheightmini = $tmparraysize['maxheightmini'];
6016 //$quality = $tmparraysize['quality'];
6017 $quality = 50; // For thumbs, we force quality to 50
6018
6019 // Create small thumbs for company (Ratio is near 16/9)
6020 // Used on logon for example
6021 vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
6022
6023 // Create mini thumbs for company (Ratio is near 16/9)
6024 // Used on menu or for setup page for example
6025 vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
6026 }
6027 }
6028
6036 public function delThumbs($file)
6037 {
6038 $imgThumbName = getImageFileNameForSize($file, '_small'); // Full path of thumb file
6039 dol_delete_file($imgThumbName);
6040 $imgThumbName = getImageFileNameForSize($file, '_mini'); // Full path of thumb file
6041 dol_delete_file($imgThumbName);
6042 }
6043
6044
6045 /* Functions common to commonobject and commonobjectline */
6046
6047 /* For default values */
6048
6062 public function getDefaultCreateValueFor($fieldname, $alternatevalue = null, $type = 'alphanohtml')
6063 {
6064 // If param here has been posted, we use this value first.
6065 if (GETPOSTISSET($fieldname)) {
6066 return GETPOST($fieldname, $type, 3);
6067 }
6068
6069 if (isset($alternatevalue)) {
6070 return $alternatevalue;
6071 }
6072
6073 $newelement = $this->element;
6074 if ($newelement == 'facture') {
6075 $newelement = 'invoice';
6076 }
6077 if ($newelement == 'commande') {
6078 $newelement = 'order';
6079 }
6080 if (empty($newelement)) {
6081 dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
6082 return '';
6083 }
6084
6085 $keyforfieldname = strtoupper($newelement.'_DEFAULT_'.$fieldname);
6086 //var_dump($keyforfieldname);
6087 if (getDolGlobalString($keyforfieldname)) {
6088 return getDolGlobalString($keyforfieldname);
6089 }
6090
6091 // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
6092 // store content into $conf->cache['overwrite_default']
6093
6094 return '';
6095 }
6096
6097
6098 /* For triggers */
6099
6100
6101 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6112 public function call_trigger($triggerName, $user)
6113 {
6114 // phpcs:enable
6115 global $langs, $conf;
6116
6117 if (!empty(self::TRIGGER_PREFIX) && strpos($triggerName, self::TRIGGER_PREFIX . '_') !== 0) {
6118 dol_print_error(null, 'The trigger "' . $triggerName . '" does not start with "' . self::TRIGGER_PREFIX . '_" as required.');
6119 exit;
6120 }
6121 if (!is_object($langs)) { // If lang was not defined, we set it. It is required by run_triggers().
6122 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
6123 $langs = new Translate('', $conf);
6124 }
6125
6126 include_once DOL_DOCUMENT_ROOT.'/core/class/interfaces.class.php';
6127 $interface = new Interfaces($this->db);
6128 $result = $interface->run_triggers($triggerName, $this, $user, $langs, $conf);
6129
6130 if ($result < 0) {
6131 if (!empty($this->errors)) {
6132 $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.
6133 } else {
6134 $this->errors = $interface->errors;
6135 }
6136 }
6137 return $result;
6138 }
6139
6140
6141 /* Functions for data in other language */
6142
6143
6152 {
6153 // To avoid SQL errors. Probably not the better solution though
6154 if (!$this->element) {
6155 return 0;
6156 }
6157 if (!($this->id > 0)) {
6158 return 0;
6159 }
6160 if (is_array($this->array_languages)) {
6161 return 1;
6162 }
6163
6164 $this->array_languages = array();
6165
6166 $element = $this->element;
6167 if ($element == 'categorie') {
6168 $element = 'categories'; // For compatibility
6169 }
6170
6171 // Request to get translation values for object
6172 $sql = "SELECT rowid, property, lang , value";
6173 $sql .= " FROM ".$this->db->prefix()."object_lang";
6174 $sql .= " WHERE type_object = '".$this->db->escape($element)."'";
6175 $sql .= " AND fk_object = ".((int) $this->id);
6176
6177 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6178 $resql = $this->db->query($sql);
6179 if ($resql) {
6180 $numrows = $this->db->num_rows($resql);
6181 if ($numrows) {
6182 $i = 0;
6183 while ($i < $numrows) {
6184 $obj = $this->db->fetch_object($resql);
6185 $key = $obj->property;
6186 $value = $obj->value;
6187 $codelang = $obj->lang;
6188 $type = $this->fields[$key]['type'];
6189
6190 // we can add this attribute to object
6191 if (preg_match('/date/', $type)) {
6192 $this->array_languages[$key][$codelang] = $this->db->jdate($value);
6193 } else {
6194 $this->array_languages[$key][$codelang] = $value;
6195 }
6196
6197 $i++;
6198 }
6199 }
6200
6201 $this->db->free($resql);
6202
6203 if ($numrows) {
6204 return $numrows;
6205 } else {
6206 return 0;
6207 }
6208 } else {
6209 dol_print_error($this->db);
6210 return -1;
6211 }
6212 }
6213
6220 public function setValuesForExtraLanguages($onlykey = '')
6221 {
6222 // Get extra fields
6223 foreach ($_POST as $postfieldkey => $postfieldvalue) {
6224 $tmparray = explode('-', $postfieldkey);
6225 if ($tmparray[0] != 'field') {
6226 continue;
6227 }
6228
6229 $element = $tmparray[1];
6230 $key = $tmparray[2];
6231 $codelang = $tmparray[3];
6232 //var_dump("postfieldkey=".$postfieldkey." element=".$element." key=".$key." codelang=".$codelang);
6233
6234 if (!empty($onlykey) && $key != $onlykey) {
6235 continue;
6236 }
6237 if ($element != $this->element) {
6238 continue;
6239 }
6240
6241 $key_type = $this->fields[$key]['type'];
6242
6243 $enabled = 1;
6244 if (isset($this->fields[$key]['enabled'])) {
6245 $enabled = (int) dol_eval($this->fields[$key]['enabled'], 1, 1, '1');
6246 }
6247 /*$perms = 1;
6248 if (isset($this->fields[$key]['perms']))
6249 {
6250 $perms = (int) dol_eval($this->fields[$key]['perms'], 1, 1, '1');
6251 }*/
6252 if (empty($enabled)) {
6253 continue;
6254 }
6255 //if (empty($perms)) continue;
6256
6257 if (in_array($key_type, array('date'))) {
6258 // Clean parameters
6259 // TODO GMT date in memory must be GMT so we should add gm=true in parameters
6260 $value_key = dol_mktime(0, 0, 0, GETPOSTINT($postfieldkey."month"), GETPOSTINT($postfieldkey."day"), GETPOSTINT($postfieldkey."year"));
6261 } elseif (in_array($key_type, array('datetime'))) {
6262 // Clean parameters
6263 // TODO GMT date in memory must be GMT so we should add gm=true in parameters
6264 $value_key = dol_mktime(GETPOSTINT($postfieldkey."hour"), GETPOSTINT($postfieldkey."min"), 0, GETPOSTINT($postfieldkey."month"), GETPOSTINT($postfieldkey."day"), GETPOSTINT($postfieldkey."year"));
6265 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
6266 $value_arr = GETPOST($postfieldkey, 'array'); // check if an array
6267 if (!empty($value_arr)) {
6268 $value_key = implode(',', $value_arr);
6269 } else {
6270 $value_key = '';
6271 }
6272 } elseif (in_array($key_type, array('price', 'double'))) {
6273 $value_arr = GETPOST($postfieldkey, 'alpha');
6274 $value_key = price2num($value_arr);
6275 } else {
6276 $value_key = GETPOST($postfieldkey);
6277 if (in_array($key_type, array('link')) && $value_key == '-1') {
6278 $value_key = '';
6279 }
6280 }
6281
6282 $this->array_languages[$key][$codelang] = $value_key;
6283
6284 /*if ($nofillrequired) {
6285 $langs->load('errors');
6286 setEventMessages($langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required), null, 'errors');
6287 return -1;
6288 }*/
6289 }
6290
6291 return 1;
6292 }
6293
6294
6295 /* Functions for extrafields */
6296
6303 public function fetchNoCompute($id)
6304 {
6305 global $conf;
6306
6307 $savDisableCompute = $conf->disable_compute;
6308 $conf->disable_compute = 1;
6309
6310 $ret = $this->fetch($id); /* @phpstan-ignore-line */
6311
6312 $conf->disable_compute = $savDisableCompute;
6313
6314 return $ret;
6315 }
6316
6317 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6327 public function fetch_optionals($rowid = null, $optionsArray = null)
6328 {
6329 // phpcs:enable
6330 global $conf, $extrafields;
6331
6332 if (empty($rowid)) {
6333 $rowid = $this->id;
6334 }
6335 if (empty($rowid) && isset($this->rowid)) {
6336 $rowid = $this->rowid; // deprecated
6337 }
6338
6339 // To avoid SQL errors. Probably not the better solution though
6340 if (!$this->table_element) {
6341 return 0;
6342 }
6343
6344 $this->array_options = array();
6345
6346 if (!is_array($optionsArray)) {
6347 // If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
6348 if (!isset($extrafields) || !is_object($extrafields)) {
6349 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6350 $extrafields = new ExtraFields($this->db);
6351 }
6352
6353 // Load array of extrafields for elementype = $this->table_element
6354 if (empty($extrafields->attributes[$this->table_element]['loaded'])) {
6355 $extrafields->fetch_name_optionals_label($this->table_element);
6356 }
6357 $optionsArray = (!empty($extrafields->attributes[$this->table_element]['label']) ? $extrafields->attributes[$this->table_element]['label'] : null);
6358 } else {
6359 global $extrafields;
6360 dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
6361 }
6362
6363 $table_element = $this->table_element;
6364 if ($table_element == 'categorie') {
6365 $table_element = 'categories'; // For compatibility
6366 }
6367
6368 // Request to get complementary values
6369 if (is_array($optionsArray) && count($optionsArray) > 0) {
6370 $sql = "SELECT rowid";
6371 foreach ($optionsArray as $name => $label) {
6372 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || (!in_array($extrafields->attributes[$this->table_element]['type'][$name], ['separate', 'point', 'multipts', 'linestrg','polygon']))) {
6373 $sql .= ", ".$name;
6374 }
6375 // use geo sql fonction to read as text
6376 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] == 'point') {
6377 $sql .= ", ST_AsWKT(".$name.") as ".$name;
6378 }
6379 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] == 'multipts') {
6380 $sql .= ", ST_AsWKT(".$name.") as ".$name;
6381 }
6382 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] == 'linestrg') {
6383 $sql .= ", ST_AsWKT(".$name.") as ".$name;
6384 }
6385 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] == 'polygon') {
6386 $sql .= ", ST_AsWKT(".$name.") as ".$name;
6387 }
6388 }
6389 $sql .= " FROM ".$this->db->prefix().$table_element."_extrafields";
6390 $sql .= " WHERE fk_object = ".((int) $rowid);
6391
6392 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6393 $resql = $this->db->query($sql);
6394 if ($resql) {
6395 $numrows = $this->db->num_rows($resql);
6396 if ($numrows) {
6397 $tab = $this->db->fetch_array($resql);
6398
6399 foreach ($tab as $key => $value) {
6400 // 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)
6401 if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && !is_int($key)) {
6402 // we can add this attribute to object
6403 if (!empty($extrafields->attributes[$this->table_element]) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date', 'datetime'))) {
6404 //var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
6405 $this->array_options["options_".$key] = $this->db->jdate($value);
6406 } else {
6407 $this->array_options["options_".$key] = $value;
6408 }
6409
6410 //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
6411 }
6412 if (!empty($extrafields->attributes[$this->table_element]['type'][$key]) && $extrafields->attributes[$this->table_element]['type'][$key] == 'password') {
6413 if (!empty($value) && preg_match('/^dolcrypt:/', $value)) {
6414 $this->array_options["options_".$key] = dolDecrypt($value);
6415 }
6416 }
6417 }
6418 } else {
6423 if (is_array($extrafields->attributes[$this->table_element]['label'])) {
6424 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
6425 $this->array_options['options_' . $key] = null;
6426 }
6427 }
6428 }
6429
6430 // If field is a computed field, value must become result of compute (regardless of whether a row exists
6431 // in the element's extrafields table)
6432 if (is_array($extrafields->attributes[$this->table_element]['label'])) {
6433 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
6434 if (!empty($extrafields->attributes[$this->table_element]) && !empty($extrafields->attributes[$this->table_element]['computed'][$key])) {
6435 //var_dump($conf->disable_compute);
6436 if (empty($conf->disable_compute)) {
6437 global $objectoffield; // We set a global variable to $objectoffield so
6438 $objectoffield = $this; // we can use it inside computed formula
6439 $this->array_options['options_' . $key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0, '2');
6440 }
6441 }
6442 }
6443 }
6444
6445 $this->db->free($resql);
6446
6447 if ($numrows) {
6448 return $numrows;
6449 } else {
6450 return 0;
6451 }
6452 } else {
6453 $this->errors[] = $this->db->lasterror;
6454 return -1;
6455 }
6456 }
6457 return 0;
6458 }
6459
6466 public function deleteExtraFields()
6467 {
6468 global $conf;
6469
6470 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6471 return 0;
6472 }
6473
6474 $this->db->begin();
6475
6476 $table_element = $this->table_element;
6477 if ($table_element == 'categorie') {
6478 $table_element = 'categories'; // For compatibility
6479 }
6480
6481 dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
6482
6483 $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6484
6485 $resql = $this->db->query($sql_del);
6486 if (!$resql) {
6487 $this->error = $this->db->lasterror();
6488 $this->db->rollback();
6489 return -1;
6490 } else {
6491 $this->db->commit();
6492 return 1;
6493 }
6494 }
6495
6506 public function insertExtraFields($trigger = '', $userused = null)
6507 {
6508 global $conf, $langs, $user;
6509
6510 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6511 return 0;
6512 }
6513
6514 if (empty($userused)) {
6515 $userused = $user;
6516 }
6517
6518 $error = 0;
6519
6520 if (!empty($this->array_options)) {
6521 // Check parameters
6522 $langs->load('admin');
6523 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6524 $extrafields = new ExtraFields($this->db);
6525 $target_extrafields = $extrafields->fetch_name_optionals_label($this->table_element);
6526
6527 // Eliminate copied source object extra fields that do not exist in target object
6528 $new_array_options = array();
6529 foreach ($this->array_options as $key => $value) {
6530 if (in_array(substr($key, 8), array_keys($target_extrafields))) { // We remove the 'options_' from $key for test
6531 $new_array_options[$key] = $value;
6532 } elseif (in_array($key, array_keys($target_extrafields))) { // We test on $key that does not contain the 'options_' prefix
6533 $new_array_options['options_'.$key] = $value;
6534 }
6535 }
6536
6537 foreach ($new_array_options as $key => $value) {
6538 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6539 $attributeType = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
6540 $attributeLabel = $langs->transnoentities($extrafields->attributes[$this->table_element]['label'][$attributeKey]);
6541 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
6542 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
6543 $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
6544 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$attributeKey];
6545
6546 // If we clone, we have to clean unique extrafields to prevent duplicates.
6547 // This behaviour can be prevented by external code by changing $this->context['createfromclone'] value in createFrom hook
6548 if (!empty($this->context['createfromclone']) && $this->context['createfromclone'] == 'createfromclone' && !empty($attributeUnique)) {
6549 $new_array_options[$key] = null;
6550 }
6551
6552 // Similar code than into insertExtraFields
6553 if ($attributeRequired) {
6554 $v = $this->array_options[$key];
6555 if (ExtraFields::isEmptyValue($v, $attributeType)) {
6556 $langs->load("errors");
6557 dol_syslog("Mandatory field '".$key."' is empty during create and set to required into definition of extrafields");
6558 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6559 return -1;
6560 }
6561 }
6562
6563 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6564 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6565
6566 if (!empty($attrfieldcomputed)) {
6567 if (getDolGlobalString('MAIN_STORE_COMPUTED_EXTRAFIELDS')) {
6568 $value = dol_eval($attrfieldcomputed, 1, 0, '2');
6569 dol_syslog($langs->trans("Extrafieldcomputed")." on ".$attributeLabel."(".$value.")", LOG_DEBUG);
6570 $new_array_options[$key] = $value;
6571 } else {
6572 $new_array_options[$key] = null;
6573 }
6574 }
6575
6576 switch ($attributeType) {
6577 case 'int':
6578 if (!is_numeric($value) && $value != '') {
6579 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6580 return -1;
6581 } elseif ($value == '') {
6582 $new_array_options[$key] = null;
6583 }
6584 break;
6585 case 'price':
6586 case 'double':
6587 $value = price2num($value);
6588 if (!is_numeric($value) && $value != '') {
6589 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." for ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6590 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6591 return -1;
6592 } elseif ($value == '') {
6593 $value = null;
6594 }
6595 //dol_syslog("double value"." on ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6596 $new_array_options[$key] = $value;
6597 break;
6598 /*case 'select': // Not required, we chose value='0' for undefined values
6599 if ($value=='-1')
6600 {
6601 $this->array_options[$key] = null;
6602 }
6603 break;*/
6604 case 'password':
6605 $algo = '';
6606 if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
6607 // If there is an encryption choice, we use it to encrypt data before insert
6608 $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
6609 $algo = reset($tmparrays);
6610 if ($algo != '') {
6611 //global $action; // $action may be 'create', 'update', 'update_extras'...
6612 //var_dump($action);
6613 //var_dump($this->oldcopy);exit;
6614 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
6615 //var_dump('algo='.$algo.' '.$this->oldcopy->array_options[$key].' -> '.$this->array_options[$key]);
6616 if (isset($this->oldcopy->array_options[$key]) && $this->array_options[$key] == $this->oldcopy->array_options[$key]) {
6617 // If old value encrypted in database is same than submitted new value, it means we don't change it, so we don't update.
6618 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
6619 if (!preg_match('/^dolcrypt:/', $this->array_options[$key])) {
6620 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6621 } else {
6622 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6623 }
6624 } else {
6625 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6626 }
6627 } else {
6628 // If value has changed
6629 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
6630 if (!preg_match('/^dolcrypt:/', $this->array_options[$key])) {
6631 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6632 } else {
6633 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6634 }
6635 } else {
6636 $new_array_options[$key] = dol_hash($this->array_options[$key], $algo);
6637 }
6638 }
6639 } else {
6640 //var_dump('jjj'.$algo.' '.$this->oldcopy->array_options[$key].' -> '.$this->array_options[$key]);
6641 // If this->oldcopy is not defined, we can't know if we change attribute or not, so we must keep value
6642 if ($algo == 'dolcrypt' && !preg_match('/^dolcrypt:/', $this->array_options[$key])) { // dolibarr reversible encryption
6643 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6644 } else {
6645 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6646 }
6647 }
6648 } else {
6649 // No encryption
6650 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6651 }
6652 } else { // Common usage
6653 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6654 }
6655 break;
6656 case 'date':
6657 case 'datetime':
6658 // If data is a string instead of a timestamp, we convert it
6659 if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6660 $this->array_options[$key] = strtotime($this->array_options[$key]);
6661 }
6662 $new_array_options[$key] = $this->db->idate($this->array_options[$key]);
6663 break;
6664 case 'datetimegmt':
6665 // If data is a string instead of a timestamp, we convert it
6666 if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6667 $this->array_options[$key] = strtotime($this->array_options[$key]);
6668 }
6669 $new_array_options[$key] = $this->db->idate($this->array_options[$key], 'gmt');
6670 break;
6671 case 'link':
6672 $param_list = array_keys($attributeParam['options']);
6673 // 0 : ObjectName
6674 // 1 : classPath
6675 $InfoFieldList = explode(":", $param_list[0]);
6676 dol_include_once($InfoFieldList[1]);
6677 if ($InfoFieldList[0] && class_exists($InfoFieldList[0])) {
6678 if ($value == '-1') { // -1 is key for no defined in combo list of objects
6679 $new_array_options[$key] = '';
6680 } elseif ($value) {
6681 $object = new $InfoFieldList[0]($this->db);
6682 if (is_numeric($value)) {
6683 $res = $object->fetch($value); // Common case
6684 } else {
6685 $res = $object->fetch('', $value); // For compatibility
6686 }
6687
6688 if ($res > 0) {
6689 $new_array_options[$key] = $object->id;
6690 } else {
6691 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
6692 return -1;
6693 }
6694 }
6695 } else {
6696 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6697 }
6698 break;
6699 case 'checkbox':
6700 case 'chkbxlst':
6701 if (is_array($this->array_options[$key])) {
6702 $new_array_options[$key] = implode(',', $this->array_options[$key]);
6703 } else {
6704 $new_array_options[$key] = $this->array_options[$key];
6705 }
6706 break;
6707 }
6708 }
6709
6710 $this->db->begin();
6711
6712 $table_element = $this->table_element;
6713 if ($table_element == 'categorie') {
6714 $table_element = 'categories'; // For compatibility
6715 }
6716
6717 dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG);
6718
6719 $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6720 $this->db->query($sql_del);
6721
6722 $sql = "INSERT INTO ".$this->db->prefix().$table_element."_extrafields (fk_object";
6723 foreach ($new_array_options as $key => $value) {
6724 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6725 // Add field of attribute
6726 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator
6727 $sql .= ",".$attributeKey;
6728 }
6729 }
6730 // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6731 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6732 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6733 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6734 $sql .= ",".$tmpkey;
6735 }
6736 }
6737 }
6738 $sql .= ") VALUES (".$this->id;
6739
6740 foreach ($new_array_options as $key => $value) {
6741 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6742 // Add field of attribute
6743 if (!in_array($extrafields->attributes[$this->table_element]['type'][$attributeKey], ['separate', 'point', 'multipts', 'linestrg', 'polygon'])) { // Only for other type than separator)
6744 if ($new_array_options[$key] != '' || $new_array_options[$key] == '0') {
6745 $sql .= ",'".$this->db->escape($new_array_options[$key])."'";
6746 } else {
6747 $sql .= ",null";
6748 }
6749 }
6750 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] == 'point') { // for point type
6751 if (!empty($new_array_options[$key])) {
6752 $sql .= ",ST_PointFromText('".$this->db->escape($new_array_options[$key])."')";
6753 } else {
6754 $sql .= ",null";
6755 }
6756 }
6757 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] == 'multipts') { // for point type
6758 if (!empty($new_array_options[$key])) {
6759 $sql .= ",ST_MultiPointFromText('".$this->db->escape($new_array_options[$key])."')";
6760 } else {
6761 $sql .= ",null";
6762 }
6763 }
6764 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] == 'linestrg') { // for linestring type
6765 if (!empty($new_array_options[$key])) {
6766 $sql .= ",ST_LineFromText('".$this->db->escape($new_array_options[$key])."')";
6767 } else {
6768 $sql .= ",null";
6769 }
6770 }
6771 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] == 'polygon') { // for polygon type
6772 if (!empty($new_array_options[$key])) {
6773 $sql .= ",ST_PolyFromText('".$this->db->escape($new_array_options[$key])."')";
6774 } else {
6775 $sql .= ",null";
6776 }
6777 }
6778 }
6779 // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6780 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6781 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6782 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6783 if (in_array($tmpval, array('int', 'double', 'price'))) {
6784 $sql .= ", 0";
6785 } else {
6786 $sql .= ", ''";
6787 }
6788 }
6789 }
6790 }
6791
6792 $sql .= ")";
6793
6794 $resql = $this->db->query($sql);
6795 if (!$resql) {
6796 $this->error = $this->db->lasterror();
6797 $error++;
6798 }
6799
6800 if (!$error && $trigger) {
6801 // Call trigger
6802 $this->context = array('extrafieldaddupdate' => 1);
6803 $result = $this->call_trigger($trigger, $userused);
6804 if ($result < 0) {
6805 $error++;
6806 }
6807 // End call trigger
6808 }
6809
6810 if ($error) {
6811 $this->db->rollback();
6812 return -1;
6813 } else {
6814 $this->db->commit();
6815 return 1;
6816 }
6817 } else {
6818 return 0;
6819 }
6820 }
6821
6832 public function insertExtraLanguages($trigger = '', $userused = null)
6833 {
6834 global $conf, $langs, $user;
6835
6836 if (empty($userused)) {
6837 $userused = $user;
6838 }
6839
6840 $error = 0;
6841
6842 if (getDolGlobalString('MAIN_EXTRALANGUAGES_DISABLED')) {
6843 return 0; // For avoid conflicts if trigger used
6844 }
6845
6846 if (is_array($this->array_languages)) {
6847 $new_array_languages = $this->array_languages;
6848
6849 foreach ($new_array_languages as $key => $value) {
6850 $attributeKey = $key;
6851 $attributeType = $this->fields[$attributeKey]['type'];
6852 $attributeLabel = $this->fields[$attributeKey]['label'];
6853
6854 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6855 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6856
6857 switch ($attributeType) {
6858 case 'int':
6859 if (is_array($value) || (!is_numeric($value) && $value != '')) {
6860 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6861 return -1;
6862 } elseif ($value == '') {
6863 $new_array_languages[$key] = null;
6864 }
6865 break;
6866 case 'double':
6867 $value = price2num($value);
6868 if (!is_numeric($value) && $value != '') {
6869 dol_syslog($langs->trans("ExtraLanguageHasWrongValue")." on ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6870 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6871 return -1;
6872 } elseif ($value == '') {
6873 $new_array_languages[$key] = null;
6874 } else {
6875 $new_array_languages[$key] = $value;
6876 }
6877 break;
6878 /*case 'select': // Not required, we chose value='0' for undefined values
6879 if ($value=='-1')
6880 {
6881 $this->array_options[$key] = null;
6882 }
6883 break;*/
6884 }
6885 }
6886
6887 $this->db->begin();
6888
6889 $table_element = $this->table_element;
6890 if ($table_element == 'categorie') { // TODO Rename table llx_categories_extrafields into llx_categorie_extrafields so we can remove this.
6891 $table_element = 'categories'; // For compatibility
6892 }
6893
6894 dol_syslog(get_class($this)."::insertExtraLanguages delete then insert", LOG_DEBUG);
6895
6896 foreach ($new_array_languages as $key => $langcodearray) { // $key = 'name', 'town', ...
6897 foreach ($langcodearray as $langcode => $value) {
6898 $sql_del = "DELETE FROM ".$this->db->prefix()."object_lang";
6899 $sql_del .= " WHERE fk_object = ".((int) $this->id)." AND property = '".$this->db->escape($key)."' AND type_object = '".$this->db->escape($table_element)."'";
6900 $sql_del .= " AND lang = '".$this->db->escape($langcode)."'";
6901 $this->db->query($sql_del);
6902
6903 if ($value !== '') {
6904 $sql = "INSERT INTO ".$this->db->prefix()."object_lang (fk_object, property, type_object, lang, value";
6905 $sql .= ") VALUES (".$this->id.", '".$this->db->escape($key)."', '".$this->db->escape($table_element)."', '".$this->db->escape($langcode)."', '".$this->db->escape($value)."'";
6906 $sql .= ")";
6907
6908 $resql = $this->db->query($sql);
6909 if (!$resql) {
6910 $this->error = $this->db->lasterror();
6911 $error++;
6912 break;
6913 }
6914 }
6915 }
6916 }
6917
6918 if (!$error && $trigger) {
6919 // Call trigger
6920 $this->context = array('extralanguagesaddupdate' => 1);
6921 $result = $this->call_trigger($trigger, $userused);
6922 if ($result < 0) {
6923 $error++;
6924 }
6925 // End call trigger
6926 }
6927
6928 if ($error) {
6929 $this->db->rollback();
6930 return -1;
6931 } else {
6932 $this->db->commit();
6933 return 1;
6934 }
6935 } else {
6936 return 0;
6937 }
6938 }
6939
6950 public function updateExtraField($key, $trigger = null, $userused = null)
6951 {
6952 global $conf, $langs, $user, $hookmanager;
6953
6954 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6955 return 0;
6956 }
6957
6958 if (empty($userused)) {
6959 $userused = $user;
6960 }
6961
6962 $error = 0;
6963
6964 if (!empty($this->array_options) && isset($this->array_options["options_".$key])) {
6965 // Check parameters
6966 $langs->load('admin');
6967 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6968 $extrafields = new ExtraFields($this->db);
6969 $extrafields->fetch_name_optionals_label($this->table_element);
6970
6971 $value = $this->array_options["options_".$key];
6972
6973 $attributeKey = $key;
6974 $attributeType = $extrafields->attributes[$this->table_element]['type'][$key];
6975 $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key];
6976 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key];
6977 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
6978 $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
6979 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$key];
6980
6981 // Similar code than into insertExtraFields
6982 if ($attributeRequired) {
6983 $mandatorypb = false;
6984 if ($attributeType == 'link' && $this->array_options["options_".$key] == '-1') {
6985 $mandatorypb = true;
6986 }
6987 if ($this->array_options["options_".$key] === '') {
6988 $mandatorypb = true;
6989 }
6990 if ($mandatorypb) {
6991 $langs->load("errors");
6992 dol_syslog("Mandatory field 'options_".$key."' is empty during update and set to required into definition of extrafields");
6993 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6994 return -1;
6995 }
6996 }
6997
6998 // $new_array_options will be used for direct update, so must contains formatted data for the UPDATE.
6999 $new_array_options = $this->array_options;
7000
7001 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
7002 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
7003 if (!empty($attrfieldcomputed)) {
7004 if (getDolGlobalString('MAIN_STORE_COMPUTED_EXTRAFIELDS')) {
7005 $value = dol_eval($attrfieldcomputed, 1, 0, '2');
7006 dol_syslog($langs->trans("Extrafieldcomputed")." on ".$attributeLabel."(".$value.")", LOG_DEBUG);
7007
7008 $new_array_options["options_".$key] = $value;
7009
7010 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7011 } else {
7012 $new_array_options["options_".$key] = null;
7013
7014 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7015 }
7016 }
7017
7018 switch ($attributeType) {
7019 case 'int':
7020 if (!is_numeric($value) && $value != '') {
7021 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
7022 return -1;
7023 } elseif ($value === '') {
7024 $new_array_options["options_".$key] = null;
7025
7026 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7027 }
7028 break;
7029 case 'price':
7030 case 'double':
7031 $value = price2num($value);
7032 if (!is_numeric($value) && $value != '') {
7033 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." on ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
7034 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
7035 return -1;
7036 } elseif ($value === '') {
7037 $value = null;
7038 }
7039 //dol_syslog("double value"." on ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
7040 $new_array_options["options_".$key] = $value;
7041
7042 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7043 break;
7044 /*case 'select': // Not required, we chose value='0' for undefined values
7045 if ($value=='-1')
7046 {
7047 $new_array_options["options_".$key] = $value;
7048
7049 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7050 }
7051 break;*/
7052 case 'password':
7053 $algo = '';
7054 if ($this->array_options["options_".$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
7055 // If there is an encryption choice, we use it to encrypt data before insert
7056 $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
7057 $algo = reset($tmparrays);
7058 if ($algo != '') {
7059 //global $action; // $action may be 'create', 'update', 'update_extras'...
7060 //var_dump($action);
7061 //var_dump($this->oldcopy);exit;
7062 //var_dump($key.' '.$this->array_options["options_".$key].' '.$algo);
7063 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
7064 //var_dump($this->oldcopy->array_options["options_".$key]); var_dump($this->array_options["options_".$key]);
7065 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.
7066 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
7067 if (!preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) {
7068 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]); // warning, must be called when on the master
7069 } else {
7070 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7071 }
7072 } else {
7073 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7074 }
7075 } else {
7076 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
7077 if (!preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) {
7078 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]);
7079 } else {
7080 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7081 }
7082 } else {
7083 $new_array_options["options_".$key] = dol_hash($this->array_options["options_".$key], $algo);
7084 }
7085 }
7086 } else {
7087 if ($algo == 'dolcrypt' && !preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) { // dolibarr reversible encryption
7088 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]); // warning, must be called when on the master
7089 } else {
7090 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7091 }
7092 }
7093 } else {
7094 // No encryption
7095 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7096 }
7097 } else { // Common usage
7098 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7099 }
7100
7101 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7102 break;
7103 case 'date':
7104 case 'datetime':
7105 if (empty($this->array_options["options_".$key])) {
7106 $new_array_options["options_".$key] = null;
7107
7108 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7109 } else {
7110 $new_array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key]);
7111 }
7112 break;
7113 case 'datetimegmt':
7114 if (empty($this->array_options["options_".$key])) {
7115 $new_array_options["options_".$key] = null;
7116
7117 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7118 } else {
7119 $new_array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key], 'gmt');
7120 }
7121 break;
7122 case 'boolean':
7123 if (empty($this->array_options["options_".$key])) {
7124 $new_array_options["options_".$key] = null;
7125
7126 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7127 }
7128 break;
7129 case 'link':
7130 if ($this->array_options["options_".$key] === '') {
7131 $new_array_options["options_".$key] = null;
7132
7133 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7134 }
7135 break;
7136 /*
7137 case 'link':
7138 $param_list = array_keys($attributeParam['options']);
7139 // 0 : ObjectName
7140 // 1 : classPath
7141 $InfoFieldList = explode(":", $param_list[0]);
7142 dol_include_once($InfoFieldList[1]);
7143 if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
7144 {
7145 if ($value == '-1') // -1 is key for no defined in combo list of objects
7146 {
7147 $new_array_options[$key] = '';
7148 } elseif ($value) {
7149 $object = new $InfoFieldList[0]($this->db);
7150 if (is_numeric($value)) $res = $object->fetch($value); // Common case
7151 else $res = $object->fetch('', $value); // For compatibility
7152
7153 if ($res > 0) $new_array_options[$key] = $object->id;
7154 else {
7155 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
7156 $this->db->rollback();
7157 return -1;
7158 }
7159 }
7160 } else {
7161 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
7162 }
7163 break;
7164 */
7165 case 'checkbox':
7166 case 'chkbxlst':
7167 $new_array_options = array();
7168 if (is_array($this->array_options["options_".$key])) {
7169 $new_array_options["options_".$key] = implode(',', $this->array_options["options_".$key]);
7170 } else {
7171 $new_array_options["options_".$key] = $this->array_options["options_".$key];
7172 }
7173
7174 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7175 break;
7176 }
7177
7178 $this->db->begin();
7179
7180 $linealreadyfound = 0;
7181
7182 // 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)
7183 $table_element = $this->table_element;
7184 if ($table_element == 'categorie') { // TODO Rename table llx_categories_extrafields into llx_categorie_extrafields so we can remove this.
7185 $table_element = 'categories'; // For compatibility
7186 }
7187
7188 $sql = "SELECT COUNT(rowid) as nb FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
7189 $resql = $this->db->query($sql);
7190 if ($resql) {
7191 $tmpobj = $this->db->fetch_object($resql);
7192 if ($tmpobj) {
7193 $linealreadyfound = $tmpobj->nb;
7194 }
7195 }
7196
7197 //var_dump('linealreadyfound='.$linealreadyfound.' sql='.$sql); exit;
7198 if ($linealreadyfound) {
7199 if ($this->array_options["options_".$key] === null) {
7200 $sql = "UPDATE ".$this->db->prefix().$table_element."_extrafields SET ".$key." = null";
7201 } else {
7202 $sql = "UPDATE ".$this->db->prefix().$table_element."_extrafields SET ".$key." = '".$this->db->escape($new_array_options["options_".$key])."'";
7203 }
7204 $sql .= " WHERE fk_object = ".((int) $this->id);
7205
7206 $resql = $this->db->query($sql);
7207 if (!$resql) {
7208 $error++;
7209 $this->error = $this->db->lasterror();
7210 }
7211 } else {
7212 $result = $this->insertExtraFields('', $user);
7213 if ($result < 0) {
7214 $error++;
7215 }
7216 }
7217
7218 if (!$error) {
7219 $parameters = array('key' => $key);
7220 global $action;
7221 $reshook = $hookmanager->executeHooks('updateExtraFieldBeforeCommit', $parameters, $this, $action);
7222 if ($reshook < 0) {
7223 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
7224 }
7225 }
7226
7227 if (!$error && $trigger) {
7228 // Call trigger
7229 $this->context = array('extrafieldupdate' => 1);
7230 $result = $this->call_trigger($trigger, $userused);
7231 if ($result < 0) {
7232 $error++;
7233 }
7234 // End call trigger
7235 }
7236
7237 if ($error) {
7238 dol_syslog(__METHOD__.$this->error, LOG_ERR);
7239 $this->db->rollback();
7240 return -1;
7241 } else {
7242 $this->db->commit();
7243 return 1;
7244 }
7245 } else {
7246 return 0;
7247 }
7248 }
7249
7256 public function getExtraField($key)
7257 {
7258 return $this->array_options['options_'.$key] ?? null;
7259 }
7260
7268 public function setExtraField($key, $value)
7269 {
7270 $this->array_options['options_'.$key] = $value;
7271 }
7272
7283 public function updateExtraLanguages($key, $trigger = null, $userused = null)
7284 {
7285 global $conf, $langs, $user;
7286
7287 if (empty($userused)) {
7288 $userused = $user;
7289 }
7290
7291 $error = 0;
7292
7293 if (getDolGlobalString('MAIN_EXTRALANGUAGES_DISABLED')) {
7294 return 0; // For avoid conflicts if trigger used
7295 }
7296
7297 return 0;
7298 }
7299
7300
7316 public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0, $nonewbutton = 0)
7317 {
7318 global $conf, $langs, $form;
7319
7320 if (!is_object($form)) {
7321 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
7322 $form = new Form($this->db);
7323 }
7324
7325 if (!empty($this->fields)) {
7326 $val = $this->fields[$key];
7327 }
7328
7329 // Validation tests and output
7330 $fieldValidationErrorMsg = '';
7331 $validationClass = '';
7332 $fieldValidationErrorMsg = $this->getFieldError($key);
7333 if (!empty($fieldValidationErrorMsg)) {
7334 $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
7335 } else {
7336 $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
7337 }
7338
7339 $valuemultiselectinput = array();
7340 $out = '';
7341 $type = '';
7342 $isDependList = 0;
7343 $param = array();
7344 $param['options'] = array();
7345 $reg = array();
7346 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7347 $size = !empty($this->fields[$key]['size']) ? $this->fields[$key]['size'] : 0;
7348 // Because we work on extrafields
7349 if (preg_match('/^(integer|link):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7350 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
7351 $type = 'link';
7352 } elseif (preg_match('/^(integer|link):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7353 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7354 $type = 'link';
7355 } elseif (preg_match('/^(integer|link):(.*):(.*)/i', $val['type'], $reg)) {
7356 $param['options'] = array($reg[2].':'.$reg[3] => 'N');
7357 $type = 'link';
7358 } elseif (preg_match('/^(sellist):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7359 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
7360 $type = 'sellist';
7361 } elseif (preg_match('/^(sellist):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7362 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7363 $type = 'sellist';
7364 } elseif (preg_match('/^(sellist):(.*):(.*)/i', $val['type'], $reg)) {
7365 $param['options'] = array($reg[2].':'.$reg[3] => 'N');
7366 $type = 'sellist';
7367 } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
7368 $param['options'] = array($reg[1] => 'N');
7369 $type = 'chkbxlst';
7370 } elseif (preg_match('/varchar\‍((\d+)\‍)/', $val['type'], $reg)) {
7371 $param['options'] = array();
7372 $type = 'varchar';
7373 $size = $reg[1];
7374 } elseif (preg_match('/varchar/', $val['type'])) {
7375 $param['options'] = array();
7376 $type = 'varchar';
7377 } else {
7378 $param['options'] = array();
7379 $type = $this->fields[$key]['type'];
7380 }
7381 //var_dump($type); var_dump($param['options']);
7382
7383 // Special case that force options and type ($type can be integer, varchar, ...)
7384 if (!empty($this->fields[$key]['arrayofkeyval']) && is_array($this->fields[$key]['arrayofkeyval'])) {
7385 $param['options'] = $this->fields[$key]['arrayofkeyval'];
7386 // Special case that prevent to force $type to have multiple input
7387 if (empty($this->fields[$key]['multiinput'])) {
7388 $type = (($this->fields[$key]['type'] == 'checkbox') ? $this->fields[$key]['type'] : 'select');
7389 }
7390 }
7391
7392 $label = $this->fields[$key]['label'];
7393 //$elementtype=$this->fields[$key]['elementtype']; // Seems not used
7394 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7395 $default = (!empty($this->fields[$key]['default']) ? $this->fields[$key]['default'] : '');
7396 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7397 $computed = (!empty($this->fields[$key]['computed']) ? $this->fields[$key]['computed'] : '');
7398 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7399 $unique = (!empty($this->fields[$key]['unique']) ? $this->fields[$key]['unique'] : 0);
7400 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7401 $required = (!empty($this->fields[$key]['required']) ? $this->fields[$key]['required'] : 0);
7402 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7403 $autofocusoncreate = (!empty($this->fields[$key]['autofocusoncreate']) ? $this->fields[$key]['autofocusoncreate'] : 0);
7404
7405 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7406 $langfile = (!empty($this->fields[$key]['langfile']) ? $this->fields[$key]['langfile'] : '');
7407 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7408 $list = (!empty($this->fields[$key]['list']) ? $this->fields[$key]['list'] : 0);
7409 $hidden = (in_array(abs($this->fields[$key]['visible']), array(0, 2)) ? 1 : 0);
7410
7411 $objectid = $this->id;
7412
7413 if ($computed) {
7414 if (!preg_match('/^search_/', $keyprefix)) {
7415 return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
7416 } else {
7417 return '';
7418 }
7419 }
7420
7421 // Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine
7422 if (empty($morecss) && !empty($val['css'])) {
7423 $morecss = $val['css'];
7424 } elseif (empty($morecss)) {
7425 if ($type == 'date') {
7426 $morecss = 'minwidth100imp';
7427 } elseif ($type == 'datetime' || $type == 'link') { // link means an foreign key to another primary id
7428 $morecss = 'minwidth200imp';
7429 } elseif (in_array($type, array('int', 'integer', 'price')) || preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', (string) $type)) {
7430 $morecss = 'maxwidth75';
7431 } elseif ($type == 'url') {
7432 $morecss = 'minwidth400';
7433 } elseif ($type == 'boolean') {
7434 $morecss = '';
7435 } else {
7436 if (is_numeric($size) && round((float) $size) < 12) {
7437 $morecss = 'minwidth100';
7438 } elseif (is_numeric($size) && round((float) $size) <= 48) {
7439 $morecss = 'minwidth200';
7440 } else {
7441 $morecss = 'minwidth400';
7442 }
7443 }
7444 }
7445
7446 // Add validation state class
7447 if (!empty($validationClass)) {
7448 $morecss .= $validationClass;
7449 }
7450
7451 if (in_array($type, array('date'))) {
7452 $tmp = explode(',', $size);
7453 $newsize = $tmp[0];
7454 $showtime = 0;
7455
7456 // Do not show current date when field not required (see selectDate() method)
7457 if (!$required && $value == '') {
7458 $value = '-1';
7459 }
7460
7461 // TODO Must also support $moreparam
7462 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
7463 } elseif (in_array($type, array('datetime'))) {
7464 $tmp = explode(',', $size);
7465 $newsize = $tmp[0];
7466 $showtime = 1;
7467
7468 // Do not show current date when field not required (see selectDate() method)
7469 if (!$required && $value == '') {
7470 $value = '-1';
7471 }
7472
7473 // TODO Must also support $moreparam
7474 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
7475 } elseif (in_array($type, array('duration'))) {
7476 $out = $form->select_duration($keyprefix.$key.$keysuffix, $value, 0, 'text', 0, 1);
7477 } elseif (in_array($type, array('int', 'integer'))) {
7478 $tmp = explode(',', $size);
7479 $newsize = $tmp[0];
7480 $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' : '').'>';
7481 } elseif (in_array($type, array('real'))) {
7482 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7483 } elseif (preg_match('/varchar/', (string) $type)) {
7484 $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' : '').'>';
7485 } elseif (in_array($type, array('email', 'mail', 'phone', 'url', 'ip'))) {
7486 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7487 } elseif (preg_match('/^text/', (string) $type)) {
7488 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7489 if (!empty($param['options'])) {
7490 // If the textarea field has a list of arrayofkeyval into its definition, we suggest a combo with possible values to fill the textarea.
7491 //var_dump($param['options']);
7492 $out .= $form->selectarray($keyprefix.$key.$keysuffix."_multiinput", $param['options'], '', 1, 0, 0, "flat maxwidthonphone".$morecss);
7493 $out .= "<script>";
7494 $out .= '
7495 function handlemultiinputdisabling(htmlname){
7496 console.log("We handle the disabling of used options for "+htmlname+"_multiinput");
7497 multiinput = $("#"+htmlname+"_multiinput");
7498 multiinput.find("option").each(function(){
7499 tmpval = $("#"+htmlname).val();
7500 tmpvalarray = tmpval.split(",");
7501 valtotest = $(this).val();
7502 if(tmpvalarray.includes(valtotest)){
7503 $(this).prop("disabled",true);
7504 } else {
7505 if($(this).prop("disabled") == true){
7506 console.log(valtotest)
7507 $(this).prop("disabled", false);
7508 }
7509 }
7510 });
7511 }
7512
7513 $(document).ready(function () {
7514 $("#'.$keyprefix.$key.$keysuffix.'_multiinput").on("change",function() {
7515 console.log("We add the selected value to the text area '.$keyprefix.$key.$keysuffix.'");
7516 tmpval = $("#'.$keyprefix.$key.$keysuffix.'").val();
7517 tmpvalarray = tmpval.split(",");
7518 valtotest = $(this).val();
7519 if(valtotest != -1 && !tmpvalarray.includes(valtotest)){
7520 if(tmpval == ""){
7521 tmpval = valtotest;
7522 } else {
7523 tmpval = tmpval + "," + valtotest;
7524 }
7525 $("#'.$keyprefix.$key.$keysuffix.'").val(tmpval);
7526 handlemultiinputdisabling("'.$keyprefix.$key.$keysuffix.'");
7527 }
7528 });
7529 $("#'.$keyprefix.$key.$keysuffix.'").on("change",function(){
7530 handlemultiinputdisabling($(this).attr("id"));
7531 });
7532 handlemultiinputdisabling("'.$keyprefix.$key.$keysuffix.'");
7533 })';
7534 $out .= "</script>";
7535 }
7536 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7537 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
7538 $out .= (string) $doleditor->Create(1, '', true, '', '', '', $morecss);
7539 } else {
7540 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7541 }
7542 } elseif (preg_match('/^html/', (string) $type)) {
7543 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7544 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7545 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && getDolGlobalInt('FCKEDITOR_ENABLE_SOCIETE'), ROWS_5, '90%');
7546 $out = (string) $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
7547 } else {
7548 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7549 }
7550 } elseif ($type == 'boolean') {
7551 $checked = '';
7552 if (!empty($value)) {
7553 $checked = ' checked value="1" ';
7554 } else {
7555 $checked = ' value="1" ';
7556 }
7557 $out = '<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
7558 } elseif ($type == 'price') {
7559 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7560 $value = price($value);
7561 }
7562 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '.$langs->getCurrencySymbol($conf->currency);
7563 } elseif (preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', (string) $type)) {
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 : '').'> ';
7568 } elseif ($type == 'select') { // combo list
7569 $out = '';
7570 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
7571 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7572 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7573 }
7574
7575 $tmpselect = '';
7576 $nbchoice = 0;
7577 foreach ($param['options'] as $keyb => $valb) {
7578 if ((string) $keyb == '') {
7579 continue;
7580 }
7581 if (strpos($valb, "|") !== false) {
7582 list($valb, $parent) = explode('|', $valb);
7583 }
7584 $nbchoice++;
7585 $tmpselect .= '<option value="'.$keyb.'"';
7586 $tmpselect .= (((string) $value == (string) $keyb) ? ' selected' : '');
7587 if (!empty($parent)) {
7588 $isDependList = 1;
7589 }
7590 $tmpselect .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
7591 $tmpselect .= '>'.$langs->trans($valb).'</option>';
7592 }
7593
7594 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7595 if ((!isset($this->fields[$key]['default'])) || ($this->fields[$key]['notnull'] != 1) || $nbchoice >= 2) {
7596 $out .= '<option value="0">&nbsp;</option>';
7597 }
7598 $out .= $tmpselect;
7599 $out .= '</select>';
7600 } elseif ($type == 'sellist') {
7601 $out = '';
7602 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
7603 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7604 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7605 }
7606
7607 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7608 if (is_array($param['options'])) {
7609 $tmpparamoptions = array_keys($param['options']);
7610 $paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
7611
7612 $InfoFieldList = explode(":", $paramoptions[0], 5);
7613 // 0 : tableName
7614 // 1 : label field name
7615 // 2 : key fields name (if different of rowid)
7616 // optional parameters...
7617 // 3 : key field parent (for dependent lists). How this is used ?
7618 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value. Or use USF on a second line separated by "\n".
7619 // 5 : string category type. This replace the filter.
7620 // 6 : ids categories list separated by comma for category root. This replace the filter.
7621 // 7 : sort field
7622
7623 // If there is filter
7624 if (! empty($InfoFieldList[4])) {
7625 $pos = 0;
7626 $parenthesisopen = 0;
7627 while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
7628 if (substr($InfoFieldList[4], $pos, 1) == '(') {
7629 $parenthesisopen++;
7630 }
7631 if (substr($InfoFieldList[4], $pos, 1) == ')') {
7632 $parenthesisopen--;
7633 }
7634 $pos++;
7635 }
7636 $tmpbefore = substr($InfoFieldList[4], 0, $pos);
7637 $tmpafter = substr($InfoFieldList[4], $pos + 1);
7638 //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
7639 $InfoFieldList[4] = $tmpbefore;
7640 if ($tmpafter !== '') {
7641 $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
7642 }
7643 //var_dump($InfoFieldList);
7644
7645 // Fix better compatibility with some old extrafield syntax filter "(field=123)"
7646 $reg = array();
7647 if (preg_match('/^\‍(?([a-z0-9]+)([=<>]+)(\d+)\‍)?$/i', $InfoFieldList[4], $reg)) {
7648 $InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
7649 }
7650
7651 //var_dump($InfoFieldList);
7652 }
7653
7654 //$Usf = empty($paramoptions[1]) ? '' :$paramoptions[1];
7655
7656 $parentName = '';
7657 $parentField = '';
7658
7659 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7660
7661 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7662 if (strpos($InfoFieldList[4], 'extra.') !== false) {
7663 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7664 } else {
7665 $keyList = $InfoFieldList[2].' as rowid';
7666 }
7667 }
7668 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7669 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7670 $keyList .= ', '.$parentField;
7671 }
7672
7673 $filter_categorie = false;
7674 if (count($InfoFieldList) > 5) {
7675 if ($InfoFieldList[0] == 'categorie') {
7676 $filter_categorie = true;
7677 }
7678 }
7679
7680 if (!$filter_categorie) {
7681 $fields_label = explode('|', $InfoFieldList[1]);
7682 if (is_array($fields_label)) {
7683 $keyList .= ', ';
7684 $keyList .= implode(', ', $fields_label);
7685 }
7686
7687 $sqlwhere = '';
7688 $sql = "SELECT " . $keyList;
7689 $sql .= " FROM " . $this->db->prefix() . $InfoFieldList[0];
7690
7691 if (!empty($InfoFieldList[4])) {
7692 // can use SELECT request
7693 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7694 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7695 }
7696
7697 // current object id can be use into filter
7698 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7699 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
7700 } else {
7701 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7702 }
7703
7704 // We have to join on extrafield table
7705 $errstr = '';
7706 if (strpos($InfoFieldList[4], 'extra') !== false) {
7707 $sql .= " as main, " . $this->db->sanitize($this->db->prefix() . $InfoFieldList[0]) . "_extrafields as extra";
7708 $sqlwhere .= " WHERE extra.fk_object = main." . $this->db->sanitize($InfoFieldList[2]);
7709 $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
7710 } else {
7711 $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
7712 }
7713 } else {
7714 $sqlwhere .= ' WHERE 1=1';
7715 }
7716
7717 // Add Usf filter on second line
7718 /*
7719 if ($Usf) {
7720 $errorstr = '';
7721 $sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
7722 if (!$errorstr) {
7723 $sqlwhere .= $sqlusf;
7724 } else {
7725 $sqlwhere .= " AND invalid_usf_filter_of_extrafield";
7726 }
7727 }
7728 */
7729
7730 // Some tables may have field, some other not. For the moment we disable it.
7731 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7732 $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7733 }
7734 $sql .= $sqlwhere;
7735 //print $sql;
7736
7737 // Note: $InfoFieldList can be 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter[:CategoryIdType[:CategoryIdList[:Sortfield]]]]]]'
7738 if (isset($InfoFieldList[7]) && preg_match('/^[a-z0-9_\-,]+$/i', $InfoFieldList[7])) {
7739 $sql .= " ORDER BY ".$this->db->escape($InfoFieldList[7]);
7740 } else {
7741 $sql .= " ORDER BY ".$this->db->sanitize(implode(', ', $fields_label));
7742 }
7743
7744 dol_syslog(get_class($this) . '::showInputField type=sellist', LOG_DEBUG);
7745 $resql = $this->db->query($sql);
7746 if ($resql) {
7747 $out .= '<option value="0">&nbsp;</option>';
7748 $num = $this->db->num_rows($resql);
7749 $i = 0;
7750 while ($i < $num) {
7751 $labeltoshow = '';
7752 $obj = $this->db->fetch_object($resql);
7753
7754 // Several field into label (eq table:code|libelle:rowid)
7755 $notrans = false;
7756 $fields_label = explode('|', $InfoFieldList[1]);
7757 if (count($fields_label) > 1) {
7758 $notrans = true;
7759 foreach ($fields_label as $field_toshow) {
7760 $labeltoshow .= $obj->$field_toshow . ' ';
7761 }
7762 } else {
7763 $labeltoshow = $obj->{$InfoFieldList[1]};
7764 }
7765 $labeltoshow = dol_trunc($labeltoshow, 45);
7766
7767 if ($value == $obj->rowid) {
7768 foreach ($fields_label as $field_toshow) {
7769 $translabel = $langs->trans($obj->$field_toshow);
7770 if ($translabel != $obj->$field_toshow) {
7771 $labeltoshow = dol_trunc($translabel) . ' ';
7772 } else {
7773 $labeltoshow = dol_trunc($obj->$field_toshow) . ' ';
7774 }
7775 }
7776 $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7777 } else {
7778 if (!$notrans) {
7779 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7780 if ($translabel != $obj->{$InfoFieldList[1]}) {
7781 $labeltoshow = dol_trunc($translabel, 18);
7782 } else {
7783 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]});
7784 }
7785 }
7786 if (empty($labeltoshow)) {
7787 $labeltoshow = '(not defined)';
7788 }
7789 if ($value == $obj->rowid) {
7790 $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7791 }
7792
7793 if (!empty($InfoFieldList[3]) && $parentField) {
7794 $parent = $parentName . ':' . $obj->{$parentField};
7795 $isDependList = 1;
7796 }
7797
7798 $out .= '<option value="' . $obj->rowid . '"';
7799 $out .= ($value == $obj->rowid ? ' selected' : '');
7800 $out .= (!empty($parent) ? ' parent="' . $parent . '"' : '');
7801 $out .= '>' . $labeltoshow . '</option>';
7802 }
7803
7804 $i++;
7805 }
7806 $this->db->free($resql);
7807 } else {
7808 print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
7809 }
7810 } else {
7811 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7812 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
7813 $out .= '<option value="0">&nbsp;</option>';
7814 foreach ($data as $data_key => $data_value) {
7815 $out .= '<option value="' . $data_key . '"';
7816 $out .= ($value == $data_key ? ' selected' : '');
7817 $out .= '>' . $data_value . '</option>';
7818 }
7819 }
7820 }
7821 $out .= '</select>';
7822 } elseif ($type == 'checkbox') {
7823 $value_arr = explode(',', $value);
7824 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ? null : $param['options']), $value_arr, 0, 0, $morecss, 0, '100%');
7825 } elseif ($type == 'radio') {
7826 $out = '';
7827 foreach ($param['options'] as $keyopt => $valopt) {
7828 $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
7829 $out .= ' value="'.$keyopt.'"';
7830 $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
7831 $out .= ($value == $keyopt ? 'checked' : '');
7832 $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$valopt.'</label><br>';
7833 }
7834 } elseif ($type == 'chkbxlst') {
7835 if (is_array($value)) {
7836 $value_arr = $value;
7837 } else {
7838 $value_arr = explode(',', $value);
7839 }
7840
7841 if (is_array($param['options'])) {
7842 $tmpparamoptions = array_keys($param['options']);
7843 $paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
7844
7845 $InfoFieldList = explode(":", $paramoptions[0], 5);
7846 // 0 : tableName
7847 // 1 : label field name
7848 // 2 : key fields name (if different of rowid)
7849 // optional parameters...
7850 // 3 : key field parent (for dependent lists). How this is used ?
7851 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value. Or use USF on a second line separated by "\n".
7852 // 5 : string category type. This replace the filter.
7853 // 6 : ids categories list separated by comma for category root. This replace the filter.
7854 // 7 : sort field
7855
7856 // If there is a filter
7857 if (! empty($InfoFieldList[4])) {
7858 $pos = 0;
7859 $parenthesisopen = 0;
7860 while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
7861 if (substr($InfoFieldList[4], $pos, 1) == '(') {
7862 $parenthesisopen++;
7863 }
7864 if (substr($InfoFieldList[4], $pos, 1) == ')') {
7865 $parenthesisopen--;
7866 }
7867 $pos++;
7868 }
7869 $tmpbefore = substr($InfoFieldList[4], 0, $pos);
7870 $tmpafter = substr($InfoFieldList[4], $pos + 1);
7871 //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
7872 $InfoFieldList[4] = $tmpbefore;
7873 if ($tmpafter !== '') {
7874 $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
7875 }
7876
7877 // Fix better compatibility with some old extrafield syntax filter "(field=123)"
7878 $reg = array();
7879 if (preg_match('/^\‍(?([a-z0-9]+)([=<>]+)(\d+)\‍)?$/i', $InfoFieldList[4], $reg)) {
7880 $InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
7881 }
7882
7883 //var_dump($InfoFieldList);
7884 }
7885
7886 //$Usf = empty($paramoptions[1]) ? '' :$paramoptions[1];
7887
7888 '@phan-var-force array{0:string,1:string,2:string,3:string,3:string,5:string,6:string} $InfoFieldList';
7889
7890 $parentName = '';
7891 $parentField = '';
7892
7893 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7894
7895 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7896 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7897 $keyList .= ', '.$parentField;
7898 }
7899 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7900 if (strpos($InfoFieldList[4], 'extra.') !== false) {
7901 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7902 } else {
7903 $keyList = $InfoFieldList[2].' as rowid';
7904 }
7905 }
7906
7907 $filter_categorie = false;
7908 if (count($InfoFieldList) > 5) {
7909 if ($InfoFieldList[0] == 'categorie') {
7910 $filter_categorie = true;
7911 }
7912 }
7913
7914 // Common filter
7915 if (!$filter_categorie) {
7916 $fields_label = explode('|', $InfoFieldList[1]);
7917 if (is_array($fields_label)) {
7918 $keyList .= ', ';
7919 $keyList .= implode(', ', $fields_label);
7920 }
7921
7922 $sqlwhere = '';
7923 $sql = "SELECT " . $keyList;
7924 $sql .= ' FROM ' . $this->db->prefix() . $InfoFieldList[0];
7925
7926 if (!empty($InfoFieldList[4])) {
7927 // can use SELECT request
7928 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7929 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7930 }
7931
7932 // current object id can be use into filter
7933 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7934 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
7935 } else {
7936 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7937 }
7938
7939 // We have to join on extrafield table
7940 $errstr = '';
7941 if (strpos($InfoFieldList[4], 'extra') !== false) {
7942 $sql .= ' as main, ' . $this->db->sanitize($this->db->prefix() . $InfoFieldList[0]) . '_extrafields as extra';
7943 $sqlwhere .= " WHERE extra.fk_object = main." . $this->db->sanitize($InfoFieldList[2]);
7944 $sqlwhere .= " AND " . $InfoFieldList[4];
7945 } else {
7946 $sqlwhere .= " WHERE " . $InfoFieldList[4];
7947 }
7948 } else {
7949 $sqlwhere .= ' WHERE 1=1';
7950 }
7951
7952 // Add Usf filter on second line
7953 /*
7954 if ($Usf) {
7955 $errorstr = '';
7956 $sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
7957 if (!$errorstr) {
7958 $sqlwhere .= $sqlusf;
7959 } else {
7960 $sqlwhere .= " AND invalid_usf_filter_of_extrafield";
7961 }
7962 }
7963 */
7964
7965 // Some tables may have field, some other not. For the moment we disable it.
7966 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7967 $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7968 }
7969 // $sql.=preg_replace('/^ AND /','',$sqlwhere);
7970 // print $sql;
7971
7972 $sql .= $sqlwhere;
7973
7974 dol_syslog(get_class($this) . '::showInputField type=chkbxlst', LOG_DEBUG);
7975
7976 $resql = $this->db->query($sql);
7977 if ($resql) {
7978 $num = $this->db->num_rows($resql);
7979 $i = 0;
7980
7981 $data = array();
7982
7983 while ($i < $num) {
7984 $labeltoshow = '';
7985 $obj = $this->db->fetch_object($resql);
7986
7987 $notrans = false;
7988 // Several field into label (eq table:code|libelle:rowid)
7989 $fields_label = explode('|', $InfoFieldList[1]);
7990 if (count($fields_label) > 1) {
7991 $notrans = true;
7992 foreach ($fields_label as $field_toshow) {
7993 $labeltoshow .= $obj->$field_toshow . ' ';
7994 }
7995 } else {
7996 $labeltoshow = $obj->{$InfoFieldList[1]};
7997 }
7998 $labeltoshow = dol_trunc($labeltoshow, 45);
7999
8000 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8001 foreach ($fields_label as $field_toshow) {
8002 $translabel = $langs->trans($obj->$field_toshow);
8003 if ($translabel != $obj->$field_toshow) {
8004 $labeltoshow = dol_trunc($translabel, 18) . ' ';
8005 } else {
8006 $labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
8007 }
8008 }
8009
8010 $data[$obj->rowid] = $labeltoshow;
8011 } else {
8012 if (!$notrans) {
8013 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
8014 if ($translabel != $obj->{$InfoFieldList[1]}) {
8015 $labeltoshow = dol_trunc($translabel, 18);
8016 } else {
8017 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
8018 }
8019 }
8020 if (empty($labeltoshow)) {
8021 $labeltoshow = '(not defined)';
8022 }
8023
8024 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8025 $data[$obj->rowid] = $labeltoshow;
8026 }
8027
8028 if (!empty($InfoFieldList[3]) && $parentField) {
8029 $parent = $parentName . ':' . $obj->{$parentField};
8030 $isDependList = 1;
8031 }
8032
8033 $data[$obj->rowid] = $labeltoshow;
8034 }
8035
8036 $i++;
8037 }
8038 $this->db->free($resql);
8039
8040 $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, 0, 0, $morecss, 0, '100%');
8041 } else {
8042 print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
8043 }
8044 } else {
8045 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
8046 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
8047 $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, 0, 0, $morecss, 0, '100%');
8048 }
8049 }
8050 } elseif ($type == 'link') {
8051 // $param_list='ObjectName:classPath[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
8052 // Filter can contains some ':' inside.
8053 $param_list = array_keys($param['options']);
8054 $param_list_array = explode(':', $param_list[0], 4);
8055
8056 $showempty = (($required && $default != '') ? 0 : 1);
8057
8058 if (!preg_match('/search_/', $keyprefix)) {
8059 if (!empty($param_list_array[2])) { // If the entry into $fields is set to add a create button
8060 if (!empty($this->fields[$key]['picto'])) {
8061 $morecss .= ' widthcentpercentminusxx';
8062 } else {
8063 $morecss .= ' widthcentpercentminusx';
8064 }
8065 } else {
8066 if (!empty($this->fields[$key]['picto'])) {
8067 $morecss .= ' widthcentpercentminusx';
8068 }
8069 }
8070 }
8071 $objectfield = $this->element.($this->module ? '@'.$this->module : '').':'.$key.$keysuffix;
8072 $out = $form->selectForForms($param_list_array[0], $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, $moreparam, 0, (empty($val['disabled']) ? 0 : 1), '', $objectfield);
8073
8074 if (!empty($param_list_array[2])) { // If the entry into $fields is set, we must add a create button
8075 if ((!GETPOSTISSET('backtopage') || strpos(GETPOST('backtopage'), $_SERVER['PHP_SELF']) === 0) // // To avoid to open several times the 'Plus' button (we accept only one level)
8076 && empty($val['disabled']) && empty($nonewbutton)) { // and to avoid to show the button if the field is protected by a "disabled".
8077 list($class, $classfile) = explode(':', $param_list[0]);
8078 if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) {
8079 $url_path = dol_buildpath(dirname(dirname($classfile)).'/card.php', 1);
8080 } else {
8081 $url_path = dol_buildpath(dirname(dirname($classfile)).'/'.strtolower($class).'_card.php', 1);
8082 }
8083 $paramforthenewlink = '';
8084 $paramforthenewlink .= (GETPOSTISSET('action') ? '&action='.GETPOST('action', 'aZ09') : '');
8085 $paramforthenewlink .= (GETPOSTISSET('id') ? '&id='.GETPOSTINT('id') : '');
8086 $paramforthenewlink .= (GETPOSTISSET('origin') ? '&origin='.GETPOST('origin', 'aZ09') : '');
8087 $paramforthenewlink .= (GETPOSTISSET('originid') ? '&originid='.GETPOSTINT('originid') : '');
8088 $paramforthenewlink .= '&fk_'.strtolower($class).'=--IDFORBACKTOPAGE--';
8089 // TODO Add JavaScript code to add input fields already filled into $paramforthenewlink so we won't loose them when going back to main page
8090 $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>';
8091 }
8092 }
8093 } elseif ($type == 'password') {
8094 // If prefix is 'search_', field is used as a filter, we use a common text field.
8095 if ($keyprefix.$key.$keysuffix == 'pass_crypted') {
8096 $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="pass" id="pass" value="" '.($moreparam ? $moreparam : '').'>';
8097 $out .= '<input type="hidden" name="pass_crypted" id="pass_crypted" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
8098 } else {
8099 $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
8100 }
8101 } elseif ($type == 'array') {
8102 $newval = $val;
8103 $newval['type'] = 'varchar(256)';
8104
8105 $out = '';
8106 if (!empty($value)) {
8107 foreach ($value as $option) {
8108 $out .= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
8109 $out .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $morecss).'<br></span>';
8110 }
8111 }
8112 $out .= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
8113
8114 $newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
8115 $newInput .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $morecss).'<br></span>';
8116
8117 if (!empty($conf->use_javascript_ajax)) {
8118 $out .= '
8119 <script nonce="'.getNonce().'">
8120 $(document).ready(function() {
8121 $("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
8122 $("'.dol_escape_js($newInput).'").insertBefore(this);
8123 });
8124
8125 $(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
8126 $(this).parent().remove();
8127 });
8128 });
8129 </script>';
8130 }
8131 }
8132 if (!empty($hidden)) {
8133 $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
8134 }
8135
8136 if ($isDependList == 1) {
8137 $out .= $this->getJSListDependancies('_common');
8138 }
8139 /* Add comments
8140 if ($type == 'date') $out.=' (YYYY-MM-DD)';
8141 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
8142 */
8143
8144 // Display error message for field
8145 if (!empty($fieldValidationErrorMsg) && function_exists('getFieldErrorIcon')) {
8146 $out .= ' '.getFieldErrorIcon($fieldValidationErrorMsg);
8147 }
8148
8149 return $out;
8150 }
8151
8165 public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '')
8166 {
8167 global $conf, $langs, $form;
8168
8169 if (!is_object($form)) {
8170 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
8171 $form = new Form($this->db);
8172 }
8173
8174 //$label = empty($val['label']) ? '' : $val['label'];
8175 $type = empty($val['type']) ? '' : $val['type'];
8176 $size = empty($val['css']) ? '' : $val['css'];
8177 $reg = array();
8178
8179 // Convert var to be able to share same code than showOutputField of extrafields
8180 if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
8181 $type = 'varchar'; // convert varchar(xx) int varchar
8182 $size = $reg[1];
8183 } elseif (preg_match('/varchar/', $type)) {
8184 $type = 'varchar'; // convert varchar(xx) int varchar
8185 }
8186 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8187 if (empty($this->fields[$key]['multiinput'])) {
8188 $type = (($this->fields[$key]['type'] == 'checkbox') ? $this->fields[$key]['type'] : 'select');
8189 }
8190 }
8191 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8192 $type = 'link';
8193 }
8194
8195 $default = empty($val['default']) ? '' : $val['default'];
8196 $computed = empty($val['computed']) ? '' : $val['computed'];
8197 $unique = empty($val['unique']) ? '' : $val['unique'];
8198 $required = empty($val['required']) ? '' : $val['required'];
8199 $param = array();
8200 $param['options'] = array();
8201
8202 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8203 $param['options'] = $val['arrayofkeyval'];
8204 }
8205 if (preg_match('/^integer:([^:]*):([^:]*)/i', $val['type'], $reg)) { // ex: integer:User:user/class/user.class.php
8206 $type = 'link';
8207 $stringforoptions = $reg[1].':'.$reg[2];
8208 // Special case: Force addition of getnomurlparam1 to -1 for users
8209 if ($reg[1] == 'User') {
8210 $stringforoptions .= ':#getnomurlparam1=-1';
8211 }
8212 $param['options'] = array($stringforoptions => $stringforoptions);
8213 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
8214 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
8215 $type = 'sellist';
8216 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
8217 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
8218 $type = 'sellist';
8219 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
8220 $param['options'] = array($reg[1].':'.$reg[2] => 'N');
8221 $type = 'sellist';
8222 } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
8223 $param['options'] = array($reg[1] => 'N');
8224 $type = 'chkbxlst';
8225 }
8226
8227 $langfile = empty($val['langfile']) ? '' : $val['langfile'];
8228 $list = (empty($val['list']) ? '' : $val['list']);
8229 $help = (empty($val['help']) ? '' : $val['help']);
8230 $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)
8231
8232 if ($hidden) {
8233 return '';
8234 }
8235
8236 // If field is a computed field, value must become result of compute
8237 if ($computed) {
8238 // Make the eval of compute string
8239 //var_dump($computed);
8240 $value = dol_eval($computed, 1, 0, '2');
8241 }
8242
8243 if (empty($morecss)) {
8244 if ($type == 'date') {
8245 $morecss = 'minwidth100imp';
8246 } elseif ($type == 'datetime' || $type == 'timestamp') {
8247 $morecss = 'minwidth200imp';
8248 } elseif (in_array($type, array('int', 'double', 'price'))) {
8249 $morecss = 'maxwidth75';
8250 } elseif ($type == 'url') {
8251 $morecss = 'minwidth400';
8252 } elseif ($type == 'boolean') {
8253 $morecss = '';
8254 } else {
8255 if (is_numeric($size) && round((float) $size) < 12) {
8256 $morecss = 'minwidth100';
8257 } elseif (is_numeric($size) && round((float) $size) <= 48) {
8258 $morecss = 'minwidth200';
8259 } else {
8260 $morecss = 'minwidth400';
8261 }
8262 }
8263 }
8264
8265 // Format output value differently according to properties of field
8266 if (in_array($key, array('rowid', 'ref')) && method_exists($this, 'getNomUrl')) {
8267 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.
8268 $value = $this->getNomUrl(1, '', 0, '', 1);
8269 }
8270 } elseif ($key == 'status' && method_exists($this, 'getLibStatut')) {
8271 $value = $this->getLibStatut(3);
8272 } elseif ($type == 'date') {
8273 if (!empty($value)) {
8274 $value = dol_print_date($value, 'day'); // We suppose dates without time are always gmt (storage of course + output)
8275 } else {
8276 $value = '';
8277 }
8278 } elseif ($type == 'datetime' || $type == 'timestamp') {
8279 if (!empty($value)) {
8280 $value = dol_print_date($value, 'dayhour', 'tzuserrel');
8281 } else {
8282 $value = '';
8283 }
8284 } elseif ($type == 'duration') {
8285 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
8286 if (!is_null($value) && $value !== '') {
8287 $value = convertSecondToTime($value, 'allhourmin');
8288 }
8289 } elseif ($type == 'double' || $type == 'real') {
8290 if (!is_null($value) && $value !== '') {
8291 $value = price($value);
8292 }
8293 } elseif ($type == 'boolean') {
8294 $checked = '';
8295 if (!empty($value)) {
8296 $checked = ' checked ';
8297 }
8298 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
8299 $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
8300 } else {
8301 $value = yn($value ? 1 : 0);
8302 }
8303 } elseif ($type == 'mail' || $type == 'email') {
8304 $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
8305 } elseif ($type == 'url') {
8306 $value = dol_print_url($value, '_blank', 32, 1);
8307 } elseif ($type == 'phone') {
8308 $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
8309 } elseif ($type == 'ip') {
8310 $value = dol_print_ip($value, 0);
8311 } elseif ($type == 'price') {
8312 if (!is_null($value) && $value !== '') {
8313 $value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
8314 }
8315 } elseif ($type == 'select') {
8316 $value = isset($param['options'][(string) $value]) ? $param['options'][(string) $value] : '';
8317 if (strpos($value, "|") !== false) {
8318 $value = $langs->trans(explode('|', $value)[0]);
8319 } elseif (! is_numeric($value)) {
8320 $value = $langs->trans($value);
8321 }
8322 } elseif ($type == 'sellist') {
8323 $param_list = array_keys($param['options']);
8324 $InfoFieldList = explode(":", $param_list[0]);
8325
8326 $selectkey = "rowid";
8327 $keyList = 'rowid';
8328
8329 if (count($InfoFieldList) > 2 && !empty($InfoFieldList[2])) {
8330 $selectkey = $InfoFieldList[2];
8331 $keyList = $InfoFieldList[2].' as rowid';
8332 }
8333
8334 $fields_label = explode('|', $InfoFieldList[1]);
8335 if (is_array($fields_label)) {
8336 $keyList .= ', ';
8337 $keyList .= implode(', ', $fields_label);
8338 }
8339
8340 $filter_categorie = false;
8341 if (count($InfoFieldList) > 5) {
8342 if ($InfoFieldList[0] == 'categorie') {
8343 $filter_categorie = true;
8344 }
8345 }
8346
8347 $sql = "SELECT ".$keyList;
8348 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
8349 if (strpos($InfoFieldList[4], 'extra') !== false) {
8350 $sql .= ' as main';
8351 }
8352 if ($selectkey == 'rowid' && empty($value)) {
8353 $sql .= " WHERE ".$selectkey." = 0";
8354 } elseif ($selectkey == 'rowid') {
8355 $sql .= " WHERE ".$selectkey." = ".((int) $value);
8356 } else {
8357 $sql .= " WHERE ".$selectkey." = '".$this->db->escape($value)."'";
8358 }
8359
8360 //$sql.= ' AND entity = '.$conf->entity;
8361
8362 dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
8363 $resql = $this->db->query($sql);
8364 if ($resql) {
8365 if (!$filter_categorie) {
8366 $value = ''; // value was used, so now we reste it to use it to build final output
8367 $numrows = $this->db->num_rows($resql);
8368 if ($numrows) {
8369 $obj = $this->db->fetch_object($resql);
8370
8371 // Several field into label (eq table:code|libelle:rowid)
8372 $fields_label = explode('|', $InfoFieldList[1]);
8373
8374 if (is_array($fields_label) && count($fields_label) > 1) {
8375 foreach ($fields_label as $field_toshow) {
8376 $translabel = '';
8377 if (!empty($obj->$field_toshow)) {
8378 $translabel = $langs->trans($obj->$field_toshow);
8379 }
8380 if ($translabel != $field_toshow) {
8381 $value .= dol_trunc($translabel, 18) . ' ';
8382 } else {
8383 $value .= $obj->$field_toshow . ' ';
8384 }
8385 }
8386 } else {
8387 $translabel = '';
8388 if (!empty($obj->{$InfoFieldList[1]})) {
8389 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
8390 }
8391 if ($translabel != $obj->{$InfoFieldList[1]}) {
8392 $value = dol_trunc($translabel, 18);
8393 } else {
8394 $value = $obj->{$InfoFieldList[1]};
8395 }
8396 }
8397 }
8398 } else {
8399 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
8400
8401 $toprint = array();
8402 $obj = $this->db->fetch_object($resql);
8403 $c = new Categorie($this->db);
8404 $c->fetch($obj->rowid);
8405 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
8406 foreach ($ways as $way) {
8407 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
8408 }
8409 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
8410 }
8411 } else {
8412 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
8413 }
8414 } elseif ($type == 'radio') {
8415 $value = $param['options'][(string) $value];
8416 } elseif ($type == 'checkbox') {
8417 $value_arr = explode(',', (string) $value);
8418 $value = '';
8419 if (is_array($value_arr) && count($value_arr) > 0) {
8420 $toprint = array();
8421 foreach ($value_arr as $keyval => $valueval) {
8422 if (!empty($valueval)) {
8423 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $param['options'][$valueval] . '</li>';
8424 }
8425 }
8426 if (!empty($toprint)) {
8427 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
8428 }
8429 }
8430 } elseif ($type == 'chkbxlst') {
8431 $value_arr = (isset($value) ? explode(',', $value) : array());
8432
8433 $param_list = array_keys($param['options']);
8434 $InfoFieldList = explode(":", $param_list[0]);
8435
8436 $selectkey = "rowid";
8437 $keyList = 'rowid';
8438
8439 if (count($InfoFieldList) >= 3) {
8440 $selectkey = $InfoFieldList[2];
8441 $keyList = $InfoFieldList[2].' as rowid';
8442 }
8443
8444 $fields_label = explode('|', $InfoFieldList[1]);
8445 if (is_array($fields_label)) {
8446 $keyList .= ', ';
8447 $keyList .= implode(', ', $fields_label);
8448 }
8449
8450 $filter_categorie = false;
8451 if (count($InfoFieldList) > 5) {
8452 if ($InfoFieldList[0] == 'categorie') {
8453 $filter_categorie = true;
8454 }
8455 }
8456
8457 $sql = "SELECT ".$keyList;
8458 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
8459 if (isset($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra') !== false) {
8460 $sql .= ' as main';
8461 }
8462 // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
8463 // $sql.= ' AND entity = '.$conf->entity;
8464
8465 dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG);
8466 $resql = $this->db->query($sql);
8467 if ($resql) {
8468 if (!$filter_categorie) {
8469 $value = ''; // value was used, so now we reset it to use it to build final output
8470 $toprint = array();
8471 while ($obj = $this->db->fetch_object($resql)) {
8472 // Several field into label (eq table:code|libelle:rowid)
8473 $fields_label = explode('|', $InfoFieldList[1]);
8474 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8475 if (is_array($fields_label) && count($fields_label) > 1) {
8476 foreach ($fields_label as $field_toshow) {
8477 $translabel = '';
8478 if (!empty($obj->$field_toshow)) {
8479 $translabel = $langs->trans($obj->$field_toshow);
8480 }
8481 if ($translabel != $field_toshow) {
8482 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
8483 } else {
8484 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->$field_toshow . '</li>';
8485 }
8486 }
8487 } else {
8488 $translabel = '';
8489 if (!empty($obj->{$InfoFieldList[1]})) {
8490 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
8491 }
8492 if ($translabel != $obj->{$InfoFieldList[1]}) {
8493 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
8494 } else {
8495 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->{$InfoFieldList[1]} . '</li>';
8496 }
8497 }
8498 }
8499 }
8500 } else {
8501 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
8502
8503 $toprint = array();
8504 while ($obj = $this->db->fetch_object($resql)) {
8505 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8506 $c = new Categorie($this->db);
8507 $c->fetch($obj->rowid);
8508 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
8509 foreach ($ways as $way) {
8510 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
8511 }
8512 }
8513 }
8514 }
8515 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
8516 } else {
8517 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
8518 }
8519 } elseif ($type == 'link') {
8520 $out = '';
8521
8522 // only if something to display (perf)
8523 if ($value) {
8524 $param_list = array_keys($param['options']);
8525 // Example: $param_list='ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
8526 // Example: $param_list='ObjectClass:PathToClass:#getnomurlparam1=-1#getnomurlparam2=customer'
8527
8528 $InfoFieldList = explode(":", $param_list[0]);
8529
8530 $classname = $InfoFieldList[0];
8531 $classpath = $InfoFieldList[1];
8532
8533 // Set $getnomurlparam1 et getnomurlparam2
8534 $getnomurlparam = 3;
8535 $getnomurlparam2 = '';
8536 $regtmp = array();
8537 if (preg_match('/#getnomurlparam1=([^#]*)/', $param_list[0], $regtmp)) {
8538 $getnomurlparam = $regtmp[1];
8539 }
8540 if (preg_match('/#getnomurlparam2=([^#]*)/', $param_list[0], $regtmp)) {
8541 $getnomurlparam2 = $regtmp[1];
8542 }
8543
8544 if (!empty($classpath)) {
8545 dol_include_once($InfoFieldList[1]);
8546
8547 if ($classname && !class_exists($classname)) {
8548 // from V19 of Dolibarr, In some cases link use element instead of class, example project_task
8549 // TODO use newObjectByElement() introduce in V20 by PR #30036 for better errors management
8550 $element_prop = getElementProperties($classname);
8551 if ($element_prop) {
8552 $classname = $element_prop['classname'];
8553 }
8554 }
8555
8556
8557 if ($classname && class_exists($classname)) {
8558 $object = new $classname($this->db);
8559 if ($object->element === 'product') { // Special case for product because default valut of fetch are wrong
8560 $result = $object->fetch($value, '', '', '', 0, 1, 1);
8561 } else {
8562 $result = $object->fetch($value);
8563 }
8564 if ($result > 0) {
8565 if ($object->element === 'product') {
8566 $get_name_url_param_arr = array($getnomurlparam, $getnomurlparam2, 0, -1, 0, '', 0);
8567 if (isset($val['get_name_url_params'])) {
8568 $get_name_url_params = explode(':', $val['get_name_url_params']);
8569 if (!empty($get_name_url_params)) {
8570 $param_num_max = count($get_name_url_param_arr) - 1;
8571 foreach ($get_name_url_params as $param_num => $param_value) {
8572 if ($param_num > $param_num_max) {
8573 break;
8574 }
8575 $get_name_url_param_arr[$param_num] = $param_value;
8576 }
8577 }
8578 }
8579
8583 $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]);
8584 } else {
8585 $value = $object->getNomUrl($getnomurlparam, $getnomurlparam2);
8586 }
8587 } else {
8588 $value = '';
8589 }
8590 }
8591 } else {
8592 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
8593 return 'Error bad setup of extrafield';
8594 }
8595 } else {
8596 $value = '';
8597 }
8598 } elseif ($type == 'password') {
8599 $value = '<span class="opacitymedium">'.$langs->trans("Encrypted").'</span>';
8600 //$value = preg_replace('/./i', '*', $value);
8601 } elseif ($type == 'array') {
8602 if (is_array($value)) {
8603 $value = implode('<br>', $value);
8604 } else {
8605 dol_syslog(__METHOD__.' Expected array from dol_eval, but got '.gettype($value), LOG_ERR);
8606 return 'Error unexpected result from code evaluation';
8607 }
8608 } else { // text|html|varchar
8609 $value = dol_htmlentitiesbr($value);
8610 }
8611
8612 //print $type.'-'.$size.'-'.$value;
8613 $out = $value;
8614
8615 return is_null($out) ? '' : $out;
8616 }
8617
8624 public function clearFieldError($fieldKey)
8625 {
8626 $this->error = '';
8627 unset($this->validateFieldsErrors[$fieldKey]);
8628 }
8629
8637 public function setFieldError($fieldKey, $msg = '')
8638 {
8639 global $langs;
8640 if (empty($msg)) {
8641 $msg = $langs->trans("UnknownError");
8642 }
8643
8644 $this->error = $this->validateFieldsErrors[$fieldKey] = $msg;
8645 }
8646
8653 public function getFieldError($fieldKey)
8654 {
8655 if (!empty($this->validateFieldsErrors[$fieldKey])) {
8656 return $this->validateFieldsErrors[$fieldKey];
8657 }
8658 return '';
8659 }
8660
8669 public function validateField($fields, $fieldKey, $fieldValue)
8670 {
8671 global $langs;
8672
8673 if (!class_exists('Validate')) {
8674 require_once DOL_DOCUMENT_ROOT . '/core/class/validate.class.php';
8675 }
8676
8677 $this->clearFieldError($fieldKey);
8678
8679 if (!isset($fields[$fieldKey]) || $fields[$fieldKey] === null) {
8680 $this->setFieldError($fieldKey, $langs->trans('FieldNotFoundInObject'));
8681 return false;
8682 }
8683
8684 $val = $fields[$fieldKey];
8685
8686 $param = array();
8687 $param['options'] = array();
8688 $type = $val['type'];
8689
8690 $required = false;
8691 if (isset($val['notnull']) && $val['notnull'] === 1) {
8692 // 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
8693 $required = true;
8694 }
8695
8696 $maxSize = 0;
8697 $minSize = 0;
8698
8699 //
8700 // PREPARE Elements
8701 //
8702 $reg = array();
8703
8704 // Convert var to be able to share same code than showOutputField of extrafields
8705 if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
8706 $type = 'varchar'; // convert varchar(xx) int varchar
8707 $maxSize = $reg[1];
8708 } elseif (preg_match('/varchar/', $type)) {
8709 $type = 'varchar'; // convert varchar(xx) int varchar
8710 }
8711
8712 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8713 $type = 'select';
8714 }
8715
8716 if (!empty($val['type']) && preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8717 $type = 'link';
8718 }
8719
8720 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8721 $param['options'] = $val['arrayofkeyval'];
8722 }
8723
8724 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8725 $type = 'link';
8726 $param['options'] = array($reg[1].':'.$reg[2] => $reg[1].':'.$reg[2]);
8727 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
8728 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
8729 $type = 'sellist';
8730 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
8731 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
8732 $type = 'sellist';
8733 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
8734 $param['options'] = array($reg[1].':'.$reg[2] => 'N');
8735 $type = 'sellist';
8736 }
8737
8738 //
8739 // TEST Value
8740 //
8741
8742 // Use Validate class to allow external Modules to use data validation part instead of concentrate all test here (factoring) or just for reuse
8743 $validate = new Validate($this->db, $langs);
8744
8745
8746 // little trick : to perform tests with good performances sort tests by quick to low
8747
8748 //
8749 // COMMON TESTS
8750 //
8751
8752 // Required test and empty value
8753 if ($required && !$validate->isNotEmptyString($fieldValue)) {
8754 $this->setFieldError($fieldKey, $validate->error);
8755 return false;
8756 } elseif (!$required && !$validate->isNotEmptyString($fieldValue)) {
8757 // if no value sent and the field is not mandatory, no need to perform tests
8758 return true;
8759 }
8760
8761 // MAX Size test
8762 if (!empty($maxSize) && !$validate->isMaxLength($fieldValue, $maxSize)) {
8763 $this->setFieldError($fieldKey, $validate->error);
8764 return false;
8765 }
8766
8767 // MIN Size test
8768 if (!empty($minSize) && !$validate->isMinLength($fieldValue, $minSize)) {
8769 $this->setFieldError($fieldKey, $validate->error);
8770 return false;
8771 }
8772
8773 //
8774 // TESTS for TYPE
8775 //
8776
8777 if (in_array($type, array('date', 'datetime', 'timestamp'))) {
8778 if (!$validate->isTimestamp($fieldValue)) {
8779 $this->setFieldError($fieldKey, $validate->error);
8780 return false;
8781 } else {
8782 return true;
8783 }
8784 } elseif ($type == 'duration') {
8785 if (!$validate->isDuration($fieldValue)) {
8786 $this->setFieldError($fieldKey, $validate->error);
8787 return false;
8788 } else {
8789 return true;
8790 }
8791 } elseif (in_array($type, array('double', 'real', 'price'))) {
8792 // is numeric
8793 if (!$validate->isNumeric($fieldValue)) {
8794 $this->setFieldError($fieldKey, $validate->error);
8795 return false;
8796 } else {
8797 return true;
8798 }
8799 } elseif ($type == 'boolean') {
8800 if (!$validate->isBool($fieldValue)) {
8801 $this->setFieldError($fieldKey, $validate->error);
8802 return false;
8803 } else {
8804 return true;
8805 }
8806 } elseif ($type == 'mail') {
8807 if (!$validate->isEmail($fieldValue)) {
8808 $this->setFieldError($fieldKey, $validate->error);
8809 return false;
8810 }
8811 } elseif ($type == 'url') {
8812 if (!$validate->isUrl($fieldValue)) {
8813 $this->setFieldError($fieldKey, $validate->error);
8814 return false;
8815 } else {
8816 return true;
8817 }
8818 } elseif ($type == 'phone') {
8819 if (!$validate->isPhone($fieldValue)) {
8820 $this->setFieldError($fieldKey, $validate->error);
8821 return false;
8822 } else {
8823 return true;
8824 }
8825 } elseif ($type == 'select' || $type == 'radio') {
8826 if (!isset($param['options'][$fieldValue])) {
8827 $this->error = $langs->trans('RequireValidValue');
8828 return false;
8829 } else {
8830 return true;
8831 }
8832 } elseif ($type == 'sellist' || $type == 'chkbxlst') {
8833 $param_list = array_keys($param['options']);
8834 $InfoFieldList = explode(":", $param_list[0]);
8835 $value_arr = explode(',', $fieldValue);
8836 $value_arr = array_map(array($this->db, 'escape'), $value_arr);
8837
8838 $selectkey = "rowid";
8839 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
8840 $selectkey = $InfoFieldList[2];
8841 }
8842
8843 if (!$validate->isInDb($value_arr, $InfoFieldList[0], $selectkey)) {
8844 $this->setFieldError($fieldKey, $validate->error);
8845 return false;
8846 } else {
8847 return true;
8848 }
8849 } elseif ($type == 'link') {
8850 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
8851 $InfoFieldList = explode(":", $param_list[0]);
8852 $classname = $InfoFieldList[0];
8853 $classpath = $InfoFieldList[1];
8854 if (!$validate->isFetchable($fieldValue, $classname, $classpath)) {
8855 $lastIsFetchableError = $validate->error;
8856
8857 // from V19 of Dolibarr, In some cases link use element instead of class, example project_task
8858 if ($validate->isFetchableElement($fieldValue, $classname)) {
8859 return true;
8860 }
8861
8862 $this->setFieldError($fieldKey, $lastIsFetchableError);
8863 return false;
8864 } else {
8865 return true;
8866 }
8867 }
8868
8869 // if no test failed all is ok
8870 return true;
8871 }
8872
8886 public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = '', $display_type = 'card')
8887 {
8888 global $db, $conf, $langs, $action, $form, $hookmanager;
8889
8890 if (!is_object($form)) {
8891 $form = new Form($db);
8892 }
8893 if (!is_object($extrafields)) {
8894 dol_syslog('Bad parameter extrafields for showOptionals', LOG_ERR);
8895 return 'Bad parameter extrafields for showOptionals';
8896 }
8897 if (!is_array($extrafields->attributes[$this->table_element])) {
8898 dol_syslog("extrafields->attributes was not loaded with extrafields->fetch_name_optionals_label(table_element);", LOG_WARNING);
8899 }
8900
8901 $out = '';
8902
8903 $parameters = array('mode' => $mode, 'params' => $params, 'keysuffix' => $keysuffix, 'keyprefix' => $keyprefix, 'display_type' => $display_type);
8904 $reshook = $hookmanager->executeHooks('showOptionals', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
8905
8906 if (empty($reshook)) {
8907 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) {
8908 $out .= "\n";
8909 $out .= '<!-- commonobject:showOptionals --> ';
8910 $out .= "\n";
8911
8912 $nbofextrafieldsshown = 0;
8913 $e = 0; // var to manage the modulo (odd/even)
8914
8915 $lastseparatorkeyfound = '';
8916 $extrafields_collapse_num = '';
8917 $extrafields_collapse_num_old = '';
8918 $i = 0;
8919
8920 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $label) {
8921 $i++;
8922
8923 // Show only the key field in params @phan-suppress-next-line PhanTypeArraySuspiciousNullable
8924 if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) {
8925 continue;
8926 }
8927
8928 // Test on 'enabled' ('enabled' is different than 'list' = 'visibility')
8929 $enabled = 1;
8930 if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
8931 $enabled = (int) dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
8932 }
8933 if (empty($enabled)) {
8934 continue;
8935 }
8936
8937 $visibility = 1;
8938 if (isset($extrafields->attributes[$this->table_element]['list'][$key])) {
8939 $visibility = (int) dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
8940 }
8941
8942 $perms = 1;
8943 if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
8944 $perms = (int) dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
8945 }
8946
8947 if (($mode == 'create') && !in_array(abs($visibility), array(1, 3))) {
8948 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list
8949 } elseif (($mode == 'edit') && !in_array(abs($visibility), array(1, 3, 4))) {
8950 // We need to make sure, that the values of hidden extrafields are also part of $_POST. Otherwise, they would be empty after an update of the object. See also getOptionalsFromPost
8951 $ef_name = 'options_' . $key;
8952 $ef_value = $this->array_options[$ef_name];
8953 $out .= '<input type="hidden" name="' . $ef_name . '" id="' . $ef_name . '" value="' . $ef_value . '" />' . "\n";
8954 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list and <> 4 = not visible at the creation
8955 } elseif ($mode == 'view' && empty($visibility)) {
8956 continue;
8957 }
8958 if (empty($perms)) {
8959 continue;
8960 }
8961
8962 // Load language if required
8963 if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
8964 $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
8965 }
8966
8967 $colspan = 0;
8968 $value = null;
8969 if (is_array($params) && count($params) > 0 && $display_type == 'card') {
8970 if (array_key_exists('cols', $params)) {
8971 $colspan = $params['cols'];
8972 } elseif (array_key_exists('colspan', $params)) { // For backward compatibility. Use cols instead now.
8973 $reg = array();
8974 if (preg_match('/colspan="(\d+)"/', $params['colspan'], $reg)) {
8975 $colspan = $reg[1];
8976 } else {
8977 $colspan = $params['colspan'];
8978 }
8979 }
8980 }
8981 $colspan = intval($colspan);
8982
8983 switch ($mode) {
8984 case "view":
8985 $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
8986 break;
8987 case "create":
8988 case "edit":
8989 // We get the value of property found with GETPOST so it takes into account:
8990 // default values overwrite, restore back to list link, ... (but not 'default value in database' of field)
8991 $check = 'alphanohtml';
8992 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text'))) {
8993 $check = 'restricthtml';
8994 }
8995 $getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, $check, 3); // GETPOST can get value from GET, POST or setup of default values overwrite.
8996 // GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
8997 if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)) {
8998 if (is_array($getposttemp)) {
8999 // $getposttemp is an array but following code expects a comma separated string
9000 $value = implode(",", $getposttemp);
9001 } else {
9002 $value = $getposttemp;
9003 }
9004 } elseif (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('int'))) {
9005 $value =( !empty($this->array_options["options_".$key]) || (isset($this->array_options["options_".$key]) && $this->array_options["options_".$key] === '0')) ? $this->array_options["options_".$key] : '';
9006 } else {
9007 $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.
9008 }
9009 //var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
9010 break;
9011 }
9012
9013 $nbofextrafieldsshown++;
9014
9015 // Output value of the current field
9016 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
9017 $extrafields_collapse_num = $key;
9018 /*
9019 $extrafield_param = $extrafields->attributes[$this->table_element]['param'][$key];
9020 if (!empty($extrafield_param) && is_array($extrafield_param)) {
9021 $extrafield_param_list = array_keys($extrafield_param['options']);
9022
9023 if (count($extrafield_param_list) > 0) {
9024 $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
9025
9026 if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
9027 //$extrafields_collapse_num = $extrafields->attributes[$this->table_element]['pos'][$key];
9028 $extrafields_collapse_num = $key;
9029 }
9030 }
9031 }
9032 */
9033
9034 // if colspan=0 or 1, the second column is not extended, so the separator must be on 2 columns
9035 $out .= $extrafields->showSeparator($key, $this, ($colspan ? $colspan + 1 : 2), $display_type, $mode);
9036
9037 $lastseparatorkeyfound = $key;
9038 } else {
9039 $collapse_group = $extrafields_collapse_num.(!empty($this->id) ? '_'.$this->id : '');
9040
9041 $class = (!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
9042 $csstyle = '';
9043 if (is_array($params) && count($params) > 0) {
9044 if (array_key_exists('class', $params)) {
9045 $class .= $params['class'].' ';
9046 }
9047 if (array_key_exists('style', $params)) {
9048 $csstyle = $params['style'];
9049 }
9050 }
9051
9052 // add html5 elements
9053 $domData = ' data-element="extrafield"';
9054 $domData .= ' data-targetelement="'.$this->element.'"';
9055 $domData .= ' data-targetid="'.$this->id.'"';
9056
9057 $html_id = (empty($this->id) ? '' : 'extrarow-'.$this->element.'_'.$key.'_'.$this->id);
9058 if ($display_type == 'card') {
9059 if (getDolGlobalString('MAIN_EXTRAFIELDS_USE_TWO_COLUMS') && ($e % 2) == 0) {
9060 $colspan = 0;
9061 }
9062
9063 if ($action == 'selectlines') {
9064 $colspan++;
9065 }
9066 }
9067
9068 // Convert date into timestamp format (value in memory must be a timestamp)
9069 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date'))) {
9070 $datenotinstring = null;
9071 if (array_key_exists('options_'.$key, $this->array_options)) {
9072 $datenotinstring = $this->array_options['options_'.$key];
9073 if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
9074 $datenotinstring = $this->db->jdate($datenotinstring);
9075 }
9076 }
9077 $datekey = $keyprefix.'options_'.$key.$keysuffix;
9078 $value = (GETPOSTISSET($datekey) && $this->id == GETPOST('elrowid', 'int')) ? dol_mktime(12, 0, 0, GETPOSTINT($datekey.'month', 3), GETPOSTINT($datekey.'day', 3), GETPOSTINT($datekey.'year', 3)) : $datenotinstring;
9079 }
9080 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('datetime'))) {
9081 $datenotinstring = null;
9082 if (array_key_exists('options_'.$key, $this->array_options)) {
9083 $datenotinstring = $this->array_options['options_'.$key];
9084 if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
9085 $datenotinstring = $this->db->jdate($datenotinstring);
9086 }
9087 }
9088 $timekey = $keyprefix.'options_'.$key.$keysuffix;
9089 $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;
9090 }
9091 // Convert float submitted string into real php numeric (value in memory must be a php numeric)
9092 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price', 'double'))) {
9093 if (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) {
9094 $value = price2num($value);
9095 } elseif (isset($this->array_options['options_'.$key])) {
9096 $value = $this->array_options['options_'.$key];
9097 }
9098 }
9099
9100 // HTML, text, select, integer and varchar: take into account default value in database if in create mode
9101 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text', 'varchar', 'select', 'radio', 'int', 'boolean'))) {
9102 if ($action == 'create' || $mode == 'create') {
9103 $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) ? $value : $extrafields->attributes[$this->table_element]['default'][$key];
9104 }
9105 }
9106
9107 $labeltoshow = $langs->trans($label);
9108 $helptoshow = $langs->trans($extrafields->attributes[$this->table_element]['help'][$key]);
9109 if ($display_type == 'card') {
9110 $out .= '<tr '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="field_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
9111 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER') && ($action == 'view' || $action == 'valid' || $action == 'editline' || $action == 'confirm_valid' || $action == 'confirm_cancel')) {
9112 $out .= '<td></td>';
9113 }
9114 $out .= '<td class="'.(empty($params['tdclass']) ? 'titlefieldmax45' : $params['tdclass']).' wordbreak';
9115 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'text') {
9116 $out .= ' tdtop';
9117 }
9118 } elseif ($display_type == 'line') {
9119 $out .= '<div '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="fieldline_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
9120 $out .= '<div style="display: inline-block; padding-right:4px" class="wordbreak';
9121 }
9122 //$out .= "titlefield";
9123 //if (GETPOST('action', 'restricthtml') == 'create') $out.='create';
9124 // BUG #11554 : For public page, use red dot for required fields, instead of bold label
9125 $tpl_context = isset($params["tpl_context"]) ? $params["tpl_context"] : "none";
9126 if ($tpl_context != "public") { // Public page : red dot instead of fieldrequired characters
9127 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
9128 $out .= ' fieldrequired';
9129 }
9130 }
9131 $out .= '">';
9132 if ($tpl_context == "public") { // Public page : red dot instead of fieldrequired characters
9133 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
9134 $out .= $form->textwithpicto($labeltoshow, $helptoshow);
9135 } else {
9136 $out .= $labeltoshow;
9137 }
9138 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
9139 $out .= '&nbsp;<span style="color: red">*</span>';
9140 }
9141 } else {
9142 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
9143 $out .= $form->textwithpicto($labeltoshow, $helptoshow);
9144 } else {
9145 $out .= $labeltoshow;
9146 }
9147 }
9148
9149 $out .= ($display_type == 'card' ? '</td>' : '</div>');
9150
9151 // Second column
9152 $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
9153 if ($display_type == 'card') {
9154 // 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
9155 $out .= '<td '.($html_id ? 'id="'.$html_id.'" ' : '').' class="valuefieldcreate '.$this->element.'_extras_'.$key;
9156 $out .= '" '.($colspan ? ' colspan="'.$colspan.'"' : '');
9157 $out .= '>';
9158 } elseif ($display_type == 'line') {
9159 $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].'">';
9160 }
9161
9162 switch ($mode) {
9163 case "view":
9164 $out .= $extrafields->showOutputField($key, $value, '', $this->table_element);
9165 break;
9166 case "create":
9167 $listoftypestoshowpicto = explode(',', getDolGlobalString('MAIN_TYPES_TO_SHOW_PICTO', 'email,phone,ip,password'));
9168 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], $listoftypestoshowpicto)) {
9169 $out .= getPictoForType($extrafields->attributes[$this->table_element]['type'][$key], ($extrafields->attributes[$this->table_element]['type'][$key] == 'text' ? 'tdtop' : ''));
9170 }
9171 //$out .= '<!-- type = '.$extrafields->attributes[$this->table_element]['type'][$key].' -->';
9172 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
9173 break;
9174 case "edit":
9175 $listoftypestoshowpicto = explode(',', getDolGlobalString('MAIN_TYPES_TO_SHOW_PICTO', 'email,phone,ip,password'));
9176 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], $listoftypestoshowpicto)) {
9177 $out .= getPictoForType($extrafields->attributes[$this->table_element]['type'][$key], ($extrafields->attributes[$this->table_element]['type'][$key] == 'text' ? 'tdtop' : ''));
9178 }
9179 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
9180 break;
9181 }
9182
9183 $out .= ($display_type == 'card' ? '</td>' : '</div>');
9184 $out .= ($display_type == 'card' ? '</tr>'."\n" : '</div>');
9185 $e++;
9186 }
9187 }
9188 $out .= "\n";
9189 // Add code to manage list depending on others
9190 if (!empty($conf->use_javascript_ajax)) {
9191 $out .= $this->getJSListDependancies();
9192 }
9193
9194 $out .= '<!-- commonobject:showOptionals end --> '."\n";
9195
9196 if (empty($nbofextrafieldsshown)) {
9197 $out = '';
9198 }
9199 }
9200 }
9201
9202 $out .= $hookmanager->resPrint;
9203
9204 return $out;
9205 }
9206
9211 public function getJSListDependancies($type = '_extra')
9212 {
9213 $out = '
9214 <script nonce="'.getNonce().'">
9215 jQuery(document).ready(function() {
9216 function showOptions'.$type.'(child_list, parent_list, orig_select)
9217 {
9218 var val = $("select[name=\""+parent_list+"\"]").val();
9219 var parentVal = parent_list + ":" + val;
9220 if(typeof val == "string"){
9221 if(val != "") {
9222 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
9223 $("select[name=\""+child_list+"\"] option[parent]").remove();
9224 $("select[name=\""+child_list+"\"]").append(options);
9225 } else {
9226 var options = orig_select.find("option[parent]").clone();
9227 $("select[name=\""+child_list+"\"] option[parent]").remove();
9228 $("select[name=\""+child_list+"\"]").append(options);
9229 }
9230 } else if(val > 0) {
9231 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
9232 $("select[name=\""+child_list+"\"] option[parent]").remove();
9233 $("select[name=\""+child_list+"\"]").append(options);
9234 } else {
9235 var options = orig_select.find("option[parent]").clone();
9236 $("select[name=\""+child_list+"\"] option[parent]").remove();
9237 $("select[name=\""+child_list+"\"]").append(options);
9238 }
9239 }
9240 function setListDependencies'.$type.'() {
9241 jQuery("select option[parent]").parent().each(function() {
9242 var orig_select = {};
9243 var child_list = $(this).attr("name");
9244 orig_select[child_list] = $(this).clone();
9245 var parent = $(this).find("option[parent]:first").attr("parent");
9246 var infos = parent.split(":");
9247 var parent_list = infos[0];
9248
9249 //Hide daughters lists
9250 if ($("#"+child_list).val() == 0 && $("#"+parent_list).val() == 0){
9251 $("#"+child_list).hide();
9252 //Show mother lists
9253 } else if ($("#"+parent_list).val() != 0){
9254 $("#"+parent_list).show();
9255 }
9256 //Show the child list if the parent list value is selected
9257 $("select[name=\""+parent_list+"\"]").click(function() {
9258 if ($(this).val() != 0){
9259 $("#"+child_list).show()
9260 }
9261 });
9262
9263 //When we change parent list
9264 $("select[name=\""+parent_list+"\"]").change(function() {
9265 showOptions'.$type.'(child_list, parent_list, orig_select[child_list]);
9266 //Select the value 0 on child list after a change on the parent list
9267 $("#"+child_list).val(0).trigger("change");
9268 //Hide child lists if the parent value is set to 0
9269 if ($(this).val() == 0){
9270 $("#"+child_list).hide();
9271 }
9272 });
9273 });
9274 }
9275
9276 setListDependencies'.$type.'();
9277 });
9278 </script>'."\n";
9279 return $out;
9280 }
9281
9287 public function getRights()
9288 {
9289 global $user;
9290
9291 $module = empty($this->module) ? '' : $this->module;
9292 $element = $this->element;
9293
9294 if ($element == 'facturerec') {
9295 $element = 'facture';
9296 } elseif ($element == 'invoice_supplier_rec') {
9297 return !$user->hasRight('fournisseur', 'facture') ? null : $user->hasRight('fournisseur', 'facture');
9298 } elseif ($module && $user->hasRight($module, $element)) {
9299 // for modules built with ModuleBuilder
9300 return $user->hasRight($module, $element);
9301 }
9302
9303 return $user->rights->$element;
9304 }
9305
9318 public static function commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
9319 {
9320 foreach ($tables as $table) {
9321 $sql = 'UPDATE '.$dbs->prefix().$table.' SET fk_soc = '.((int) $dest_id).' WHERE fk_soc = '.((int) $origin_id);
9322
9323 if (!$dbs->query($sql)) {
9324 if ($ignoreerrors) {
9325 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.
9326 }
9327 //$this->errors = $db->lasterror();
9328 return false;
9329 }
9330 }
9331
9332 return true;
9333 }
9334
9347 public static function commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
9348 {
9349 foreach ($tables as $table) {
9350 $sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_product = '.((int) $dest_id).' WHERE fk_product = '.((int) $origin_id);
9351
9352 if (!$dbs->query($sql)) {
9353 if ($ignoreerrors) {
9354 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.
9355 }
9356 //$this->errors = $db->lasterror();
9357 return false;
9358 }
9359 }
9360
9361 return true;
9362 }
9363
9376 public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
9377 {
9378 global $conf;
9379
9380 $buyPrice = 0;
9381
9382 if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && getDolGlobalInt('ForceBuyingPriceIfNull') > 0)) {
9383 // When ForceBuyingPriceIfNull is set
9384 $buyPrice = $unitPrice * (1 - $discountPercent / 100);
9385 } else {
9386 // Get cost price for margin calculation
9387 if (!empty($fk_product) && $fk_product > 0) {
9388 $result = 0;
9389 if (getDolGlobalString('MARGIN_TYPE') == 'costprice') {
9390 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
9391 $product = new Product($this->db);
9392 $result = $product->fetch($fk_product);
9393 if ($result <= 0) {
9394 $this->errors[] = 'ErrorProductIdDoesNotExists';
9395 return -1;
9396 }
9397 if ($product->cost_price > 0) {
9398 $buyPrice = $product->cost_price;
9399 } elseif ($product->pmp > 0) {
9400 $buyPrice = $product->pmp;
9401 }
9402 } elseif (getDolGlobalString('MARGIN_TYPE') == 'pmp') {
9403 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
9404 $product = new Product($this->db);
9405 $result = $product->fetch($fk_product);
9406 if ($result <= 0) {
9407 $this->errors[] = 'ErrorProductIdDoesNotExists';
9408 return -1;
9409 }
9410 if ($product->pmp > 0) {
9411 $buyPrice = $product->pmp;
9412 }
9413 }
9414
9415 if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1', 'pmp', 'costprice'))) {
9416 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
9417 $productFournisseur = new ProductFournisseur($this->db);
9418 if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0) {
9419 $buyPrice = $productFournisseur->fourn_unitprice;
9420 } elseif ($result < 0) {
9421 $this->errors[] = $productFournisseur->error;
9422 return -2;
9423 }
9424 }
9425 }
9426 }
9427 return $buyPrice;
9428 }
9429
9437 public function getDataToShowPhoto($modulepart, $imagesize)
9438 {
9439 // See getDataToShowPhoto() implemented by Product for example.
9440 return array('dir' => '', 'file' => '', 'originalfile' => '', 'altfile' => '', 'email' => '', 'capture' => '');
9441 }
9442
9443
9444 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
9464 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')
9465 {
9466 // phpcs:enable
9467 global $user, $langs;
9468
9469 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
9470 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
9471
9472 $sortfield = 'position_name';
9473 $sortorder = 'asc';
9474
9475 $dir = $sdir.'/';
9476 $pdir = '/';
9477
9478 $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
9479 $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
9480
9481 // For backward compatibility
9482 if ($modulepart == 'product') {
9483 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
9484 $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9485 $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9486 }
9487 }
9488 if ($modulepart == 'category') {
9489 $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9490 $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9491 }
9492
9493 // Defined relative dir to DOL_DATA_ROOT
9494 $relativedir = '';
9495 if ($dir) {
9496 $relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
9497 $relativedir = preg_replace('/^[\\/]/', '', $relativedir);
9498 $relativedir = preg_replace('/[\\/]$/', '', $relativedir);
9499 }
9500
9501 $dirthumb = $dir.'thumbs/';
9502 $pdirthumb = $pdir.'thumbs/';
9503
9504 $return = '<!-- Photo -->'."\n";
9505 $nbphoto = 0;
9506
9507 $filearray = dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ? SORT_DESC : SORT_ASC), 1);
9508
9509 /*if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) // For backward compatibility, we scan also old dirs
9510 {
9511 $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
9512 $filearray=array_merge($filearray, $filearrayold);
9513 }*/
9514
9515 completeFileArrayWithDatabaseInfo($filearray, $relativedir);
9516
9517 if (count($filearray)) {
9518 if ($sortfield && $sortorder) {
9519 $filearray = dol_sort_array($filearray, $sortfield, $sortorder);
9520 }
9521
9522 foreach ($filearray as $key => $val) {
9523 $photo = '';
9524 $file = $val['name'];
9525
9526 //if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
9527 if (image_format_supported($file) >= 0) {
9528 $nbphoto++;
9529 $photo = $file;
9530 $viewfilename = $file;
9531
9532 if ($size == 1 || $size == 'small') { // Format vignette
9533 // Find name of thumb file
9534 $photo_vignette = basename(getImageFileNameForSize($dir.$file, '_small'));
9535 if (!dol_is_file($dirthumb.$photo_vignette)) {
9536 // The thumb does not exists, so we will use the original file
9537 $dirthumb = $dir;
9538 $pdirthumb = $pdir;
9539 $photo_vignette = basename($file);
9540 }
9541
9542 // Get filesize of original file
9543 $imgarray = dol_getImageSize($dir.$photo);
9544
9545 if ($nbbyrow > 0) {
9546 if ($nbphoto == 1) {
9547 $return .= '<table class="valigntop center centpercent" style="border: 0; padding: 2px; border-spacing: 2px; border-collapse: separate;">';
9548 }
9549
9550 if ($nbphoto % $nbbyrow == 1) {
9551 $return .= '<tr class="center valignmiddle" style="border: 1px">';
9552 }
9553 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%" class="photo">'."\n";
9554 } elseif ($nbbyrow < 0) {
9555 $return .= '<div class="inline-block">'."\n";
9556 }
9557
9558 $relativefile = preg_replace('/^\//', '', $pdir.$photo);
9559 if (empty($nolink)) {
9560 $urladvanced = getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
9561 if ($urladvanced) {
9562 $return .= '<a href="'.$urladvanced.'">';
9563 } else {
9564 $return .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank" rel="noopener noreferrer">';
9565 }
9566 }
9567
9568 // Show image (width height=$maxHeight)
9569 // Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
9570 $alt = $langs->transnoentitiesnoconv('File').': '.$relativefile;
9571 $alt .= ' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
9572 if ($overwritetitle) {
9573 if (is_numeric($overwritetitle)) {
9574 $alt = '';
9575 } else {
9576 $alt = $overwritetitle;
9577 }
9578 }
9579 if (empty($cache) && !empty($val['label'])) {
9580 // label is md5 of file
9581 // use it in url to say we want to cache this version of the file
9582 $cache = $val['label'];
9583 }
9584 if ($usesharelink) {
9585 if (array_key_exists('share', $val) && $val['share']) {
9586 if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
9587 $return .= '<!-- Show original file (thumb not yet available with shared links) -->';
9588 $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).'">';
9589 } else {
9590 $return .= '<!-- Show original file -->';
9591 $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).'">';
9592 }
9593 } else {
9594 $return .= '<!-- Show nophoto file (because file is not shared) -->';
9595 $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
9596 }
9597 } else {
9598 if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
9599 $return .= '<!-- Show thumb -->';
9600 $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).'">';
9601 } else {
9602 $return .= '<!-- Show original file -->';
9603 $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).'">';
9604 }
9605 }
9606
9607 if (empty($nolink)) {
9608 $return .= '</a>';
9609 }
9610
9611 if ($showfilename) {
9612 $return .= '<br>'.$viewfilename;
9613 }
9614 if ($showaction) {
9615 $return .= '<br>';
9616 // If $photo_vignette set, we add a link to generate thumbs if file is an image and width or height higher than limits
9617 if ($photo_vignette && (image_format_supported($photo) > 0) && ((isset($imgarray['width']) && $imgarray['width'] > $maxWidth) || (isset($imgarray['width']) && $imgarray['width'] > $maxHeight))) {
9618 $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>';
9619 }
9620 // Special case for product
9621 if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
9622 // Link to resize
9623 $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; ';
9624
9625 // Link to delete
9626 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
9627 $return .= img_delete().'</a>';
9628 }
9629 }
9630 $return .= "\n";
9631
9632 if ($nbbyrow > 0) {
9633 $return .= '</td>';
9634 if (($nbphoto % $nbbyrow) == 0) {
9635 $return .= '</tr>';
9636 }
9637 } elseif ($nbbyrow < 0) {
9638 $return .= '</div>'."\n";
9639 }
9640 }
9641
9642 if (empty($size)) { // Format origine
9643 $return .= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
9644
9645 if ($showfilename) {
9646 $return .= '<br>'.$viewfilename;
9647 }
9648 if ($showaction) {
9649 // Special case for product
9650 if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
9651 // Link to resize
9652 $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; ';
9653
9654 // Link to delete
9655 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
9656 $return .= img_delete().'</a>';
9657 }
9658 }
9659 }
9660
9661 // On continue ou on arrete de boucler ?
9662 if ($nbmax && $nbphoto >= $nbmax) {
9663 break;
9664 }
9665 }
9666 }
9667
9668 if ($size == 1 || $size == 'small') {
9669 if ($nbbyrow > 0) {
9670 // Ferme tableau
9671 while ($nbphoto % $nbbyrow) {
9672 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%">&nbsp;</td>';
9673 $nbphoto++;
9674 }
9675
9676 if ($nbphoto) {
9677 $return .= '</table>';
9678 }
9679 }
9680 }
9681 }
9682
9683 $this->nbphoto = $nbphoto;
9684
9685 return $return;
9686 }
9687
9688
9695 protected function isArray($info)
9696 {
9697 if (is_array($info)) {
9698 if (isset($info['type']) && $info['type'] == 'array') {
9699 return true;
9700 } else {
9701 return false;
9702 }
9703 }
9704 return false;
9705 }
9706
9713 public function isDate($info)
9714 {
9715 if (isset($info['type']) && ($info['type'] == 'date' || $info['type'] == 'datetime' || $info['type'] == 'timestamp')) {
9716 return true;
9717 }
9718 return false;
9719 }
9720
9727 public function isDuration($info)
9728 {
9729 if (is_array($info)) {
9730 if (isset($info['type']) && ($info['type'] == 'duration')) {
9731 return true;
9732 } else {
9733 return false;
9734 }
9735 } else {
9736 return false;
9737 }
9738 }
9739
9746 public function isInt($info)
9747 {
9748 if (is_array($info)) {
9749 if (isset($info['type']) && (preg_match('/(^int|int$)/i', $info['type']))) {
9750 return true;
9751 } else {
9752 return false;
9753 }
9754 } else {
9755 return false;
9756 }
9757 }
9758
9765 public function isFloat($info)
9766 {
9767 if (is_array($info)) {
9768 if (isset($info['type']) && (preg_match('/^(double|real|price)/i', $info['type']))) {
9769 return true;
9770 } else {
9771 return false;
9772 }
9773 }
9774 return false;
9775 }
9776
9783 public function isText($info)
9784 {
9785 if (is_array($info)) {
9786 if (isset($info['type']) && $info['type'] == 'text') {
9787 return true;
9788 } else {
9789 return false;
9790 }
9791 }
9792 return false;
9793 }
9794
9801 protected function canBeNull($info)
9802 {
9803 if (is_array($info)) {
9804 if (array_key_exists('notnull', $info) && $info['notnull'] != '1') {
9805 return true;
9806 } else {
9807 return false;
9808 }
9809 }
9810 return true;
9811 }
9812
9819 protected function isForcedToNullIfZero($info)
9820 {
9821 if (is_array($info)) {
9822 if (array_key_exists('notnull', $info) && $info['notnull'] == '-1') {
9823 return true;
9824 } else {
9825 return false;
9826 }
9827 }
9828 return false;
9829 }
9830
9837 protected function isIndex($info)
9838 {
9839 if (is_array($info)) {
9840 if (array_key_exists('index', $info) && $info['index'] == true) {
9841 return true;
9842 } else {
9843 return false;
9844 }
9845 }
9846 return false;
9847 }
9848
9849
9858 protected function setSaveQuery()
9859 {
9860 global $conf;
9861
9862 $queryarray = array();
9863 foreach ($this->fields as $field => $info) { // Loop on definition of fields
9864 // Depending on field type ('datetime', ...)
9865 if ($this->isDate($info)) {
9866 if (empty($this->{$field})) {
9867 $queryarray[$field] = null;
9868 } else {
9869 $queryarray[$field] = $this->db->idate($this->{$field});
9870 }
9871 } elseif ($this->isDuration($info)) {
9872 // $this->{$field} may be null, '', 0, '0', 123, '123'
9873 if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
9874 if (!isset($this->{$field})) {
9875 if (!empty($info['default'])) {
9876 $queryarray[$field] = $info['default'];
9877 } else {
9878 $queryarray[$field] = 0;
9879 }
9880 } else {
9881 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9882 }
9883 } else {
9884 $queryarray[$field] = null;
9885 }
9886 } elseif ($this->isInt($info) || $this->isFloat($info)) {
9887 if ($field == 'entity' && is_null($this->{$field})) {
9888 $queryarray[$field] = ((int) $conf->entity);
9889 } else {
9890 // $this->{$field} may be null, '', 0, '0', 123, '123'
9891 if ((isset($this->{$field}) && ((string) $this->{$field}) != '') || !empty($info['notnull'])) {
9892 if (!isset($this->{$field})) {
9893 $queryarray[$field] = 0;
9894 } elseif ($this->isInt($info)) {
9895 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9896 } elseif ($this->isFloat($info)) {
9897 $queryarray[$field] = (float) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9898 }
9899 } else {
9900 $queryarray[$field] = null;
9901 }
9902 }
9903 } else {
9904 // Note: If $this->{$field} is not defined, it means there is a bug into definition of ->fields or a missing declaration of property
9905 // We should keep the warning generated by this because it is a bug somewhere else in code, not here.
9906 $queryarray[$field] = $this->{$field};
9907 }
9908
9909 if (array_key_exists('type', $info) && $info['type'] == 'timestamp' && empty($queryarray[$field])) {
9910 unset($queryarray[$field]);
9911 }
9912 if (!empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) {
9913 $queryarray[$field] = null; // May force 0 to null
9914 }
9915 }
9916
9917 return $queryarray;
9918 }
9919
9926 public function setVarsFromFetchObj(&$obj)
9927 {
9928 global $db;
9929
9930 foreach ($this->fields as $field => $info) {
9931 if ($this->isDate($info)) {
9932 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') {
9933 $this->$field = '';
9934 } else {
9935 $this->$field = $db->jdate($obj->$field);
9936 }
9937 } elseif ($this->isInt($info)) {
9938 if ($field == 'rowid') {
9939 $this->id = (int) $obj->$field;
9940 } else {
9941 if ($this->isForcedToNullIfZero($info)) {
9942 if (empty($obj->$field)) {
9943 $this->$field = null;
9944 } else {
9945 $this->$field = (int) $obj->$field;
9946 }
9947 } else {
9948 if (isset($obj->$field) && (!is_null($obj->$field) || (array_key_exists('notnull', $info) && $info['notnull'] == 1))) {
9949 $this->$field = (int) $obj->$field;
9950 } else {
9951 $this->$field = null;
9952 }
9953 }
9954 }
9955 } elseif ($this->isFloat($info)) {
9956 if ($this->isForcedToNullIfZero($info)) {
9957 if (empty($obj->$field)) {
9958 $this->$field = null;
9959 } else {
9960 $this->$field = (float) $obj->$field;
9961 }
9962 } else {
9963 if (isset($obj->$field) && (!is_null($obj->$field) || (array_key_exists('notnull', $info) && $info['notnull'] == 1))) {
9964 $this->$field = (float) $obj->$field;
9965 } else {
9966 $this->$field = null;
9967 }
9968 }
9969 } else {
9970 $this->$field = isset($obj->$field) ? $obj->$field : null;
9971 }
9972 }
9973
9974 // If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
9975 if (!isset($this->fields['ref']) && isset($this->id)) {
9976 $this->ref = (string) $this->id;
9977 }
9978 }
9979
9984 public function emtpyObjectVars()
9985 {
9986 foreach ($this->fields as $field => $arr) {
9987 $this->$field = null;
9988 }
9989 }
9990
9998 public function getFieldList($alias = '', $excludefields = array())
9999 {
10000 $keys = array_keys($this->fields);
10001 if (!empty($alias)) {
10002 $keys_with_alias = array();
10003 foreach ($keys as $fieldname) {
10004 if (!empty($excludefields)) {
10005 if (in_array($fieldname, $excludefields)) { // The field is excluded and must not be in output
10006 continue;
10007 }
10008 }
10009 $keys_with_alias[] = $alias . '.' . $fieldname;
10010 }
10011 return implode(',', $keys_with_alias);
10012 } else {
10013 return implode(',', $keys);
10014 }
10015 }
10016
10024 protected function quote($value, $fieldsentry)
10025 {
10026 if (is_null($value)) {
10027 return 'NULL';
10028 } elseif (preg_match('/^(int|double|real|price)/i', $fieldsentry['type'])) {
10029 return price2num((string) $value);
10030 } elseif (preg_match('/int$/i', $fieldsentry['type'])) {
10031 return (int) $value;
10032 } elseif ($fieldsentry['type'] == 'boolean') {
10033 if ($value) {
10034 return 'true';
10035 } else {
10036 return 'false';
10037 }
10038 } else {
10039 return "'".$this->db->escape($value)."'";
10040 }
10041 }
10042
10043
10051 public function createCommon(User $user, $notrigger = 0)
10052 {
10053 global $langs;
10054
10055 dol_syslog(get_class($this)."::createCommon create", LOG_DEBUG);
10056
10057 $error = 0;
10058
10059 $now = dol_now();
10060
10061 $fieldvalues = $this->setSaveQuery();
10062
10063 // Note: Here, $fieldvalues contains same keys (or less) that are inside ->fields
10064
10065 if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) {
10066 $fieldvalues['date_creation'] = $this->db->idate($now);
10067 }
10068 if (array_key_exists('fk_user_creat', $fieldvalues) && !($fieldvalues['fk_user_creat'] > 0)) {
10069 $fieldvalues['fk_user_creat'] = $user->id;
10070 $this->fk_user_creat = $user->id;
10071 }
10072 if (array_key_exists('user_creation_id', $fieldvalues) && !($fieldvalues['user_creation_id'] > 0)) {
10073 $fieldvalues['user_creation_id'] = $user->id;
10074 $this->user_creation_id = $user->id;
10075 }
10076 if (array_key_exists('pass_crypted', $fieldvalues) && property_exists($this, 'pass')) {
10077 // @phan-suppress-next-line PhanUndeclaredProperty
10078 $fieldvalues['pass_crypted'] = dol_hash($this->pass);
10079 }
10080 if (array_key_exists('ref', $fieldvalues)) {
10081 $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
10082 }
10083
10084 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
10085
10086 $keys = array();
10087 $values = array(); // Array to store string forged for SQL syntax
10088 foreach ($fieldvalues as $k => $v) {
10089 $keys[$k] = $k;
10090 $value = $this->fields[$k];
10091 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
10092 $values[$k] = $this->quote($v, $value); // May return string 'NULL' if $value is null
10093 }
10094
10095 // Clean and check mandatory
10096 foreach ($keys as $key) {
10097 if (!isset($this->fields[$key])) {
10098 continue;
10099 }
10100 $key_fields = $this->fields[$key];
10101
10102 // If field is an implicit foreign key field (so type = 'integer:...')
10103 if (preg_match('/^integer:/i', $key_fields['type']) && $values[$key] == '-1') {
10104 $values[$key] = '';
10105 }
10106 if (!empty($key_fields['foreignkey']) && $values[$key] == '-1') {
10107 $values[$key] = '';
10108 }
10109
10110 if (isset($key_fields['notnull']) && $key_fields['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && (!isset($key_fields['default']) || is_null($key_fields['default']))) {
10111 $error++;
10112 $langs->load("errors");
10113 dol_syslog("Mandatory field '".$key."' is empty and required into ->fields definition of class");
10114 $this->errors[] = $langs->trans("ErrorFieldRequired", isset($key_fields['label']) ? $key_fields['label'] : $key);
10115 }
10116
10117 // If value is null and there is a default value for field @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
10118 if (isset($key_fields['notnull']) && $key_fields['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && !is_null($key_fields['default'])) {
10119 $values[$key] = $this->quote($key_fields['default'], $key_fields);
10120 }
10121
10122 // If field is an implicit foreign key field (so type = 'integer:...')
10123 if (isset($key_fields['type']) && preg_match('/^integer:/i', $key_fields['type']) && empty($values[$key])) {
10124 if (isset($key_fields['default'])) {
10125 $values[$key] = ((int) $key_fields['default']);
10126 } else {
10127 $values[$key] = 'null';
10128 }
10129 }
10130 if (!empty($key_fields['foreignkey']) && empty($values[$key])) {
10131 $values[$key] = 'null';
10132 }
10133 }
10134
10135 if ($error) {
10136 return -1;
10137 }
10138
10139 $this->db->begin();
10140
10141 if (!$error) {
10142 $sql = "INSERT INTO ".$this->db->prefix().$this->table_element;
10143 $sql .= " (".implode(", ", $keys).')';
10144 $sql .= " VALUES (".implode(", ", $values).")"; // $values can contains 'abc' or 123
10145
10146 $res = $this->db->query($sql);
10147 if (!$res) {
10148 $error++;
10149 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
10150 $this->errors[] = "ErrorRefAlreadyExists";
10151 } else {
10152 $this->errors[] = $this->db->lasterror();
10153 }
10154 }
10155 }
10156
10157 if (!$error) {
10158 $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
10159 }
10160
10161 // If we have a field ref with a default value of (PROV)
10162 if (!$error) {
10163 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
10164 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)') {
10165 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ref = '(PROV".((int) $this->id).")' WHERE (ref = '(PROV)' OR ref = '') AND rowid = ".((int) $this->id);
10166 $resqlupdate = $this->db->query($sql);
10167
10168 if ($resqlupdate === false) {
10169 $error++;
10170 $this->errors[] = $this->db->lasterror();
10171 } else {
10172 $this->ref = '(PROV'.$this->id.')';
10173 }
10174 }
10175 }
10176
10177 // Create extrafields
10178 if (!$error) {
10179 $result = $this->insertExtraFields();
10180 if ($result < 0) {
10181 $error++;
10182 }
10183 }
10184
10185 // Create lines
10186 if (!empty($this->table_element_line) && !empty($this->fk_element)) {
10187 foreach ($this->lines as $line) {
10188 $keyforparent = $this->fk_element;
10189 $line->$keyforparent = $this->id;
10190
10191 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
10192 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
10193 if (!is_object($line)) {
10194 $line = (object) $line;
10195 }
10196
10197 $result = 0;
10198 if (method_exists($line, 'insert')) {
10199 $result = $line->insert($user, 1);
10200 } elseif (method_exists($line, 'create')) {
10201 $result = $line->create($user, 1);
10202 }
10203 if ($result < 0) {
10204 $this->error = $line->error;
10205 $this->db->rollback();
10206 return -1;
10207 }
10208 }
10209 }
10210
10211 // Triggers
10212 if (!$error && !$notrigger) {
10213 // Call triggers
10214 $result = $this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user);
10215 if ($result < 0) {
10216 $error++;
10217 }
10218 // End call triggers
10219 }
10220
10221 // Commit or rollback
10222 if ($error) {
10223 $this->db->rollback();
10224 return -1;
10225 } else {
10226 $this->db->commit();
10227 return $this->id;
10228 }
10229 }
10230
10231
10241 public function fetchCommon($id, $ref = null, $morewhere = '', $noextrafields = 0)
10242 {
10243 if (empty($id) && empty($ref) && empty($morewhere)) {
10244 return -1;
10245 }
10246
10247 $fieldlist = $this->getFieldList('t');
10248 if (empty($fieldlist)) {
10249 return 0;
10250 }
10251
10252 $sql = "SELECT ".$fieldlist;
10253 $sql .= " FROM ".$this->db->prefix().$this->table_element.' as t';
10254
10255 if (!empty($id)) {
10256 $sql .= ' WHERE t.rowid = '.((int) $id);
10257 } elseif (!empty($ref)) {
10258 $sql .= " WHERE t.ref = '".$this->db->escape($ref)."'";
10259 } else {
10260 $sql .= ' WHERE 1 = 1'; // usage with empty id and empty ref is very rare
10261 }
10262 if (empty($id) && isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
10263 $sql .= ' AND t.entity IN ('.getEntity($this->element).')';
10264 }
10265 if ($morewhere) {
10266 $sql .= $morewhere;
10267 }
10268 $sql .= ' LIMIT 1'; // This is a fetch, to be certain to get only one record
10269
10270 $res = $this->db->query($sql);
10271 if ($res) {
10272 $obj = $this->db->fetch_object($res);
10273 if ($obj) {
10274 $this->setVarsFromFetchObj($obj);
10275
10276 // Retrieve all extrafield
10277 // fetch optionals attributes and labels
10278 if (empty($noextrafields)) {
10279 $result = $this->fetch_optionals();
10280 if ($result < 0) {
10281 $this->error = $this->db->lasterror();
10282 $this->errors[] = $this->error;
10283 return -4;
10284 }
10285 }
10286
10287 return $this->id;
10288 } else {
10289 return 0;
10290 }
10291 } else {
10292 $this->error = $this->db->lasterror();
10293 $this->errors[] = $this->error;
10294 return -1;
10295 }
10296 }
10297
10305 public function fetchLinesCommon($morewhere = '', $noextrafields = 0)
10306 {
10307 $objectlineclassname = get_class($this).'Line';
10308 if (!class_exists($objectlineclassname)) {
10309 $this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
10310 return -1;
10311 }
10312
10313 $objectline = new $objectlineclassname($this->db);
10314 '@phan-var-force CommonObjectLine $objectline';
10315
10316 $sql = "SELECT ".$objectline->getFieldList('l');
10317 $sql .= " FROM ".$this->db->prefix().$objectline->table_element." as l";
10318 $sql .= " WHERE l.fk_".$this->db->escape($this->element)." = ".((int) $this->id);
10319 if ($morewhere) {
10320 $sql .= $morewhere;
10321 }
10322 if (isset($objectline->fields['position'])) {
10323 $sql .= $this->db->order('position', 'ASC');
10324 }
10325
10326 $resql = $this->db->query($sql);
10327 if ($resql) {
10328 $num_rows = $this->db->num_rows($resql);
10329 $i = 0;
10330 $this->lines = array();
10331 while ($i < $num_rows) {
10332 $obj = $this->db->fetch_object($resql);
10333 if ($obj) {
10334 $newline = new $objectlineclassname($this->db);
10335 '@phan-var-force CommonObjectLine $newline';
10336 $newline->setVarsFromFetchObj($obj);
10337
10338 // Note: extrafields load of line not yet supported
10339 /*
10340 if (empty($noextrafields)) {
10341 // Load extrafields of line
10342 }*/
10343
10344 $this->lines[] = $newline;
10345 }
10346 $i++;
10347 }
10348
10349 return 1;
10350 } else {
10351 $this->error = $this->db->lasterror();
10352 $this->errors[] = $this->error;
10353 return -1;
10354 }
10355 }
10356
10364 public function updateCommon(User $user, $notrigger = 0)
10365 {
10366 dol_syslog(get_class($this)."::updateCommon update", LOG_DEBUG);
10367
10368 $error = 0;
10369
10370 $now = dol_now();
10371
10372 // $this->oldcopy should have been set by the caller of update
10373 //if (empty($this->oldcopy)) {
10374 // dol_syslog("this->oldcopy should have been set by the caller of update (here properties were already modified)", LOG_WARNING);
10375 // $this->oldcopy = dol_clone($this, 2);
10376 //}
10377
10378 $fieldvalues = $this->setSaveQuery();
10379
10380 // Note: Here, $fieldvalues contains same keys (or less) that are inside ->fields
10381
10382 if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) {
10383 $fieldvalues['date_modification'] = $this->db->idate($now);
10384 }
10385 if (array_key_exists('fk_user_modif', $fieldvalues) && !($fieldvalues['fk_user_modif'] > 0)) {
10386 $fieldvalues['fk_user_modif'] = $user->id;
10387 }
10388 if (array_key_exists('user_modification_id', $fieldvalues) && !($fieldvalues['user_modification_id'] > 0)) {
10389 $fieldvalues['user_modification_id'] = $user->id;
10390 }
10391 if (array_key_exists('ref', $fieldvalues)) {
10392 $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
10393 }
10394
10395 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
10396
10397 // Add quotes and escape on fields with type string
10398 $keys = array();
10399 $values = array();
10400 $tmp = array();
10401 foreach ($fieldvalues as $k => $v) {
10402 $keys[$k] = $k;
10403 $value = $this->fields[$k];
10404 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
10405 $values[$k] = $this->quote($v, $value);
10406 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
10407 $tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
10408 }
10409
10410 // Clean and check mandatory fields
10411 foreach ($keys as $key) {
10412 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
10413 $values[$key] = ''; // This is an implicit foreign key field
10414 }
10415 if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
10416 $values[$key] = ''; // This is an explicit foreign key field
10417 }
10418
10419 //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
10420 /*
10421 if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
10422 {
10423 $error++;
10424 $this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
10425 }*/
10426 }
10427
10428 $sql = 'UPDATE '.$this->db->prefix().$this->table_element.' SET '.implode(', ', $tmp).' WHERE rowid='.((int) $this->id);
10429
10430 $this->db->begin();
10431
10432 if (!$error) {
10433 $res = $this->db->query($sql);
10434 if (!$res) {
10435 $error++;
10436 $this->errors[] = $this->db->lasterror();
10437 }
10438 }
10439
10440 // Update extrafield
10441 if (!$error) {
10442 $result = $this->insertExtraFields(); // This delete and reinsert extrafields
10443 if ($result < 0) {
10444 $error++;
10445 }
10446 }
10447
10448 // Triggers
10449 if (!$error && !$notrigger) {
10450 // Call triggers
10451 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
10452 if ($result < 0) {
10453 $error++;
10454 } //Do also here what you must do to rollback action if trigger fail
10455 // End call triggers
10456 }
10457
10458 // Commit or rollback
10459 if ($error) {
10460 $this->db->rollback();
10461 return -1;
10462 } else {
10463 $this->db->commit();
10464 return $this->id;
10465 }
10466 }
10467
10476 public function deleteCommon(User $user, $notrigger = 0, $forcechilddeletion = 0)
10477 {
10478 dol_syslog(get_class($this)."::deleteCommon delete", LOG_DEBUG);
10479
10480 $error = 0;
10481
10482 $this->db->begin();
10483
10484 if ($forcechilddeletion) { // Force also delete of childtables that should lock deletion in standard case when option force is off
10485 foreach ($this->childtables as $table) {
10486 $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
10487 $resql = $this->db->query($sql);
10488 if (!$resql) {
10489 $this->error = $this->db->lasterror();
10490 $this->errors[] = $this->error;
10491 $this->db->rollback();
10492 return -1;
10493 }
10494 }
10495 } elseif (!empty($this->childtables)) { // If object has children linked with a foreign key field, we check all child tables.
10496 $objectisused = $this->isObjectUsed($this->id);
10497 if (!empty($objectisused)) {
10498 dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
10499 $this->error = 'ErrorRecordHasChildren';
10500 $this->errors[] = $this->error;
10501 $this->db->rollback();
10502 return 0;
10503 }
10504 }
10505
10506 // Delete cascade first
10507 if (is_array($this->childtablesoncascade) && !empty($this->childtablesoncascade)) {
10508 foreach ($this->childtablesoncascade as $tabletodelete) {
10509 $deleteFromObject = explode(':', $tabletodelete, 4);
10510 if (count($deleteFromObject) >= 2) {
10511 $className = str_replace('@', '', $deleteFromObject[0]);
10512 $filePath = $deleteFromObject[1];
10513 $columnName = $deleteFromObject[2];
10514 $filter = '';
10515 if (!empty($deleteFromObject[3])) {
10516 $filter = $deleteFromObject[3];
10517 }
10518 if (dol_include_once($filePath)) {
10519 $childObject = new $className($this->db);
10520 if (method_exists($childObject, 'deleteByParentField')) {
10521 '@phan-var-force CommonObject $childObject';
10522 $result = $childObject->deleteByParentField($this->id, $columnName, $filter);
10523 if ($result < 0) {
10524 $error++;
10525 $this->errors[] = $childObject->error;
10526 break;
10527 }
10528 } else {
10529 $error++;
10530 $this->errors[] = "You defined a cascade delete on an object $className/$this->id but there is no method deleteByParentField for it";
10531 break;
10532 }
10533 } else {
10534 $error++;
10535 $this->errors[] = 'Cannot include child class file '.$filePath;
10536 break;
10537 }
10538 } else {
10539 // Delete record in child table
10540 $sql = "DELETE FROM ".$this->db->prefix().$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
10541
10542 $resql = $this->db->query($sql);
10543 if (!$resql) {
10544 $error++;
10545 $this->error = $this->db->lasterror();
10546 $this->errors[] = $this->error;
10547 break;
10548 }
10549 }
10550 }
10551 }
10552
10553 if (!$error) {
10554 if (!$notrigger) {
10555 // Call triggers
10556 $result = $this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
10557 if ($result < 0) {
10558 $error++;
10559 } // Do also here what you must do to rollback action if trigger fail
10560 // End call triggers
10561 }
10562 }
10563
10564 // Delete llx_ecm_files
10565 if (!$error) {
10566 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
10567 if (!$res) {
10568 $error++;
10569 }
10570 }
10571
10572 // Delete linked object
10573 $res = $this->deleteObjectLinked();
10574 if ($res < 0) {
10575 $error++;
10576 }
10577
10578 if (!$error && !empty($this->isextrafieldmanaged)) {
10579 $result = $this->deleteExtraFields();
10580 if ($result < 0) {
10581 $error++;
10582 }
10583 }
10584
10585 if (!$error) {
10586 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.' WHERE rowid='.((int) $this->id);
10587
10588 $resql = $this->db->query($sql);
10589 if (!$resql) {
10590 $error++;
10591 $this->errors[] = $this->db->lasterror();
10592 }
10593 }
10594
10595 // Commit or rollback
10596 if ($error) {
10597 $this->db->rollback();
10598 return -1;
10599 } else {
10600 $this->db->commit();
10601 return 1;
10602 }
10603 }
10604
10616 public function deleteByParentField($parentId = 0, $parentField = '', $filter = '', $filtermode = "AND")
10617 {
10618 global $user;
10619
10620 $error = 0;
10621 $deleted = 0;
10622
10623 if (!empty($parentId) && !empty($parentField)) {
10624 $this->db->begin();
10625
10626 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
10627 $sql .= " WHERE ".$this->db->sanitize($parentField)." = ".(int) $parentId;
10628
10629 // Manage filter
10630 $errormessage = '';
10631 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
10632 if ($errormessage) {
10633 $this->errors[] = $errormessage;
10634 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
10635 return -1;
10636 }
10637
10638 $resql = $this->db->query($sql);
10639 if (!$resql) {
10640 $this->errors[] = $this->db->lasterror();
10641 $error++;
10642 } else {
10643 while ($obj = $this->db->fetch_object($resql)) {
10644 $result = $this->fetch($obj->rowid); // @phpstan-ignore-line
10645 if ($result < 0) {
10646 $error++;
10647 $this->errors[] = $this->error;
10648 } else {
10649 $result = $this->delete($user); // @phpstan-ignore-line
10650 if ($result < 0) {
10651 $error++;
10652 $this->errors[] = $this->error;
10653 } else {
10654 $deleted++;
10655 }
10656 }
10657 }
10658 }
10659
10660 if (empty($error)) {
10661 $this->db->commit();
10662 return $deleted;
10663 } else {
10664 $this->error = implode(', ', $this->errors);
10665 $this->db->rollback();
10666 return $error * -1;
10667 }
10668 }
10669
10670 return $deleted;
10671 }
10672
10681 public function deleteLineCommon(User $user, $idline, $notrigger = 0)
10682 {
10683 $error = 0;
10684
10685 $tmpforobjectclass = get_class($this);
10686 $tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line';
10687
10688 $this->db->begin();
10689
10690 // Call trigger
10691 $result = $this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user);
10692 if ($result < 0) {
10693 $error++;
10694 }
10695 // End call triggers
10696
10697 if (empty($error)) {
10698 $sql = "DELETE FROM ".$this->db->prefix().$this->table_element_line;
10699 $sql .= " WHERE rowid = ".((int) $idline);
10700
10701 $resql = $this->db->query($sql);
10702 if (!$resql) {
10703 $this->error = "Error ".$this->db->lasterror();
10704 $error++;
10705 }
10706 }
10707
10708 if (empty($error)) {
10709 // Remove extrafields
10710 $tmpobjectline = new $tmpforobjectlineclass($this->db);
10711 '@phan-var-force CommonObjectLine $tmpobjectline';
10712 if (!isset($tmpobjectline->isextrafieldmanaged) || !empty($tmpobjectline->isextrafieldmanaged)) {
10713 $tmpobjectline->id = $idline;
10714 $result = $tmpobjectline->deleteExtraFields();
10715 if ($result < 0) {
10716 $error++;
10717 $this->error = "Error ".get_class($this)."::deleteLineCommon deleteExtraFields error -4 ".$tmpobjectline->error;
10718 }
10719 }
10720 }
10721
10722 if (empty($error)) {
10723 $this->db->commit();
10724 return 1;
10725 } else {
10726 dol_syslog(get_class($this)."::deleteLineCommon ERROR:".$this->error, LOG_ERR);
10727 $this->db->rollback();
10728 return -1;
10729 }
10730 }
10731
10732
10742 public function setStatusCommon($user, $status, $notrigger = 0, $triggercode = '')
10743 {
10744 $error = 0;
10745
10746 $this->db->begin();
10747
10748 $statusfield = 'status';
10749 if (in_array($this->element, array('don', 'donation', 'shipping'))) {
10750 $statusfield = 'fk_statut';
10751 }
10752
10753 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
10754 $sql .= " SET ".$statusfield." = ".((int) $status);
10755 $sql .= " WHERE rowid = ".((int) $this->id);
10756
10757 if ($this->db->query($sql)) {
10758 if (!$error) {
10759 $this->oldcopy = clone $this;
10760 }
10761
10762 if (!$error && !$notrigger) {
10763 // Call trigger
10764 $result = $this->call_trigger($triggercode, $user);
10765 if ($result < 0) {
10766 $error++;
10767 }
10768 }
10769
10770 if (!$error) {
10771 $this->status = $status;
10772 if (property_exists($this, 'statut')) { // For backward compatibility
10773 $this->statut = $status;
10774 }
10775 $this->db->commit();
10776 return 1;
10777 } else {
10778 $this->db->rollback();
10779 return -1;
10780 }
10781 } else {
10782 $this->error = $this->db->error();
10783 $this->db->rollback();
10784 return -1;
10785 }
10786 }
10787
10797 public function setSignedStatusCommon($user, $status, $notrigger = 0, $triggercode = '')
10798 {
10799 $error = 0;
10800
10801 $this->db->begin();
10802
10803 $statusfield = 'signed_status';
10804
10805 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
10806 $sql .= " SET ".$statusfield." = ".((int) $status);
10807 $sql .= " WHERE rowid = ".((int) $this->id);
10808
10809 if ($this->db->query($sql)) {
10810 if (!$error) {
10811 $this->oldcopy = clone $this;
10812 }
10813
10814 if (!$error && !$notrigger) {
10815 // Call trigger
10816 $result = $this->call_trigger($triggercode, $user);
10817 if ($result < 0) {
10818 $error++;
10819 }
10820 }
10821
10822 if (!$error) {
10823 $this->status = $status;
10824 $this->db->commit();
10825 return 1;
10826 } else {
10827 $this->db->rollback();
10828 return -1;
10829 }
10830 } else {
10831 $this->error = $this->db->error();
10832 $this->db->rollback();
10833 return -1;
10834 }
10835 }
10836
10837
10844 public function initAsSpecimenCommon()
10845 {
10846 global $user;
10847
10848 $this->id = 0;
10849 $this->specimen = 1;
10850 $fields = array(
10851 'label' => 'This is label',
10852 'ref' => 'ABCD1234',
10853 'description' => 'This is a description',
10854 'qty' => 123.12,
10855 'note_public' => 'Public note',
10856 'note_private' => 'Private note',
10857 'date_creation' => (dol_now() - 3600 * 48),
10858 'date_modification' => (dol_now() - 3600 * 24),
10859 'fk_user_creat' => $user->id,
10860 'fk_user_modif' => $user->id,
10861 'date' => dol_now(),
10862 );
10863 foreach ($fields as $key => $value) {
10864 if (array_key_exists($key, $this->fields)) {
10865 $this->{$key} = $value; // @phpstan-ignore-line
10866 }
10867 }
10868
10869 // Force values to default values when known
10870 if (property_exists($this, 'fields')) {
10871 foreach ($this->fields as $key => $value) {
10872 // If fields are already set, do nothing
10873 if (array_key_exists($key, $fields)) {
10874 continue;
10875 }
10876
10877 if (!empty($value['default'])) {
10878 $this->$key = $value['default'];
10879 }
10880 }
10881 }
10882
10883 return 1;
10884 }
10885
10886
10887 /* Part for comments */
10888
10894 public function fetchComments()
10895 {
10896 require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
10897
10898 $comment = new Comment($this->db);
10899 $result = $comment->fetchAllFor($this->element, $this->id);
10900 if ($result < 0) {
10901 $this->errors = array_merge($this->errors, $comment->errors);
10902 return -1;
10903 } else {
10904 $this->comments = $comment->comments;
10905 }
10906 return count($this->comments);
10907 }
10908
10914 public function getNbComments()
10915 {
10916 return count($this->comments);
10917 }
10918
10925 public function trimParameters($parameters)
10926 {
10927 if (!is_array($parameters)) {
10928 return;
10929 }
10930 foreach ($parameters as $parameter) {
10931 if (isset($this->$parameter)) {
10932 $this->$parameter = trim($this->$parameter);
10933 }
10934 }
10935 }
10936
10937 /* Part for categories/tags */
10938
10949 public function getCategoriesCommon($type_categ)
10950 {
10951 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10952
10953 // Get current categories
10954 $c = new Categorie($this->db);
10955 $existing = $c->containing($this->id, $type_categ, 'id');
10956
10957 return $existing;
10958 }
10959
10972 public function setCategoriesCommon($categories, $type_categ = '', $remove_existing = true)
10973 {
10974 // Handle single category
10975 if (!is_array($categories)) {
10976 $categories = array($categories);
10977 }
10978
10979 dol_syslog(get_class($this)."::setCategoriesCommon Object Id:".$this->id.' type_categ:'.$type_categ.' nb tag add:'.count($categories), LOG_DEBUG);
10980
10981 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10982
10983 if (empty($type_categ)) {
10984 dol_syslog(__METHOD__.': Type '.$type_categ.'is an unknown category type. Done nothing.', LOG_ERR);
10985 return -1;
10986 }
10987
10988 // Get current categories
10989 $c = new Categorie($this->db);
10990 $existing = $c->containing($this->id, $type_categ, 'id');
10991 if ($remove_existing) {
10992 // Diff
10993 if (is_array($existing)) {
10994 $to_del = array_diff($existing, $categories);
10995 $to_add = array_diff($categories, $existing);
10996 } else {
10997 $to_del = array(); // Nothing to delete
10998 $to_add = $categories;
10999 }
11000 } else {
11001 $to_del = array(); // Nothing to delete
11002 $to_add = array_diff($categories, $existing);
11003 }
11004
11005 $error = 0;
11006 $ok = 0;
11007
11008 // Process
11009 foreach ($to_del as $del) {
11010 if ($c->fetch($del) > 0) {
11011 $result = $c->del_type($this, $type_categ);
11012 if ($result < 0) {
11013 $error++;
11014 $this->error = $c->error;
11015 $this->errors = $c->errors;
11016 break;
11017 } else {
11018 $ok += $result;
11019 }
11020 }
11021 }
11022 foreach ($to_add as $add) {
11023 if ($c->fetch($add) > 0) {
11024 $result = $c->add_type($this, $type_categ);
11025 if ($result < 0) {
11026 $error++;
11027 $this->error = $c->error;
11028 $this->errors = $c->errors;
11029 break;
11030 } else {
11031 $ok += $result;
11032 }
11033 }
11034 }
11035
11036 return $error ? (-1 * $error) : $ok;
11037 }
11038
11047 public function cloneCategories($fromId, $toId, $type = '')
11048 {
11049 $this->db->begin();
11050
11051 if (empty($type)) {
11052 $type = $this->table_element;
11053 }
11054
11055 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
11056 $categorystatic = new Categorie($this->db);
11057
11058 $sql = "INSERT INTO ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type])." (fk_categorie, fk_product)";
11059 $sql .= " SELECT fk_categorie, $toId FROM ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type]);
11060 $sql .= " WHERE fk_product = ".((int) $fromId);
11061
11062 if (!$this->db->query($sql)) {
11063 $this->error = $this->db->lasterror();
11064 $this->db->rollback();
11065 return -1;
11066 }
11067
11068 $this->db->commit();
11069 return 1;
11070 }
11071
11078 public function deleteEcmFiles($mode = 0)
11079 {
11080 global $conf;
11081
11082 $this->db->begin();
11083
11084 // Delete in database with mode 0
11085 if ($mode == 0) {
11086 switch ($this->element) {
11087 case 'propal':
11088 $element = 'propale';
11089 break;
11090 case 'product':
11091 $element = 'produit';
11092 break;
11093 case 'order_supplier':
11094 $element = 'fournisseur/commande';
11095 break;
11096 case 'invoice_supplier':
11097 // Special cases that need to use get_exdir to get real dir of object
11098 // In future, all object should use this to define path of documents.
11099 $element = 'fournisseur/facture/'.get_exdir($this->id, 2, 0, 1, $this, 'invoice_supplier');
11100 break;
11101 case 'shipping':
11102 $element = 'expedition/sending';
11103 break;
11104 case 'task':
11105 case 'project_task':
11106 require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
11107
11108 $project_result = $this->fetch_projet();
11109 if ($project_result >= 0) {
11110 $element = 'projet/'.dol_sanitizeFileName($this->project->ref).'/';
11111 }
11112 // no break
11113 default:
11114 $element = $this->element;
11115 }
11116 '@phan-var-force string $element';
11117
11118 // Delete ecm_files_extrafields with mode 0 (using name)
11119 $sql = "DELETE FROM ".$this->db->prefix()."ecm_files_extrafields WHERE fk_object IN (";
11120 $sql .= " SELECT rowid FROM ".$this->db->prefix()."ecm_files WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
11121 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
11122 $sql .= ")";
11123
11124 if (!$this->db->query($sql)) {
11125 $this->error = $this->db->lasterror();
11126 $this->db->rollback();
11127 return false;
11128 }
11129
11130 // Delete ecm_files with mode 0 (using name)
11131 $sql = "DELETE FROM ".$this->db->prefix()."ecm_files";
11132 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
11133 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
11134
11135 if (!$this->db->query($sql)) {
11136 $this->error = $this->db->lasterror();
11137 $this->db->rollback();
11138 return false;
11139 }
11140 }
11141
11142 // Delete in database with mode 1
11143 if ($mode == 1) {
11144 $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files_extrafields";
11145 $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).")";
11146 $resql = $this->db->query($sql);
11147 if (!$resql) {
11148 $this->error = $this->db->lasterror();
11149 $this->db->rollback();
11150 return false;
11151 }
11152
11153 $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files";
11154 $sql .= " WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? "" : "@".$this->module))."' AND src_object_id = ".((int) $this->id);
11155 $resql = $this->db->query($sql);
11156 if (!$resql) {
11157 $this->error = $this->db->lasterror();
11158 $this->db->rollback();
11159 return false;
11160 }
11161 }
11162
11163 $this->db->commit();
11164 return true;
11165 }
11166}
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:637
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:242
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.
newToken()
Return the value of token currently saved into session with name 'newtoken'.
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:141
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:137
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:2010
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:2010