dolibarr 20.0.5
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
4851 // Check if module is enabled (to avoid error if tables of module not created)
4852 if (isset($element['enabled']) && !empty($element['enabled'])) {
4853 $enabled = (int) dol_eval($element['enabled'], 1);
4854 if (empty($enabled)) {
4855 continue;
4856 }
4857 }
4858
4859 // Check if element can be deleted
4860 $sql = "SELECT COUNT(*) as nb";
4861 $sql .= " FROM ".$this->db->prefix().$table." as c";
4862 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4863 $sql .= ", ".$this->db->prefix().$element['parent']." as p";
4864 }
4865 if (!empty($element['fk_element'])) {
4866 $sql .= " WHERE c.".$element['fk_element']." = ".((int) $id);
4867 } else {
4868 $sql .= " WHERE c.".$this->fk_element." = ".((int) $id);
4869 }
4870 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4871 $sql .= " AND c.".$element['parentkey']." = p.rowid";
4872 }
4873 if (!empty($element['parent']) && !empty($element['parenttypefield']) && !empty($element['parenttypevalue'])) {
4874 $sql .= " AND c.".$element['parenttypefield']." = '".$this->db->escape($element['parenttypevalue'])."'";
4875 }
4876 if (!empty($entity)) {
4877 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4878 $sql .= " AND p.entity = ".((int) $entity);
4879 } else {
4880 $sql .= " AND c.entity = ".((int) $entity);
4881 }
4882 }
4883
4884 $resql = $this->db->query($sql);
4885 if ($resql) {
4886 $obj = $this->db->fetch_object($resql);
4887 if ($obj->nb > 0) {
4888 $langs->load("errors");
4889 //print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
4890 $haschild += $obj->nb;
4891 if (is_numeric($element)) { // very old usage array('table1', 'table2', ...)
4892 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $table);
4893 } elseif (is_string($element)) { // old usage array('table1' => 'TranslateKey1', 'table2' => 'TranslateKey2', ...)
4894 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element));
4895 } else { // new usage: $element['name']=Translation key
4896 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element['name']));
4897 }
4898 break; // We found at least one, we stop here
4899 }
4900 } else {
4901 $this->errors[] = $this->db->lasterror();
4902 return -1;
4903 }
4904 }
4905 if ($haschild > 0) {
4906 $this->errors[] = "ErrorRecordHasChildren";
4907 return $haschild;
4908 } else {
4909 return 0;
4910 }
4911 }
4912
4919 public function hasProductsOrServices($predefined = -1)
4920 {
4921 $nb = 0;
4922
4923 foreach ($this->lines as $key => $val) {
4924 $qualified = 0;
4925 if ($predefined == -1) {
4926 $qualified = 1;
4927 }
4928 if ($predefined == 1 && $val->fk_product > 0) {
4929 $qualified = 1;
4930 }
4931 if ($predefined == 0 && $val->fk_product <= 0) {
4932 $qualified = 1;
4933 }
4934 if ($predefined == 2 && $val->fk_product > 0 && $val->product_type == 0) {
4935 $qualified = 1;
4936 }
4937 if ($predefined == 3 && $val->fk_product > 0 && $val->product_type == 1) {
4938 $qualified = 1;
4939 }
4940 if ($qualified) {
4941 $nb++;
4942 }
4943 }
4944 dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
4945 return $nb;
4946 }
4947
4953 public function getTotalDiscount()
4954 {
4955 if (!empty($this->table_element_line)) {
4956 $total_discount = 0.00;
4957
4958 $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
4959 $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
4960 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
4961
4962 dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
4963 $resql = $this->db->query($sql);
4964 if ($resql) {
4965 $num = $this->db->num_rows($resql);
4966 $i = 0;
4967 while ($i < $num) {
4968 $obj = $this->db->fetch_object($resql);
4969
4970 $pu_ht = $obj->pu_ht;
4971 $qty = $obj->qty;
4972 $total_ht = $obj->total_ht;
4973
4974 $total_discount_line = (float) price2num(($pu_ht * $qty) - $total_ht, 'MT');
4975 $total_discount += $total_discount_line;
4976
4977 $i++;
4978 }
4979 }
4980
4981 //print $total_discount; exit;
4982 return (float) price2num($total_discount);
4983 }
4984
4985 return null;
4986 }
4987
4988
4995 public function getTotalWeightVolume()
4996 {
4997 $totalWeight = 0;
4998 $totalVolume = 0;
4999 // defined for shipment only
5000 $totalOrdered = 0;
5001 // defined for shipment only
5002 $totalToShip = 0;
5003
5004 if (empty($this->lines)) {
5005 return array('weight' => $totalWeight, 'volume' => $totalVolume, 'ordered' => $totalOrdered, 'toship' => $totalToShip);
5006 }
5007
5008 foreach ($this->lines as $line) {
5009 if (isset($line->qty_asked)) {
5010 $totalOrdered += $line->qty_asked; // defined for shipment only
5011 }
5012 if (isset($line->qty_shipped)) {
5013 $totalToShip += $line->qty_shipped; // defined for shipment only
5014 } elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty)) {
5015 if (empty($totalToShip)) {
5016 $totalToShip = 0;
5017 }
5018 $totalToShip += $line->qty; // defined for reception only
5019 }
5020
5021 // Define qty, weight, volume, weight_units, volume_units
5022 if ($this->element == 'shipping') {
5023 // for shipments
5024 $qty = $line->qty_shipped ? $line->qty_shipped : 0;
5025 } else {
5026 $qty = $line->qty ? $line->qty : 0;
5027 }
5028
5029 $weight = !empty($line->weight) ? $line->weight : 0;
5030 ($weight == 0 && !empty($line->product->weight)) ? $weight = $line->product->weight : 0;
5031 $volume = !empty($line->volume) ? $line->volume : 0;
5032 ($volume == 0 && !empty($line->product->volume)) ? $volume = $line->product->volume : 0;
5033
5034 $weight_units = !empty($line->weight_units) ? $line->weight_units : 0;
5035 ($weight_units == 0 && !empty($line->product->weight_units)) ? $weight_units = $line->product->weight_units : 0;
5036 $volume_units = !empty($line->volume_units) ? $line->volume_units : 0;
5037 ($volume_units == 0 && !empty($line->product->volume_units)) ? $volume_units = $line->product->volume_units : 0;
5038
5039 $weightUnit = 0;
5040 $volumeUnit = 0;
5041 if (!empty($weight_units)) {
5042 $weightUnit = $weight_units;
5043 }
5044 if (!empty($volume_units)) {
5045 $volumeUnit = $volume_units;
5046 }
5047
5048 if (empty($totalWeight)) {
5049 $totalWeight = 0; // Avoid warning because $totalWeight is ''
5050 }
5051 if (empty($totalVolume)) {
5052 $totalVolume = 0; // Avoid warning because $totalVolume is ''
5053 }
5054
5055 //var_dump($line->volume_units);
5056 if ($weight_units < 50) { // < 50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
5057 $trueWeightUnit = pow(10, $weightUnit);
5058 $totalWeight += $weight * $qty * $trueWeightUnit;
5059 } else {
5060 if ($weight_units == 99) {
5061 // conversion 1 Pound = 0.45359237 KG
5062 $trueWeightUnit = 0.45359237;
5063 $totalWeight += $weight * $qty * $trueWeightUnit;
5064 } elseif ($weight_units == 98) {
5065 // conversion 1 Ounce = 0.0283495 KG
5066 $trueWeightUnit = 0.0283495;
5067 $totalWeight += $weight * $qty * $trueWeightUnit;
5068 } else {
5069 $totalWeight += $weight * $qty; // This may be wrong if we mix different units
5070 }
5071 }
5072 if ($volume_units < 50) { // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
5073 //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
5074 $trueVolumeUnit = pow(10, $volumeUnit);
5075 //print $line->volume;
5076 $totalVolume += $volume * $qty * $trueVolumeUnit;
5077 } else {
5078 $totalVolume += $volume * $qty; // This may be wrong if we mix different units
5079 }
5080 }
5081
5082 return array('weight' => $totalWeight, 'volume' => $totalVolume, 'ordered' => $totalOrdered, 'toship' => $totalToShip);
5083 }
5084
5085
5091 public function setExtraParameters()
5092 {
5093 $this->db->begin();
5094
5095 $extraparams = (!empty($this->extraparams) ? json_encode($this->extraparams) : null);
5096
5097 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
5098 $sql .= " SET extraparams = ".(!empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
5099 $sql .= " WHERE rowid = ".((int) $this->id);
5100
5101 dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
5102 $resql = $this->db->query($sql);
5103 if (!$resql) {
5104 $this->error = $this->db->lasterror();
5105 $this->db->rollback();
5106 return -1;
5107 } else {
5108 $this->db->commit();
5109 return 1;
5110 }
5111 }
5112
5113
5114 // --------------------
5115 // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
5116 // --------------------
5117
5118 /* This is to show add lines */
5119
5129 public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl')
5130 {
5131 global $conf, $user, $langs, $object, $hookmanager, $extrafields, $form;
5132
5133 // Line extrafield
5134 if (!is_object($extrafields)) {
5135 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5136 $extrafields = new ExtraFields($this->db);
5137 }
5138 $extrafields->fetch_name_optionals_label($this->table_element_line);
5139
5140 // Output template part (modules that overwrite templates must declare this into descriptor)
5141 // Use global variables + $dateSelector + $seller and $buyer
5142 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'.
5143 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5144 foreach ($dirtpls as $module => $reldir) {
5145 if (!empty($module)) {
5146 $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
5147 } else {
5148 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php';
5149 }
5150
5151 if (empty($conf->file->strict_mode)) {
5152 $res = @include $tpl;
5153 } else {
5154 $res = include $tpl; // for debug
5155 }
5156 if ($res) {
5157 break;
5158 }
5159 }
5160 }
5161
5162
5163
5164 /* This is to show array of line of details */
5165
5166
5181 public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl')
5182 {
5183 global $conf, $hookmanager, $langs, $user, $form, $extrafields, $object;
5184 // TODO We should not use global var for this
5185 global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
5186
5187 // Define $usemargins (used by objectline_xxx.tpl.php files)
5188 $usemargins = 0;
5189 if (isModEnabled('margin') && !empty($this->element) && in_array($this->element, array('facture', 'facturerec', 'propal', 'commande'))) {
5190 $usemargins = 1;
5191 }
5192
5193 $num = count($this->lines);
5194
5195 // Line extrafield
5196 if (!is_object($extrafields)) {
5197 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5198 $extrafields = new ExtraFields($this->db);
5199 }
5200 $extrafields->fetch_name_optionals_label($this->table_element_line);
5201
5202 $parameters = array('num' => $num, 'dateSelector' => $dateSelector, 'seller' => $seller, 'buyer' => $buyer, 'selected' => $selected, 'table_element_line' => $this->table_element_line);
5203 $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5204 if (empty($reshook)) {
5205 // Output template part (modules that overwrite templates must declare this into descriptor)
5206 // Use global variables + $dateSelector + $seller and $buyer
5207 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook.
5208 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5209 foreach ($dirtpls as $module => $reldir) {
5210 $res = 0;
5211 if (!empty($module)) {
5212 $tpl = dol_buildpath($reldir.'/objectline_title.tpl.php');
5213 } else {
5214 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php';
5215 }
5216 if (file_exists($tpl)) {
5217 if (empty($conf->file->strict_mode)) {
5218 $res = @include $tpl;
5219 } else {
5220 $res = include $tpl; // for debug
5221 }
5222 }
5223 if ($res) {
5224 break;
5225 }
5226 }
5227 }
5228
5229 $i = 0;
5230
5231 print "<!-- begin printObjectLines() --><tbody>\n";
5232 foreach ($this->lines as $line) {
5233 //Line extrafield
5234 $line->fetch_optionals();
5235
5236 //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line)))
5237 if (is_object($hookmanager)) { // Old code is commented on preceding line.
5238 if (empty($line->fk_parent_line)) {
5239 $parameters = array('line' => $line, 'num' => $num, 'i' => $i, 'dateSelector' => $dateSelector, 'seller' => $seller, 'buyer' => $buyer, 'selected' => $selected, 'table_element_line' => $line->table_element, 'defaulttpldir' => $defaulttpldir);
5240 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5241 } else {
5242 $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);
5243 $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5244 }
5245 }
5246 if (empty($reshook)) {
5247 $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafields, $defaulttpldir);
5248 }
5249
5250 $i++;
5251 }
5252 print "</tbody><!-- end printObjectLines() -->\n";
5253 }
5254
5272 public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/core/tpl')
5273 {
5274 global $conf, $langs, $user, $object, $hookmanager;
5275 global $form;
5276 global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
5277
5278 $object_rights = $this->getRights();
5279
5280 // var used into tpl
5281 $text = '';
5282 $description = '';
5283
5284 // Line in view mode
5285 if ($action != 'editline' || $selected != $line->id) {
5286 // Product
5287 if (!empty($line->fk_product) && $line->fk_product > 0) {
5288 $product_static = new Product($this->db);
5289 $product_static->fetch($line->fk_product);
5290
5291 $product_static->ref = $line->ref; //can change ref in hook
5292 $product_static->label = !empty($line->label) ? $line->label : ""; //can change label in hook
5293
5294 $text = $product_static->getNomUrl(1);
5295
5296 // Define output language and label
5297 if (getDolGlobalInt('MAIN_MULTILANGS')) {
5298 if (property_exists($this, 'socid') && !is_object($this->thirdparty)) {
5299 dol_print_error(null, 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
5300 return;
5301 }
5302
5303 $prod = new Product($this->db);
5304 $prod->fetch($line->fk_product);
5305
5306 $outputlangs = $langs;
5307 $newlang = '';
5308 if (empty($newlang) && GETPOST('lang_id', 'aZ09')) {
5309 $newlang = GETPOST('lang_id', 'aZ09');
5310 }
5311 if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE') && empty($newlang) && is_object($this->thirdparty)) {
5312 $newlang = $this->thirdparty->default_lang; // To use language of customer
5313 }
5314 if (!empty($newlang)) {
5315 $outputlangs = new Translate("", $conf);
5316 $outputlangs->setDefaultLang($newlang);
5317 }
5318
5319 $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
5320 } else {
5321 $label = $line->product_label;
5322 }
5323
5324 $text .= ' - '.(!empty($line->label) ? $line->label : $label);
5325 $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.
5326 }
5327
5328 $line->pu_ttc = price2num((!empty($line->subprice) ? $line->subprice : 0) * (1 + ((!empty($line->tva_tx) ? $line->tva_tx : 0) / 100)), 'MU');
5329
5330 // Output template part (modules that overwrite templates must declare this into descriptor)
5331 // Use global variables + $dateSelector + $seller and $buyer
5332 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5333 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5334 foreach ($dirtpls as $module => $reldir) {
5335 $res = 0;
5336 if (!empty($module)) {
5337 $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
5338 } else {
5339 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php';
5340 }
5341 //var_dump($tpl);
5342 if (file_exists($tpl)) {
5343 if (empty($conf->file->strict_mode)) {
5344 $res = @include $tpl;
5345 } else {
5346 $res = include $tpl; // for debug
5347 }
5348 }
5349 if ($res) {
5350 break;
5351 }
5352 }
5353 }
5354
5355 // Line in update mode
5356 if ($this->status == 0 && $action == 'editline' && $selected == $line->id) {
5357 $label = (!empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
5358
5359 $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU');
5360
5361 // Output template part (modules that overwrite templates must declare this into descriptor)
5362 // Use global variables + $dateSelector + $seller and $buyer
5363 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5364 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5365 foreach ($dirtpls as $module => $reldir) {
5366 if (!empty($module)) {
5367 $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
5368 } else {
5369 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php';
5370 }
5371
5372 if (empty($conf->file->strict_mode)) {
5373 $res = @include $tpl;
5374 } else {
5375 $res = include $tpl; // for debug
5376 }
5377 if ($res) {
5378 break;
5379 }
5380 }
5381 }
5382 }
5383
5384
5385 /* This is to show array of line of details of source object */
5386
5387
5398 public function printOriginLinesList($restrictlist = '', $selectedLines = array())
5399 {
5400 global $langs, $hookmanager, $form, $action;
5401
5402 print '<tr class="liste_titre">';
5403 print '<td class="linecolref">'.$langs->trans('Ref').'</td>';
5404 print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
5405 print '<td class="linecolvat right">'.$langs->trans('VATRate').'</td>';
5406 print '<td class="linecoluht right">'.$langs->trans('PriceUHT').'</td>';
5407 if (isModEnabled("multicurrency")) {
5408 print '<td class="linecoluht_currency right">'.$langs->trans('PriceUHTCurrency').'</td>';
5409 }
5410 print '<td class="linecolqty right">'.$langs->trans('Qty').'</td>';
5411 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5412 print '<td class="linecoluseunit left">'.$langs->trans('Unit').'</td>';
5413 }
5414 print '<td class="linecoldiscount right">'.$langs->trans('ReductionShort').'</td>';
5415 print '<td class="linecolht right">'.$langs->trans('TotalHT').'</td>';
5416 print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
5417 print '</tr>';
5418 $i = 0;
5419
5420 if (!empty($this->lines)) {
5421 foreach ($this->lines as $line) {
5422 $reshook = 0;
5423 //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line))) {
5424 if (is_object($hookmanager)) { // Old code is commented on preceding line.
5425 $parameters = array('line' => $line, 'i' => $i, 'restrictlist' => $restrictlist, 'selectedLines' => $selectedLines);
5426 if (!empty($line->fk_parent_line)) {
5427 $parameters['fk_parent_line'] = $line->fk_parent_line;
5428 }
5429 $reshook = $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5430 }
5431 if (empty($reshook)) {
5432 $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines);
5433 }
5434
5435 $i++;
5436 }
5437 }
5438 }
5439
5453 public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
5454 {
5455 global $langs, $conf;
5456
5457 //var_dump($line);
5458 if (!empty($line->date_start)) {
5459 $date_start = $line->date_start;
5460 } else {
5461 $date_start = $line->date_debut_prevue;
5462 if ($line->date_debut_reel) {
5463 $date_start = $line->date_debut_reel;
5464 }
5465 }
5466 if (!empty($line->date_end)) {
5467 $date_end = $line->date_end;
5468 } else {
5469 $date_end = $line->date_fin_prevue;
5470 if ($line->date_fin_reel) {
5471 $date_end = $line->date_fin_reel;
5472 }
5473 }
5474
5475 $this->tpl['id'] = $line->id;
5476
5477 $this->tpl['label'] = '';
5478 if (!empty($line->fk_parent_line)) {
5479 $this->tpl['label'] .= img_picto('', 'rightarrow');
5480 }
5481
5482 if (($line->info_bits & 2) == 2) { // TODO Not sure this is used for source object
5483 $discount = new DiscountAbsolute($this->db);
5484 if (property_exists($this, 'socid')) {
5485 $discount->fk_soc = $this->socid;
5486 $discount->socid = $this->socid;
5487 }
5488 $this->tpl['label'] .= $discount->getNomUrl(0, 'discount');
5489 } elseif (!empty($line->fk_product)) {
5490 if (empty($line->product)) {
5491 $line->fetch_product();
5492 }
5493 $productstatic = $line->product;
5494
5495 $this->tpl['label'] .= (is_object($productstatic) ? $productstatic->getNomUrl(1) : $line->ref);
5496 $this->tpl['label'] .= ' - '.(!empty($line->label) ? $line->label : $line->product_label);
5497 // Dates
5498 if ($line->product_type == 1 && ($date_start || $date_end)) {
5499 $this->tpl['label'] .= get_date_range($date_start, $date_end);
5500 }
5501 } else {
5502 $this->tpl['label'] .= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
5503 if (!empty($line->desc)) {
5504 $this->tpl['label'] .= $line->desc;
5505 } else {
5506 $this->tpl['label'] .= ($line->label ? '&nbsp;'.$line->label : '');
5507 }
5508
5509 // Dates
5510 if ($line->product_type == 1 && ($date_start || $date_end)) {
5511 $this->tpl['label'] .= get_date_range($date_start, $date_end);
5512 }
5513 }
5514
5515 if (!empty($line->desc)) {
5516 if ($line->desc == '(CREDIT_NOTE)') { // TODO Not sure this is used for source object
5517 $discount = new DiscountAbsolute($this->db);
5518 $discount->fetch($line->fk_remise_except);
5519 $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
5520 } elseif ($line->desc == '(DEPOSIT)') { // TODO Not sure this is used for source object
5521 $discount = new DiscountAbsolute($this->db);
5522 $discount->fetch($line->fk_remise_except);
5523 $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
5524 } elseif ($line->desc == '(EXCESS RECEIVED)') {
5525 $discount = new DiscountAbsolute($this->db);
5526 $discount->fetch($line->fk_remise_except);
5527 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
5528 } elseif ($line->desc == '(EXCESS PAID)') {
5529 $discount = new DiscountAbsolute($this->db);
5530 $discount->fetch($line->fk_remise_except);
5531 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
5532 } else {
5533 $this->tpl['description'] = dol_trunc($line->desc, 60);
5534 }
5535 } else {
5536 $this->tpl['description'] = '&nbsp;';
5537 }
5538
5539 // VAT Rate
5540 $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
5541 $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
5542 if (!empty($line->vat_src_code) && !preg_match('/\‍(/', $this->tpl['vat_rate'])) {
5543 $this->tpl['vat_rate'] .= ' ('.$line->vat_src_code.')';
5544 }
5545
5546 $this->tpl['price'] = price($line->subprice);
5547 $this->tpl['total_ht'] = price($line->total_ht);
5548 $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
5549 $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
5550 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5551 $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
5552 }
5553 $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
5554
5555 // Is the line strike or not
5556 $this->tpl['strike'] = 0;
5557 if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) {
5558 $this->tpl['strike'] = 1;
5559 }
5560
5561 // Output template part (modules that overwrite templates must declare this into descriptor)
5562 // Use global variables + $dateSelector + $seller and $buyer
5563 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5564 foreach ($dirtpls as $module => $reldir) {
5565 if (!empty($module)) {
5566 $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
5567 } else {
5568 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php';
5569 }
5570
5571 if (empty($conf->file->strict_mode)) {
5572 $res = @include $tpl;
5573 } else {
5574 $res = include $tpl; // for debug
5575 }
5576 if ($res) {
5577 break;
5578 }
5579 }
5580 }
5581
5582
5583 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5594 public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
5595 {
5596 // phpcs:enable
5597 $this->db->begin();
5598
5599 $sql = "INSERT INTO ".$this->db->prefix()."element_resources (";
5600 $sql .= "resource_id";
5601 $sql .= ", resource_type";
5602 $sql .= ", element_id";
5603 $sql .= ", element_type";
5604 $sql .= ", busy";
5605 $sql .= ", mandatory";
5606 $sql .= ") VALUES (";
5607 $sql .= ((int) $resource_id);
5608 $sql .= ", '".$this->db->escape($resource_type)."'";
5609 $sql .= ", '".$this->db->escape($this->id)."'";
5610 $sql .= ", '".$this->db->escape($this->element)."'";
5611 $sql .= ", '".$this->db->escape($busy)."'";
5612 $sql .= ", '".$this->db->escape($mandatory)."'";
5613 $sql .= ")";
5614
5615 dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
5616 if ($this->db->query($sql)) {
5617 $this->db->commit();
5618 return 1;
5619 } else {
5620 $this->error = $this->db->lasterror();
5621 $this->db->rollback();
5622 return 0;
5623 }
5624 }
5625
5626 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5635 public function delete_resource($rowid, $element, $notrigger = 0)
5636 {
5637 // phpcs:enable
5638 global $user;
5639
5640 $this->db->begin();
5641
5642 $sql = "DELETE FROM ".$this->db->prefix()."element_resources";
5643 $sql .= " WHERE rowid = ".((int) $rowid);
5644
5645 dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
5646
5647 $resql = $this->db->query($sql);
5648 if (!$resql) {
5649 $this->error = $this->db->lasterror();
5650 $this->db->rollback();
5651 return -1;
5652 } else {
5653 if (!$notrigger) {
5654 $result = $this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
5655 if ($result < 0) {
5656 $this->db->rollback();
5657 return -1;
5658 }
5659 }
5660 $this->db->commit();
5661 return 1;
5662 }
5663 }
5664
5665
5671 public function __clone()
5672 {
5673 // Force a copy of this->lines, otherwise it will point to same object.
5674 if (isset($this->lines) && is_array($this->lines)) {
5675 $nboflines = count($this->lines);
5676 for ($i = 0; $i < $nboflines; $i++) {
5677 if (is_object($this->lines[$i])) {
5678 $this->lines[$i] = clone $this->lines[$i];
5679 }
5680 }
5681 }
5682 }
5683
5697 protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
5698 {
5699 global $conf, $langs, $user, $hookmanager, $action;
5700
5701 $srctemplatepath = '';
5702
5703 $parameters = array('modelspath' => $modelspath, 'modele' => $modele, 'outputlangs' => $outputlangs, 'hidedetails' => $hidedetails, 'hidedesc' => $hidedesc, 'hideref' => $hideref, 'moreparams' => $moreparams);
5704 $reshook = $hookmanager->executeHooks('commonGenerateDocument', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5705
5706 if (!empty($reshook)) {
5707 return $reshook;
5708 }
5709
5710 dol_syslog("commonGenerateDocument modele=".$modele." outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
5711
5712 if (empty($modele)) {
5713 $this->error = 'BadValueForParameterModele';
5714 return -1;
5715 }
5716
5717 // Increase limit for PDF build
5718 $err = error_reporting();
5719 error_reporting(0);
5720 @set_time_limit(120);
5721 error_reporting($err);
5722
5723 // If selected model is a filename template (then $modele="modelname" or "modelname:filename")
5724 $tmp = explode(':', $modele, 2);
5725 $saved_model = $modele;
5726 if (!empty($tmp[1])) {
5727 $modele = $tmp[0];
5728 $srctemplatepath = $tmp[1];
5729 }
5730
5731 // Search template files
5732 $file = '';
5733 $classname = '';
5734 $filefound = '';
5735 $dirmodels = array('/');
5736 if (is_array($conf->modules_parts['models'])) {
5737 $dirmodels = array_merge($dirmodels, $conf->modules_parts['models']);
5738 }
5739 foreach ($dirmodels as $reldir) {
5740 foreach (array('doc', 'pdf') as $prefix) {
5741 if (in_array(get_class($this), array('Adherent'))) {
5742 // Member module use prefix_modele.class.php
5743 $file = $prefix."_".$modele.".class.php";
5744 } else {
5745 // Other module use prefix_modele.modules.php
5746 $file = $prefix."_".$modele.".modules.php";
5747 }
5748
5749 $file = dol_sanitizeFileName($file);
5750
5751 // We check if the file exists
5752 $file = dol_buildpath($reldir.$modelspath.$file, 0);
5753 if (file_exists($file)) {
5754 $filefound = $file;
5755 $classname = $prefix.'_'.$modele;
5756 break;
5757 }
5758 }
5759 if ($filefound) {
5760 break;
5761 }
5762 }
5763
5764 if ($filefound === '' || $classname === '') {
5765 $this->error = $langs->trans("Error").' Failed to load doc generator with modelpaths='.$modelspath.' - modele='.$modele;
5766 $this->errors[] = $this->error;
5767 dol_syslog($this->error, LOG_ERR);
5768 return -1;
5769 }
5770
5771 // Sanitize $filefound
5772 $filefound = dol_sanitizePathName($filefound);
5773
5774 // If generator was found
5775 global $db; // Required to solve a conception error making an include of some code that uses $db instead of $this->db just after.
5776
5777 require_once $filefound;
5778
5779 $obj = new $classname($this->db);
5780
5781 // If generator is ODT, we must have srctemplatepath defined, if not we set it.
5782 if ($obj->type == 'odt' && empty($srctemplatepath)) {
5783 $varfortemplatedir = $obj->scandir;
5784 if ($varfortemplatedir && getDolGlobalString($varfortemplatedir)) {
5785 $dirtoscan = getDolGlobalString($varfortemplatedir);
5786
5787 $listoffiles = array();
5788
5789 // Now we add first model found in directories scanned
5790 $listofdir = explode(',', $dirtoscan);
5791 foreach ($listofdir as $key => $tmpdir) {
5792 $tmpdir = trim($tmpdir);
5793 $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
5794 if (!$tmpdir) {
5795 unset($listofdir[$key]);
5796 continue;
5797 }
5798 if (is_dir($tmpdir)) {
5799 $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
5800 if (count($tmpfiles)) {
5801 $listoffiles = array_merge($listoffiles, $tmpfiles);
5802 }
5803 }
5804 }
5805
5806 if (count($listoffiles)) {
5807 foreach ($listoffiles as $record) {
5808 $srctemplatepath = $record['fullname'];
5809 break;
5810 }
5811 }
5812 }
5813
5814 if (empty($srctemplatepath)) {
5815 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
5816 return -1;
5817 }
5818 }
5819
5820 if ($obj->type == 'odt' && !empty($srctemplatepath)) {
5821 if (!dol_is_file($srctemplatepath)) {
5822 dol_syslog("Failed to locate template file ".$srctemplatepath, LOG_WARNING);
5823 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
5824 return -1;
5825 }
5826 }
5827
5828 // We save charset_output to restore it because write_file can change it if needed for
5829 // output format that does not support UTF8.
5830 $sav_charset_output = empty($outputlangs->charset_output) ? '' : $outputlangs->charset_output;
5831
5832 // update model_pdf in object
5833 $this->model_pdf = $saved_model;
5834
5835 if (in_array(get_class($this), array('Adherent'))) {
5836 '@phan-var-force Adherent $this';
5837 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, 'tmp_cards', $moreparams);
5838 } else {
5839 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
5840 }
5841 // After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
5842
5843 if ($resultwritefile > 0) {
5844 $outputlangs->charset_output = $sav_charset_output;
5845
5846 // We delete old preview
5847 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5848 dol_delete_preview($this);
5849
5850 // Index file in database
5851 if (!empty($obj->result['fullpath'])) {
5852 $destfull = $obj->result['fullpath'];
5853
5854 // Update the last_main_doc field into main object (if document generator has property ->update_main_doc_field set)
5855 $update_main_doc_field = 0;
5856 if (!empty($obj->update_main_doc_field)) {
5857 $update_main_doc_field = 1;
5858 }
5859
5860 // Check that the file exists, before indexing it.
5861 // Hint: It does not exist, if we create a PDF and auto delete the ODT File
5862 if (dol_is_file($destfull)) {
5863 $this->indexFile($destfull, $update_main_doc_field);
5864 }
5865 } else {
5866 dol_syslog('Method ->write_file was called on object '.get_class($obj).' and return a success but the return array ->result["fullpath"] was not set.', LOG_WARNING);
5867 }
5868
5869 // Success in building document. We build meta file.
5870 dol_meta_create($this);
5871
5872 return 1;
5873 } else {
5874 $outputlangs->charset_output = $sav_charset_output;
5875 $this->error = $obj->error;
5876 $this->errors = $obj->errors;
5877 dol_syslog("Error generating document for ".__CLASS__.". Error: ".$obj->error, LOG_ERR);
5878 return -1;
5879 }
5880 }
5881
5891 public function indexFile($destfull, $update_main_doc_field)
5892 {
5893 global $conf, $user;
5894
5895 $upload_dir = dirname($destfull);
5896 $destfile = basename($destfull);
5897 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
5898
5899 if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a tmp dir
5900 $filename = basename($destfile);
5901 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
5902 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
5903
5904 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
5905 $ecmfile = new EcmFiles($this->db);
5906 $result = $ecmfile->fetch(0, '', ($rel_dir ? $rel_dir.'/' : '').$filename);
5907
5908 // Set the public "share" key
5909 $setsharekey = false;
5910 if ($this->element == 'propal' || $this->element == 'proposal') {
5911 if (getDolGlobalInt("PROPOSAL_ALLOW_ONLINESIGN")) {
5912 $setsharekey = true; // feature to make online signature is not set or set to on (default)
5913 }
5914 if (getDolGlobalInt("PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD")) {
5915 $setsharekey = true;
5916 }
5917 }
5918 if ($this->element == 'commande' && getDolGlobalInt("ORDER_ALLOW_EXTERNAL_DOWNLOAD")) {
5919 $setsharekey = true;
5920 }
5921 if ($this->element == 'facture' && getDolGlobalInt("INVOICE_ALLOW_EXTERNAL_DOWNLOAD")) {
5922 $setsharekey = true;
5923 }
5924 if ($this->element == 'bank_account' && getDolGlobalInt("BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD")) {
5925 $setsharekey = true;
5926 }
5927 if ($this->element == 'product' && getDolGlobalInt("PRODUCT_ALLOW_EXTERNAL_DOWNLOAD")) {
5928 $setsharekey = true;
5929 }
5930 if ($this->element == 'contrat' && getDolGlobalInt("CONTRACT_ALLOW_EXTERNAL_DOWNLOAD")) {
5931 $setsharekey = true;
5932 }
5933 if ($this->element == 'fichinter' && getDolGlobalInt("FICHINTER_ALLOW_EXTERNAL_DOWNLOAD")) {
5934 $setsharekey = true;
5935 }
5936 if ($this->element == 'supplier_proposal' && getDolGlobalInt("SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD")) {
5937 $setsharekey = true;
5938 }
5939 if ($this->element == 'societe_rib' && getDolGlobalInt("SOCIETE_RIB_ALLOW_ONLINESIGN")) {
5940 $setsharekey = true;
5941 }
5942
5943 if ($setsharekey) {
5944 if (empty($ecmfile->share)) { // Because object not found or share not set yet
5945 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
5946 $ecmfile->share = getRandomPassword(true);
5947 }
5948 }
5949
5950 if ($result > 0) {
5951 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5952 $ecmfile->fullpath_orig = '';
5953 $ecmfile->gen_or_uploaded = 'generated';
5954 $ecmfile->description = ''; // indexed content
5955 $ecmfile->keywords = ''; // keyword content
5956 $result = $ecmfile->update($user);
5957 if ($result < 0) {
5958 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5959 return -1;
5960 }
5961 } else {
5962 $ecmfile->entity = $conf->entity;
5963 $ecmfile->filepath = $rel_dir;
5964 $ecmfile->filename = $filename;
5965 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5966 $ecmfile->fullpath_orig = '';
5967 $ecmfile->gen_or_uploaded = 'generated';
5968 $ecmfile->description = ''; // indexed content
5969 $ecmfile->keywords = ''; // keyword content
5970 $ecmfile->src_object_type = $this->table_element; // $this->table_name is 'myobject' or 'mymodule_myobject'.
5971 $ecmfile->src_object_id = $this->id;
5972
5973 $result = $ecmfile->create($user);
5974 if ($result < 0) {
5975 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5976 return -1;
5977 }
5978 }
5979
5980 /*$this->result['fullname']=$destfull;
5981 $this->result['filepath']=$ecmfile->filepath;
5982 $this->result['filename']=$ecmfile->filename;*/
5983 //var_dump($obj->update_main_doc_field);exit;
5984
5985 if ($update_main_doc_field && !empty($this->table_element)) {
5986 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath."/".$ecmfile->filename)."'";
5987 $sql .= " WHERE rowid = ".((int) $this->id);
5988
5989 $resql = $this->db->query($sql);
5990 if (!$resql) {
5991 dol_print_error($this->db);
5992 return -1;
5993 } else {
5994 $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
5995 }
5996 }
5997 }
5998
5999 return 1;
6000 }
6001
6009 public function addThumbs($file)
6010 {
6011 $file_osencoded = dol_osencode($file);
6012
6013 if (file_exists($file_osencoded)) {
6014 require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
6015
6016 $tmparraysize = getDefaultImageSizes();
6017 $maxwidthsmall = $tmparraysize['maxwidthsmall'];
6018 $maxheightsmall = $tmparraysize['maxheightsmall'];
6019 $maxwidthmini = $tmparraysize['maxwidthmini'];
6020 $maxheightmini = $tmparraysize['maxheightmini'];
6021 //$quality = $tmparraysize['quality'];
6022 $quality = 50; // For thumbs, we force quality to 50
6023
6024 // Create small thumbs for company (Ratio is near 16/9)
6025 // Used on logon for example
6026 vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
6027
6028 // Create mini thumbs for company (Ratio is near 16/9)
6029 // Used on menu or for setup page for example
6030 vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
6031 }
6032 }
6033
6041 public function delThumbs($file)
6042 {
6043 $imgThumbName = getImageFileNameForSize($file, '_small'); // Full path of thumb file
6044 dol_delete_file($imgThumbName);
6045 $imgThumbName = getImageFileNameForSize($file, '_mini'); // Full path of thumb file
6046 dol_delete_file($imgThumbName);
6047 }
6048
6049
6050 /* Functions common to commonobject and commonobjectline */
6051
6052 /* For default values */
6053
6067 public function getDefaultCreateValueFor($fieldname, $alternatevalue = null, $type = 'alphanohtml')
6068 {
6069 // If param here has been posted, we use this value first.
6070 if (GETPOSTISSET($fieldname)) {
6071 return GETPOST($fieldname, $type, 3);
6072 }
6073
6074 if (isset($alternatevalue)) {
6075 return $alternatevalue;
6076 }
6077
6078 $newelement = $this->element;
6079 if ($newelement == 'facture') {
6080 $newelement = 'invoice';
6081 }
6082 if ($newelement == 'commande') {
6083 $newelement = 'order';
6084 }
6085 if (empty($newelement)) {
6086 dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
6087 return '';
6088 }
6089
6090 $keyforfieldname = strtoupper($newelement.'_DEFAULT_'.$fieldname);
6091 //var_dump($keyforfieldname);
6092 if (getDolGlobalString($keyforfieldname)) {
6093 return getDolGlobalString($keyforfieldname);
6094 }
6095
6096 // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
6097 // store content into $conf->cache['overwrite_default']
6098
6099 return '';
6100 }
6101
6102
6103 /* For triggers */
6104
6105
6106 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6117 public function call_trigger($triggerName, $user)
6118 {
6119 // phpcs:enable
6120 global $langs, $conf;
6121
6122 if (!empty(self::TRIGGER_PREFIX) && strpos($triggerName, self::TRIGGER_PREFIX . '_') !== 0) {
6123 dol_print_error(null, 'The trigger "' . $triggerName . '" does not start with "' . self::TRIGGER_PREFIX . '_" as required.');
6124 exit;
6125 }
6126 if (!is_object($langs)) { // If lang was not defined, we set it. It is required by run_triggers().
6127 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
6128 $langs = new Translate('', $conf);
6129 }
6130
6131 include_once DOL_DOCUMENT_ROOT.'/core/class/interfaces.class.php';
6132 $interface = new Interfaces($this->db);
6133 $result = $interface->run_triggers($triggerName, $this, $user, $langs, $conf);
6134
6135 if ($result < 0) {
6136 if (!empty($this->errors)) {
6137 $this->errors = array_unique(array_merge($this->errors, $interface->errors)); // We use array_unique because when a trigger call another trigger on same object, this->errors is added twice.
6138 } else {
6139 $this->errors = $interface->errors;
6140 }
6141 }
6142 return $result;
6143 }
6144
6145
6146 /* Functions for data in other language */
6147
6148
6157 {
6158 // To avoid SQL errors. Probably not the better solution though
6159 if (!$this->element) {
6160 return 0;
6161 }
6162 if (!($this->id > 0)) {
6163 return 0;
6164 }
6165 if (is_array($this->array_languages)) {
6166 return 1;
6167 }
6168
6169 $this->array_languages = array();
6170
6171 $element = $this->element;
6172 if ($element == 'categorie') {
6173 $element = 'categories'; // For compatibility
6174 }
6175
6176 // Request to get translation values for object
6177 $sql = "SELECT rowid, property, lang , value";
6178 $sql .= " FROM ".$this->db->prefix()."object_lang";
6179 $sql .= " WHERE type_object = '".$this->db->escape($element)."'";
6180 $sql .= " AND fk_object = ".((int) $this->id);
6181
6182 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6183 $resql = $this->db->query($sql);
6184 if ($resql) {
6185 $numrows = $this->db->num_rows($resql);
6186 if ($numrows) {
6187 $i = 0;
6188 while ($i < $numrows) {
6189 $obj = $this->db->fetch_object($resql);
6190 $key = $obj->property;
6191 $value = $obj->value;
6192 $codelang = $obj->lang;
6193 $type = $this->fields[$key]['type'];
6194
6195 // we can add this attribute to object
6196 if (preg_match('/date/', $type)) {
6197 $this->array_languages[$key][$codelang] = $this->db->jdate($value);
6198 } else {
6199 $this->array_languages[$key][$codelang] = $value;
6200 }
6201
6202 $i++;
6203 }
6204 }
6205
6206 $this->db->free($resql);
6207
6208 if ($numrows) {
6209 return $numrows;
6210 } else {
6211 return 0;
6212 }
6213 } else {
6214 dol_print_error($this->db);
6215 return -1;
6216 }
6217 }
6218
6225 public function setValuesForExtraLanguages($onlykey = '')
6226 {
6227 // Get extra fields
6228 foreach ($_POST as $postfieldkey => $postfieldvalue) {
6229 $tmparray = explode('-', $postfieldkey);
6230 if ($tmparray[0] != 'field') {
6231 continue;
6232 }
6233
6234 $element = $tmparray[1];
6235 $key = $tmparray[2];
6236 $codelang = $tmparray[3];
6237 //var_dump("postfieldkey=".$postfieldkey." element=".$element." key=".$key." codelang=".$codelang);
6238
6239 if (!empty($onlykey) && $key != $onlykey) {
6240 continue;
6241 }
6242 if ($element != $this->element) {
6243 continue;
6244 }
6245
6246 $key_type = $this->fields[$key]['type'];
6247
6248 $enabled = 1;
6249 if (isset($this->fields[$key]['enabled'])) {
6250 $enabled = (int) dol_eval($this->fields[$key]['enabled'], 1, 1, '1');
6251 }
6252 /*$perms = 1;
6253 if (isset($this->fields[$key]['perms']))
6254 {
6255 $perms = (int) dol_eval($this->fields[$key]['perms'], 1, 1, '1');
6256 }*/
6257 if (empty($enabled)) {
6258 continue;
6259 }
6260 //if (empty($perms)) continue;
6261
6262 if (in_array($key_type, array('date'))) {
6263 // Clean parameters
6264 // TODO GMT date in memory must be GMT so we should add gm=true in parameters
6265 $value_key = dol_mktime(0, 0, 0, GETPOSTINT($postfieldkey."month"), GETPOSTINT($postfieldkey."day"), GETPOSTINT($postfieldkey."year"));
6266 } elseif (in_array($key_type, array('datetime'))) {
6267 // Clean parameters
6268 // TODO GMT date in memory must be GMT so we should add gm=true in parameters
6269 $value_key = dol_mktime(GETPOSTINT($postfieldkey."hour"), GETPOSTINT($postfieldkey."min"), 0, GETPOSTINT($postfieldkey."month"), GETPOSTINT($postfieldkey."day"), GETPOSTINT($postfieldkey."year"));
6270 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
6271 $value_arr = GETPOST($postfieldkey, 'array'); // check if an array
6272 if (!empty($value_arr)) {
6273 $value_key = implode(',', $value_arr);
6274 } else {
6275 $value_key = '';
6276 }
6277 } elseif (in_array($key_type, array('price', 'double'))) {
6278 $value_arr = GETPOST($postfieldkey, 'alpha');
6279 $value_key = price2num($value_arr);
6280 } else {
6281 $value_key = GETPOST($postfieldkey);
6282 if (in_array($key_type, array('link')) && $value_key == '-1') {
6283 $value_key = '';
6284 }
6285 }
6286
6287 $this->array_languages[$key][$codelang] = $value_key;
6288
6289 /*if ($nofillrequired) {
6290 $langs->load('errors');
6291 setEventMessages($langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required), null, 'errors');
6292 return -1;
6293 }*/
6294 }
6295
6296 return 1;
6297 }
6298
6299
6300 /* Functions for extrafields */
6301
6308 public function fetchNoCompute($id)
6309 {
6310 global $conf;
6311
6312 $savDisableCompute = $conf->disable_compute;
6313 $conf->disable_compute = 1;
6314
6315 $ret = $this->fetch($id); /* @phpstan-ignore-line */
6316
6317 $conf->disable_compute = $savDisableCompute;
6318
6319 return $ret;
6320 }
6321
6322 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6332 public function fetch_optionals($rowid = null, $optionsArray = null)
6333 {
6334 // phpcs:enable
6335 global $conf, $extrafields;
6336
6337 if (empty($rowid)) {
6338 $rowid = $this->id;
6339 }
6340 if (empty($rowid) && isset($this->rowid)) {
6341 $rowid = $this->rowid; // deprecated
6342 }
6343
6344 // To avoid SQL errors. Probably not the better solution though
6345 if (!$this->table_element) {
6346 return 0;
6347 }
6348
6349 $this->array_options = array();
6350
6351 if (!is_array($optionsArray)) {
6352 // If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
6353 if (!isset($extrafields) || !is_object($extrafields)) {
6354 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6355 $extrafields = new ExtraFields($this->db);
6356 }
6357
6358 // Load array of extrafields for elementype = $this->table_element
6359 if (empty($extrafields->attributes[$this->table_element]['loaded'])) {
6360 $extrafields->fetch_name_optionals_label($this->table_element);
6361 }
6362 $optionsArray = (!empty($extrafields->attributes[$this->table_element]['label']) ? $extrafields->attributes[$this->table_element]['label'] : null);
6363 } else {
6364 global $extrafields;
6365 dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
6366 }
6367
6368 $table_element = $this->table_element;
6369 if ($table_element == 'categorie') {
6370 $table_element = 'categories'; // For compatibility
6371 }
6372
6373 // Request to get complementary values
6374 if (is_array($optionsArray) && count($optionsArray) > 0) {
6375 $sql = "SELECT rowid";
6376 foreach ($optionsArray as $name => $label) {
6377 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || (!in_array($extrafields->attributes[$this->table_element]['type'][$name], ['separate', 'point', 'multipts', 'linestrg','polygon']))) {
6378 $sql .= ", ".$name;
6379 }
6380 // use geo sql fonction to read as text
6381 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] == 'point') {
6382 $sql .= ", ST_AsWKT(".$name.") as ".$name;
6383 }
6384 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] == 'multipts') {
6385 $sql .= ", ST_AsWKT(".$name.") as ".$name;
6386 }
6387 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] == 'linestrg') {
6388 $sql .= ", ST_AsWKT(".$name.") as ".$name;
6389 }
6390 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] == 'polygon') {
6391 $sql .= ", ST_AsWKT(".$name.") as ".$name;
6392 }
6393 }
6394 $sql .= " FROM ".$this->db->prefix().$table_element."_extrafields";
6395 $sql .= " WHERE fk_object = ".((int) $rowid);
6396
6397 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6398 $resql = $this->db->query($sql);
6399 if ($resql) {
6400 $numrows = $this->db->num_rows($resql);
6401 if ($numrows) {
6402 $tab = $this->db->fetch_array($resql);
6403
6404 foreach ($tab as $key => $value) {
6405 // Test fetch_array ! is_int($key) because fetch_array result is a mix table with Key as alpha and Key as int (depend db engine)
6406 if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && !is_int($key)) {
6407 // we can add this attribute to object
6408 if (!empty($extrafields->attributes[$this->table_element]) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date', 'datetime'))) {
6409 //var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
6410 $this->array_options["options_".$key] = $this->db->jdate($value);
6411 } else {
6412 $this->array_options["options_".$key] = $value;
6413 }
6414
6415 //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
6416 }
6417 if (!empty($extrafields->attributes[$this->table_element]['type'][$key]) && $extrafields->attributes[$this->table_element]['type'][$key] == 'password') {
6418 if (!empty($value) && preg_match('/^dolcrypt:/', $value)) {
6419 $this->array_options["options_".$key] = dolDecrypt($value);
6420 }
6421 }
6422 }
6423 } else {
6428 if (is_array($extrafields->attributes[$this->table_element]['label'])) {
6429 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
6430 $this->array_options['options_' . $key] = null;
6431 }
6432 }
6433 }
6434
6435 // If field is a computed field, value must become result of compute (regardless of whether a row exists
6436 // in the element's extrafields table)
6437 if (is_array($extrafields->attributes[$this->table_element]['label'])) {
6438 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
6439 if (!empty($extrafields->attributes[$this->table_element]) && !empty($extrafields->attributes[$this->table_element]['computed'][$key])) {
6440 //var_dump($conf->disable_compute);
6441 if (empty($conf->disable_compute)) {
6442 global $objectoffield; // We set a global variable to $objectoffield so
6443 $objectoffield = $this; // we can use it inside computed formula
6444 $this->array_options['options_' . $key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0, '2');
6445 }
6446 }
6447 }
6448 }
6449
6450 $this->db->free($resql);
6451
6452 if ($numrows) {
6453 return $numrows;
6454 } else {
6455 return 0;
6456 }
6457 } else {
6458 $this->errors[] = $this->db->lasterror;
6459 return -1;
6460 }
6461 }
6462 return 0;
6463 }
6464
6471 public function deleteExtraFields()
6472 {
6473 global $conf;
6474
6475 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6476 return 0;
6477 }
6478
6479 $this->db->begin();
6480
6481 $table_element = $this->table_element;
6482 if ($table_element == 'categorie') {
6483 $table_element = 'categories'; // For compatibility
6484 }
6485
6486 dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
6487
6488 $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6489
6490 $resql = $this->db->query($sql_del);
6491 if (!$resql) {
6492 $this->error = $this->db->lasterror();
6493 $this->db->rollback();
6494 return -1;
6495 } else {
6496 $this->db->commit();
6497 return 1;
6498 }
6499 }
6500
6511 public function insertExtraFields($trigger = '', $userused = null)
6512 {
6513 global $conf, $langs, $user;
6514
6515 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6516 return 0;
6517 }
6518
6519 if (empty($userused)) {
6520 $userused = $user;
6521 }
6522
6523 $error = 0;
6524
6525 if (!empty($this->array_options)) {
6526 // Check parameters
6527 $langs->load('admin');
6528 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6529 $extrafields = new ExtraFields($this->db);
6530 $target_extrafields = $extrafields->fetch_name_optionals_label($this->table_element);
6531
6532 // Eliminate copied source object extra fields that do not exist in target object
6533 $new_array_options = array();
6534 foreach ($this->array_options as $key => $value) {
6535 if (in_array(substr($key, 8), array_keys($target_extrafields))) { // We remove the 'options_' from $key for test
6536 $new_array_options[$key] = $value;
6537 } elseif (in_array($key, array_keys($target_extrafields))) { // We test on $key that does not contain the 'options_' prefix
6538 $new_array_options['options_'.$key] = $value;
6539 }
6540 }
6541
6542 foreach ($new_array_options as $key => $value) {
6543 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6544 $attributeType = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
6545 $attributeLabel = $langs->transnoentities($extrafields->attributes[$this->table_element]['label'][$attributeKey]);
6546 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
6547 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
6548 $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
6549 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$attributeKey];
6550
6551 // If we clone, we have to clean unique extrafields to prevent duplicates.
6552 // This behaviour can be prevented by external code by changing $this->context['createfromclone'] value in createFrom hook
6553 if (!empty($this->context['createfromclone']) && $this->context['createfromclone'] == 'createfromclone' && !empty($attributeUnique)) {
6554 $new_array_options[$key] = null;
6555 }
6556
6557 // If we create product combination, we have to clean unique extrafields to prevent duplicates.
6558 // This behaviour can be prevented by external code by changing $this->context['createproductcombination'] value in hook
6559 if (!empty($this->context['createproductcombination']) && $this->context['createproductcombination'] == 'createproductcombination' && !empty($attributeUnique)) {
6560 $new_array_options[$key] = null;
6561 }
6562
6563 // Similar code than into insertExtraFields
6564 if ($attributeRequired) {
6565 $v = $this->array_options[$key];
6566 if (ExtraFields::isEmptyValue($v, $attributeType)) {
6567 $langs->load("errors");
6568 dol_syslog("Mandatory field '".$key."' is empty during create and set to required into definition of extrafields");
6569 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6570 return -1;
6571 }
6572 }
6573
6574 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6575 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6576
6577 if (!empty($attrfieldcomputed)) {
6578 if (getDolGlobalString('MAIN_STORE_COMPUTED_EXTRAFIELDS')) {
6579 $value = dol_eval($attrfieldcomputed, 1, 0, '2');
6580 dol_syslog($langs->trans("Extrafieldcomputed")." on ".$attributeLabel."(".$value.")", LOG_DEBUG);
6581 $new_array_options[$key] = $value;
6582 } else {
6583 $new_array_options[$key] = null;
6584 }
6585 }
6586
6587 switch ($attributeType) {
6588 case 'int':
6589 if (!is_numeric($value) && $value != '') {
6590 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6591 return -1;
6592 } elseif ($value == '') {
6593 $new_array_options[$key] = null;
6594 }
6595 break;
6596 case 'price':
6597 case 'double':
6598 $value = price2num($value);
6599 if (!is_numeric($value) && $value != '') {
6600 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." for ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6601 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6602 return -1;
6603 } elseif ($value == '') {
6604 $value = null;
6605 }
6606 //dol_syslog("double value"." on ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6607 $new_array_options[$key] = $value;
6608 break;
6609 /*case 'select': // Not required, we chose value='0' for undefined values
6610 if ($value=='-1')
6611 {
6612 $this->array_options[$key] = null;
6613 }
6614 break;*/
6615 case 'password':
6616 $algo = '';
6617 if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
6618 // If there is an encryption choice, we use it to encrypt data before insert
6619 $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
6620 $algo = reset($tmparrays);
6621 if ($algo != '') {
6622 //global $action; // $action may be 'create', 'update', 'update_extras'...
6623 //var_dump($action);
6624 //var_dump($this->oldcopy);exit;
6625 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
6626 //var_dump('algo='.$algo.' '.$this->oldcopy->array_options[$key].' -> '.$this->array_options[$key]);
6627 if (isset($this->oldcopy->array_options[$key]) && $this->array_options[$key] == $this->oldcopy->array_options[$key]) {
6628 // If old value encrypted in database is same than submitted new value, it means we don't change it, so we don't update.
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] = $this->array_options[$key]; // Value is kept
6637 }
6638 } else {
6639 // If value has changed
6640 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
6641 if (!preg_match('/^dolcrypt:/', $this->array_options[$key])) {
6642 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6643 } else {
6644 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6645 }
6646 } else {
6647 $new_array_options[$key] = dol_hash($this->array_options[$key], $algo);
6648 }
6649 }
6650 } else {
6651 //var_dump('jjj'.$algo.' '.$this->oldcopy->array_options[$key].' -> '.$this->array_options[$key]);
6652 // If this->oldcopy is not defined, we can't know if we change attribute or not, so we must keep value
6653 if ($algo == 'dolcrypt' && !preg_match('/^dolcrypt:/', $this->array_options[$key])) { // dolibarr reversible encryption
6654 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6655 } else {
6656 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6657 }
6658 }
6659 } else {
6660 // No encryption
6661 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6662 }
6663 } else { // Common usage
6664 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6665 }
6666 break;
6667 case 'date':
6668 case 'datetime':
6669 // If data is a string instead of a timestamp, we convert it
6670 if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6671 $this->array_options[$key] = strtotime($this->array_options[$key]);
6672 }
6673 $new_array_options[$key] = $this->db->idate($this->array_options[$key]);
6674 break;
6675 case 'datetimegmt':
6676 // If data is a string instead of a timestamp, we convert it
6677 if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6678 $this->array_options[$key] = strtotime($this->array_options[$key]);
6679 }
6680 $new_array_options[$key] = $this->db->idate($this->array_options[$key], 'gmt');
6681 break;
6682 case 'link':
6683 $param_list = array_keys($attributeParam['options']);
6684 // 0 : ObjectName
6685 // 1 : classPath
6686 $InfoFieldList = explode(":", $param_list[0]);
6687 dol_include_once($InfoFieldList[1]);
6688 if ($InfoFieldList[0] && class_exists($InfoFieldList[0])) {
6689 if ($value == '-1') { // -1 is key for no defined in combo list of objects
6690 $new_array_options[$key] = '';
6691 } elseif ($value) {
6692 $object = new $InfoFieldList[0]($this->db);
6693 if (is_numeric($value)) {
6694 $res = $object->fetch($value); // Common case
6695 } else {
6696 $res = $object->fetch('', $value); // For compatibility
6697 }
6698
6699 if ($res > 0) {
6700 $new_array_options[$key] = $object->id;
6701 } else {
6702 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
6703 return -1;
6704 }
6705 }
6706 } else {
6707 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6708 }
6709 break;
6710 case 'checkbox':
6711 case 'chkbxlst':
6712 if (is_array($this->array_options[$key])) {
6713 $new_array_options[$key] = implode(',', $this->array_options[$key]);
6714 } else {
6715 $new_array_options[$key] = $this->array_options[$key];
6716 }
6717 break;
6718 }
6719 }
6720
6721 $this->db->begin();
6722
6723 $table_element = $this->table_element;
6724 if ($table_element == 'categorie') {
6725 $table_element = 'categories'; // For compatibility
6726 }
6727
6728 dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG);
6729
6730 $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6731 $this->db->query($sql_del);
6732
6733 $sql = "INSERT INTO ".$this->db->prefix().$table_element."_extrafields (fk_object";
6734 foreach ($new_array_options as $key => $value) {
6735 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6736 // Add field of attribute
6737 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator
6738 $sql .= ",".$attributeKey;
6739 }
6740 }
6741 // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6742 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6743 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6744 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6745 $sql .= ",".$tmpkey;
6746 }
6747 }
6748 }
6749 $sql .= ") VALUES (".$this->id;
6750
6751 foreach ($new_array_options as $key => $value) {
6752 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6753 // Add field of attribute
6754 if (!in_array($extrafields->attributes[$this->table_element]['type'][$attributeKey], ['separate', 'point', 'multipts', 'linestrg', 'polygon'])) { // Only for other type than separator)
6755 if ($new_array_options[$key] != '' || $new_array_options[$key] == '0') {
6756 $sql .= ",'".$this->db->escape($new_array_options[$key])."'";
6757 } else {
6758 $sql .= ",null";
6759 }
6760 }
6761 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] == 'point') { // for point type
6762 if (!empty($new_array_options[$key])) {
6763 $sql .= ",ST_PointFromText('".$this->db->escape($new_array_options[$key])."')";
6764 } else {
6765 $sql .= ",null";
6766 }
6767 }
6768 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] == 'multipts') { // for point type
6769 if (!empty($new_array_options[$key])) {
6770 $sql .= ",ST_MultiPointFromText('".$this->db->escape($new_array_options[$key])."')";
6771 } else {
6772 $sql .= ",null";
6773 }
6774 }
6775 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] == 'linestrg') { // for linestring type
6776 if (!empty($new_array_options[$key])) {
6777 $sql .= ",ST_LineFromText('".$this->db->escape($new_array_options[$key])."')";
6778 } else {
6779 $sql .= ",null";
6780 }
6781 }
6782 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] == 'polygon') { // for polygon type
6783 if (!empty($new_array_options[$key])) {
6784 $sql .= ",ST_PolyFromText('".$this->db->escape($new_array_options[$key])."')";
6785 } else {
6786 $sql .= ",null";
6787 }
6788 }
6789 }
6790 // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6791 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6792 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6793 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6794 if (in_array($tmpval, array('int', 'double', 'price'))) {
6795 $sql .= ", 0";
6796 } else {
6797 $sql .= ", ''";
6798 }
6799 }
6800 }
6801 }
6802
6803 $sql .= ")";
6804
6805 $resql = $this->db->query($sql);
6806 if (!$resql) {
6807 $this->error = $this->db->lasterror();
6808 $error++;
6809 }
6810
6811 if (!$error && $trigger) {
6812 // Call trigger
6813 $this->context = array('extrafieldaddupdate' => 1);
6814 $result = $this->call_trigger($trigger, $userused);
6815 if ($result < 0) {
6816 $error++;
6817 }
6818 // End call trigger
6819 }
6820
6821 if ($error) {
6822 $this->db->rollback();
6823 return -1;
6824 } else {
6825 $this->db->commit();
6826 return 1;
6827 }
6828 } else {
6829 return 0;
6830 }
6831 }
6832
6843 public function insertExtraLanguages($trigger = '', $userused = null)
6844 {
6845 global $conf, $langs, $user;
6846
6847 if (empty($userused)) {
6848 $userused = $user;
6849 }
6850
6851 $error = 0;
6852
6853 if (getDolGlobalString('MAIN_EXTRALANGUAGES_DISABLED')) {
6854 return 0; // For avoid conflicts if trigger used
6855 }
6856
6857 if (is_array($this->array_languages)) {
6858 $new_array_languages = $this->array_languages;
6859
6860 foreach ($new_array_languages as $key => $value) {
6861 $attributeKey = $key;
6862 $attributeType = $this->fields[$attributeKey]['type'];
6863 $attributeLabel = $this->fields[$attributeKey]['label'];
6864
6865 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6866 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6867
6868 switch ($attributeType) {
6869 case 'int':
6870 if (is_array($value) || (!is_numeric($value) && $value != '')) {
6871 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6872 return -1;
6873 } elseif ($value == '') {
6874 $new_array_languages[$key] = null;
6875 }
6876 break;
6877 case 'double':
6878 $value = price2num($value);
6879 if (!is_numeric($value) && $value != '') {
6880 dol_syslog($langs->trans("ExtraLanguageHasWrongValue")." on ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6881 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6882 return -1;
6883 } elseif ($value == '') {
6884 $new_array_languages[$key] = null;
6885 } else {
6886 $new_array_languages[$key] = $value;
6887 }
6888 break;
6889 /*case 'select': // Not required, we chose value='0' for undefined values
6890 if ($value=='-1')
6891 {
6892 $this->array_options[$key] = null;
6893 }
6894 break;*/
6895 }
6896 }
6897
6898 $this->db->begin();
6899
6900 $table_element = $this->table_element;
6901 if ($table_element == 'categorie') { // TODO Rename table llx_categories_extrafields into llx_categorie_extrafields so we can remove this.
6902 $table_element = 'categories'; // For compatibility
6903 }
6904
6905 dol_syslog(get_class($this)."::insertExtraLanguages delete then insert", LOG_DEBUG);
6906
6907 foreach ($new_array_languages as $key => $langcodearray) { // $key = 'name', 'town', ...
6908 foreach ($langcodearray as $langcode => $value) {
6909 $sql_del = "DELETE FROM ".$this->db->prefix()."object_lang";
6910 $sql_del .= " WHERE fk_object = ".((int) $this->id)." AND property = '".$this->db->escape($key)."' AND type_object = '".$this->db->escape($table_element)."'";
6911 $sql_del .= " AND lang = '".$this->db->escape($langcode)."'";
6912 $this->db->query($sql_del);
6913
6914 if ($value !== '') {
6915 $sql = "INSERT INTO ".$this->db->prefix()."object_lang (fk_object, property, type_object, lang, value";
6916 $sql .= ") VALUES (".$this->id.", '".$this->db->escape($key)."', '".$this->db->escape($table_element)."', '".$this->db->escape($langcode)."', '".$this->db->escape($value)."'";
6917 $sql .= ")";
6918
6919 $resql = $this->db->query($sql);
6920 if (!$resql) {
6921 $this->error = $this->db->lasterror();
6922 $error++;
6923 break;
6924 }
6925 }
6926 }
6927 }
6928
6929 if (!$error && $trigger) {
6930 // Call trigger
6931 $this->context = array('extralanguagesaddupdate' => 1);
6932 $result = $this->call_trigger($trigger, $userused);
6933 if ($result < 0) {
6934 $error++;
6935 }
6936 // End call trigger
6937 }
6938
6939 if ($error) {
6940 $this->db->rollback();
6941 return -1;
6942 } else {
6943 $this->db->commit();
6944 return 1;
6945 }
6946 } else {
6947 return 0;
6948 }
6949 }
6950
6961 public function updateExtraField($key, $trigger = null, $userused = null)
6962 {
6963 global $conf, $langs, $user, $hookmanager;
6964
6965 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6966 return 0;
6967 }
6968
6969 if (empty($userused)) {
6970 $userused = $user;
6971 }
6972
6973 $error = 0;
6974
6975 if (!empty($this->array_options) && isset($this->array_options["options_".$key])) {
6976 // Check parameters
6977 $langs->load('admin');
6978 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6979 $extrafields = new ExtraFields($this->db);
6980 $extrafields->fetch_name_optionals_label($this->table_element);
6981
6982 $value = $this->array_options["options_".$key];
6983
6984 $attributeKey = $key;
6985 $attributeType = $extrafields->attributes[$this->table_element]['type'][$key];
6986 $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key];
6987 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key];
6988 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
6989 $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
6990 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$key];
6991
6992 // Similar code than into insertExtraFields
6993 if ($attributeRequired) {
6994 $mandatorypb = false;
6995 if ($attributeType == 'link' && $this->array_options["options_".$key] == '-1') {
6996 $mandatorypb = true;
6997 }
6998 if ($this->array_options["options_".$key] === '') {
6999 $mandatorypb = true;
7000 }
7001 if ($mandatorypb) {
7002 $langs->load("errors");
7003 dol_syslog("Mandatory field 'options_".$key."' is empty during update and set to required into definition of extrafields");
7004 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
7005 return -1;
7006 }
7007 }
7008
7009 // $new_array_options will be used for direct update, so must contains formatted data for the UPDATE.
7010 $new_array_options = $this->array_options;
7011
7012 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
7013 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
7014 if (!empty($attrfieldcomputed)) {
7015 if (getDolGlobalString('MAIN_STORE_COMPUTED_EXTRAFIELDS')) {
7016 $value = dol_eval($attrfieldcomputed, 1, 0, '2');
7017 dol_syslog($langs->trans("Extrafieldcomputed")." on ".$attributeLabel."(".$value.")", LOG_DEBUG);
7018
7019 $new_array_options["options_".$key] = $value;
7020
7021 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7022 } else {
7023 $new_array_options["options_".$key] = null;
7024
7025 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7026 }
7027 }
7028
7029 switch ($attributeType) {
7030 case 'int':
7031 if (!is_numeric($value) && $value != '') {
7032 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
7033 return -1;
7034 } elseif ($value === '') {
7035 $new_array_options["options_".$key] = null;
7036
7037 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7038 }
7039 break;
7040 case 'price':
7041 case 'double':
7042 $value = price2num($value);
7043 if (!is_numeric($value) && $value != '') {
7044 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." on ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
7045 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
7046 return -1;
7047 } elseif ($value === '') {
7048 $value = null;
7049 }
7050 //dol_syslog("double value"." on ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
7051 $new_array_options["options_".$key] = $value;
7052
7053 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7054 break;
7055 /*case 'select': // Not required, we chose value='0' for undefined values
7056 if ($value=='-1')
7057 {
7058 $new_array_options["options_".$key] = $value;
7059
7060 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7061 }
7062 break;*/
7063 case 'password':
7064 $algo = '';
7065 if ($this->array_options["options_".$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
7066 // If there is an encryption choice, we use it to encrypt data before insert
7067 $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
7068 $algo = reset($tmparrays);
7069 if ($algo != '') {
7070 //global $action; // $action may be 'create', 'update', 'update_extras'...
7071 //var_dump($action);
7072 //var_dump($this->oldcopy);exit;
7073 //var_dump($key.' '.$this->array_options["options_".$key].' '.$algo);
7074 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
7075 //var_dump($this->oldcopy->array_options["options_".$key]); var_dump($this->array_options["options_".$key]);
7076 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.
7077 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
7078 if (!preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) {
7079 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]); // warning, must be called when on the master
7080 } else {
7081 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7082 }
7083 } else {
7084 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7085 }
7086 } else {
7087 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
7088 if (!preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) {
7089 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]);
7090 } else {
7091 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7092 }
7093 } else {
7094 $new_array_options["options_".$key] = dol_hash($this->array_options["options_".$key], $algo);
7095 }
7096 }
7097 } else {
7098 if ($algo == 'dolcrypt' && !preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) { // dolibarr reversible encryption
7099 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]); // warning, must be called when on the master
7100 } else {
7101 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7102 }
7103 }
7104 } else {
7105 // No encryption
7106 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7107 }
7108 } else { // Common usage
7109 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
7110 }
7111
7112 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7113 break;
7114 case 'date':
7115 case 'datetime':
7116 if (empty($this->array_options["options_".$key])) {
7117 $new_array_options["options_".$key] = null;
7118
7119 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7120 } else {
7121 $new_array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key]);
7122 }
7123 break;
7124 case 'datetimegmt':
7125 if (empty($this->array_options["options_".$key])) {
7126 $new_array_options["options_".$key] = null;
7127
7128 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7129 } else {
7130 $new_array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key], 'gmt');
7131 }
7132 break;
7133 case 'boolean':
7134 if (empty($this->array_options["options_".$key])) {
7135 $new_array_options["options_".$key] = null;
7136
7137 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7138 }
7139 break;
7140 case 'link':
7141 if ($this->array_options["options_".$key] === '') {
7142 $new_array_options["options_".$key] = null;
7143
7144 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7145 }
7146 break;
7147 /*
7148 case 'link':
7149 $param_list = array_keys($attributeParam['options']);
7150 // 0 : ObjectName
7151 // 1 : classPath
7152 $InfoFieldList = explode(":", $param_list[0]);
7153 dol_include_once($InfoFieldList[1]);
7154 if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
7155 {
7156 if ($value == '-1') // -1 is key for no defined in combo list of objects
7157 {
7158 $new_array_options[$key] = '';
7159 } elseif ($value) {
7160 $object = new $InfoFieldList[0]($this->db);
7161 if (is_numeric($value)) $res = $object->fetch($value); // Common case
7162 else $res = $object->fetch('', $value); // For compatibility
7163
7164 if ($res > 0) $new_array_options[$key] = $object->id;
7165 else {
7166 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
7167 $this->db->rollback();
7168 return -1;
7169 }
7170 }
7171 } else {
7172 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
7173 }
7174 break;
7175 */
7176 case 'checkbox':
7177 case 'chkbxlst':
7178 $new_array_options = array();
7179 if (is_array($this->array_options["options_".$key])) {
7180 $new_array_options["options_".$key] = implode(',', $this->array_options["options_".$key]);
7181 } else {
7182 $new_array_options["options_".$key] = $this->array_options["options_".$key];
7183 }
7184
7185 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7186 break;
7187 }
7188
7189 $this->db->begin();
7190
7191 $linealreadyfound = 0;
7192
7193 // 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)
7194 $table_element = $this->table_element;
7195 if ($table_element == 'categorie') { // TODO Rename table llx_categories_extrafields into llx_categorie_extrafields so we can remove this.
7196 $table_element = 'categories'; // For compatibility
7197 }
7198
7199 $sql = "SELECT COUNT(rowid) as nb FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
7200 $resql = $this->db->query($sql);
7201 if ($resql) {
7202 $tmpobj = $this->db->fetch_object($resql);
7203 if ($tmpobj) {
7204 $linealreadyfound = $tmpobj->nb;
7205 }
7206 }
7207
7208 //var_dump('linealreadyfound='.$linealreadyfound.' sql='.$sql); exit;
7209 if ($linealreadyfound) {
7210 if ($this->array_options["options_".$key] === null) {
7211 $sql = "UPDATE ".$this->db->prefix().$table_element."_extrafields SET ".$key." = null";
7212 } else {
7213 $sql = "UPDATE ".$this->db->prefix().$table_element."_extrafields SET ".$key." = '".$this->db->escape($new_array_options["options_".$key])."'";
7214 }
7215 $sql .= " WHERE fk_object = ".((int) $this->id);
7216
7217 $resql = $this->db->query($sql);
7218 if (!$resql) {
7219 $error++;
7220 $this->error = $this->db->lasterror();
7221 }
7222 } else {
7223 $result = $this->insertExtraFields('', $user);
7224 if ($result < 0) {
7225 $error++;
7226 }
7227 }
7228
7229 if (!$error) {
7230 $parameters = array('key' => $key);
7231 global $action;
7232 $reshook = $hookmanager->executeHooks('updateExtraFieldBeforeCommit', $parameters, $this, $action);
7233 if ($reshook < 0) {
7234 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
7235 }
7236 }
7237
7238 if (!$error && $trigger) {
7239 // Call trigger
7240 $this->context = array('extrafieldupdate' => 1);
7241 $result = $this->call_trigger($trigger, $userused);
7242 if ($result < 0) {
7243 $error++;
7244 }
7245 // End call trigger
7246 }
7247
7248 if ($error) {
7249 dol_syslog(__METHOD__.$this->error, LOG_ERR);
7250 $this->db->rollback();
7251 return -1;
7252 } else {
7253 $this->db->commit();
7254 return 1;
7255 }
7256 } else {
7257 return 0;
7258 }
7259 }
7260
7267 public function getExtraField($key)
7268 {
7269 return $this->array_options['options_'.$key] ?? null;
7270 }
7271
7279 public function setExtraField($key, $value)
7280 {
7281 $this->array_options['options_'.$key] = $value;
7282 }
7283
7294 public function updateExtraLanguages($key, $trigger = null, $userused = null)
7295 {
7296 global $conf, $langs, $user;
7297
7298 if (empty($userused)) {
7299 $userused = $user;
7300 }
7301
7302 $error = 0;
7303
7304 if (getDolGlobalString('MAIN_EXTRALANGUAGES_DISABLED')) {
7305 return 0; // For avoid conflicts if trigger used
7306 }
7307
7308 return 0;
7309 }
7310
7311
7327 public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0, $nonewbutton = 0)
7328 {
7329 global $conf, $langs, $form;
7330
7331 if (!is_object($form)) {
7332 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
7333 $form = new Form($this->db);
7334 }
7335
7336 if (!empty($this->fields)) {
7337 $val = $this->fields[$key];
7338 }
7339
7340 // Validation tests and output
7341 $fieldValidationErrorMsg = '';
7342 $validationClass = '';
7343 $fieldValidationErrorMsg = $this->getFieldError($key);
7344 if (!empty($fieldValidationErrorMsg)) {
7345 $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
7346 } else {
7347 $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
7348 }
7349
7350 $valuemultiselectinput = array();
7351 $out = '';
7352 $type = '';
7353 $isDependList = 0;
7354 $param = array();
7355 $param['options'] = array();
7356 $reg = array();
7357 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7358 $size = !empty($this->fields[$key]['size']) ? $this->fields[$key]['size'] : 0;
7359 // Because we work on extrafields
7360 if (preg_match('/^(integer|link):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7361 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
7362 $type = 'link';
7363 } elseif (preg_match('/^(integer|link):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7364 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7365 $type = 'link';
7366 } elseif (preg_match('/^(integer|link):(.*):(.*)/i', $val['type'], $reg)) {
7367 $param['options'] = array($reg[2].':'.$reg[3] => 'N');
7368 $type = 'link';
7369 } elseif (preg_match('/^(sellist):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7370 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
7371 $type = 'sellist';
7372 } elseif (preg_match('/^(sellist):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7373 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7374 $type = 'sellist';
7375 } elseif (preg_match('/^(sellist):(.*):(.*)/i', $val['type'], $reg)) {
7376 $param['options'] = array($reg[2].':'.$reg[3] => 'N');
7377 $type = 'sellist';
7378 } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
7379 $param['options'] = array($reg[1] => 'N');
7380 $type = 'chkbxlst';
7381 } elseif (preg_match('/varchar\‍((\d+)\‍)/', $val['type'], $reg)) {
7382 $param['options'] = array();
7383 $type = 'varchar';
7384 $size = $reg[1];
7385 } elseif (preg_match('/varchar/', $val['type'])) {
7386 $param['options'] = array();
7387 $type = 'varchar';
7388 } else {
7389 $param['options'] = array();
7390 $type = $this->fields[$key]['type'];
7391 }
7392 //var_dump($type); var_dump($param['options']);
7393
7394 // Special case that force options and type ($type can be integer, varchar, ...)
7395 if (!empty($this->fields[$key]['arrayofkeyval']) && is_array($this->fields[$key]['arrayofkeyval'])) {
7396 $param['options'] = $this->fields[$key]['arrayofkeyval'];
7397 // Special case that prevent to force $type to have multiple input
7398 if (empty($this->fields[$key]['multiinput'])) {
7399 $type = (($this->fields[$key]['type'] == 'checkbox') ? $this->fields[$key]['type'] : 'select');
7400 }
7401 }
7402
7403 $label = $this->fields[$key]['label'];
7404 //$elementtype=$this->fields[$key]['elementtype']; // Seems not used
7405 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7406 $default = (!empty($this->fields[$key]['default']) ? $this->fields[$key]['default'] : '');
7407 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7408 $computed = (!empty($this->fields[$key]['computed']) ? $this->fields[$key]['computed'] : '');
7409 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7410 $unique = (!empty($this->fields[$key]['unique']) ? $this->fields[$key]['unique'] : 0);
7411 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7412 $required = (!empty($this->fields[$key]['required']) ? $this->fields[$key]['required'] : 0);
7413 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7414 $autofocusoncreate = (!empty($this->fields[$key]['autofocusoncreate']) ? $this->fields[$key]['autofocusoncreate'] : 0);
7415
7416 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7417 $langfile = (!empty($this->fields[$key]['langfile']) ? $this->fields[$key]['langfile'] : '');
7418 // @phan-suppress-next-line PhanTypeArraySuspiciousNullable
7419 $list = (!empty($this->fields[$key]['list']) ? $this->fields[$key]['list'] : 0);
7420 $hidden = (in_array(abs($this->fields[$key]['visible']), array(0, 2)) ? 1 : 0);
7421
7422 $objectid = $this->id;
7423
7424 if ($computed) {
7425 if (!preg_match('/^search_/', $keyprefix)) {
7426 return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
7427 } else {
7428 return '';
7429 }
7430 }
7431
7432 // Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine
7433 if (empty($morecss) && !empty($val['css'])) {
7434 $morecss = $val['css'];
7435 } elseif (empty($morecss)) {
7436 if ($type == 'date') {
7437 $morecss = 'minwidth100imp';
7438 } elseif ($type == 'datetime' || $type == 'link') { // link means an foreign key to another primary id
7439 $morecss = 'minwidth200imp';
7440 } elseif (in_array($type, array('int', 'integer', 'price')) || preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', (string) $type)) {
7441 $morecss = 'maxwidth75';
7442 } elseif ($type == 'url') {
7443 $morecss = 'minwidth400';
7444 } elseif ($type == 'boolean') {
7445 $morecss = '';
7446 } else {
7447 if (is_numeric($size) && round((float) $size) < 12) {
7448 $morecss = 'minwidth100';
7449 } elseif (is_numeric($size) && round((float) $size) <= 48) {
7450 $morecss = 'minwidth200';
7451 } else {
7452 $morecss = 'minwidth400';
7453 }
7454 }
7455 }
7456
7457 // Add validation state class
7458 if (!empty($validationClass)) {
7459 $morecss .= $validationClass;
7460 }
7461
7462 if (in_array($type, array('date'))) {
7463 $tmp = explode(',', $size);
7464 $newsize = $tmp[0];
7465 $showtime = 0;
7466
7467 // Do not show current date when field not required (see selectDate() method)
7468 if (!$required && $value == '') {
7469 $value = '-1';
7470 }
7471
7472 // TODO Must also support $moreparam
7473 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
7474 } elseif (in_array($type, array('datetime'))) {
7475 $tmp = explode(',', $size);
7476 $newsize = $tmp[0];
7477 $showtime = 1;
7478
7479 // Do not show current date when field not required (see selectDate() method)
7480 if (!$required && $value == '') {
7481 $value = '-1';
7482 }
7483
7484 // TODO Must also support $moreparam
7485 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
7486 } elseif (in_array($type, array('duration'))) {
7487 $out = $form->select_duration($keyprefix.$key.$keysuffix, $value, 0, 'text', 0, 1);
7488 } elseif (in_array($type, array('int', 'integer'))) {
7489 $tmp = explode(',', $size);
7490 $newsize = $tmp[0];
7491 $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' : '').'>';
7492 } elseif (in_array($type, array('real'))) {
7493 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7494 } elseif (preg_match('/varchar/', (string) $type)) {
7495 $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' : '').'>';
7496 } elseif (in_array($type, array('email', 'mail', 'phone', 'url', 'ip'))) {
7497 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7498 } elseif (preg_match('/^text/', (string) $type)) {
7499 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7500 if (!empty($param['options'])) {
7501 // If the textarea field has a list of arrayofkeyval into its definition, we suggest a combo with possible values to fill the textarea.
7502 //var_dump($param['options']);
7503 $out .= $form->selectarray($keyprefix.$key.$keysuffix."_multiinput", $param['options'], '', 1, 0, 0, "flat maxwidthonphone".$morecss);
7504 $out .= "<script>";
7505 $out .= '
7506 function handlemultiinputdisabling(htmlname){
7507 console.log("We handle the disabling of used options for "+htmlname+"_multiinput");
7508 multiinput = $("#"+htmlname+"_multiinput");
7509 multiinput.find("option").each(function(){
7510 tmpval = $("#"+htmlname).val();
7511 tmpvalarray = tmpval.split(",");
7512 valtotest = $(this).val();
7513 if(tmpvalarray.includes(valtotest)){
7514 $(this).prop("disabled",true);
7515 } else {
7516 if($(this).prop("disabled") == true){
7517 console.log(valtotest)
7518 $(this).prop("disabled", false);
7519 }
7520 }
7521 });
7522 }
7523
7524 $(document).ready(function () {
7525 $("#'.$keyprefix.$key.$keysuffix.'_multiinput").on("change",function() {
7526 console.log("We add the selected value to the text area '.$keyprefix.$key.$keysuffix.'");
7527 tmpval = $("#'.$keyprefix.$key.$keysuffix.'").val();
7528 tmpvalarray = tmpval.split(",");
7529 valtotest = $(this).val();
7530 if(valtotest != -1 && !tmpvalarray.includes(valtotest)){
7531 if(tmpval == ""){
7532 tmpval = valtotest;
7533 } else {
7534 tmpval = tmpval + "," + valtotest;
7535 }
7536 $("#'.$keyprefix.$key.$keysuffix.'").val(tmpval);
7537 handlemultiinputdisabling("'.$keyprefix.$key.$keysuffix.'");
7538 }
7539 });
7540 $("#'.$keyprefix.$key.$keysuffix.'").on("change",function(){
7541 handlemultiinputdisabling($(this).attr("id"));
7542 });
7543 handlemultiinputdisabling("'.$keyprefix.$key.$keysuffix.'");
7544 })';
7545 $out .= "</script>";
7546 }
7547 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7548 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
7549 $out .= (string) $doleditor->Create(1, '', true, '', '', '', $morecss);
7550 } else {
7551 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7552 }
7553 } elseif (preg_match('/^html/', (string) $type)) {
7554 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7555 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7556 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && getDolGlobalInt('FCKEDITOR_ENABLE_SOCIETE'), ROWS_5, '90%');
7557 $out = (string) $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
7558 } else {
7559 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7560 }
7561 } elseif ($type == 'boolean') {
7562 $checked = '';
7563 if (!empty($value)) {
7564 $checked = ' checked value="1" ';
7565 } else {
7566 $checked = ' value="1" ';
7567 }
7568 $out = '<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
7569 } elseif ($type == 'price') {
7570 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7571 $value = price($value);
7572 }
7573 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '.$langs->getCurrencySymbol($conf->currency);
7574 } elseif (preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', (string) $type)) {
7575 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7576 $value = price($value);
7577 }
7578 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
7579 } elseif ($type == 'select') { // combo list
7580 $out = '';
7581 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
7582 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7583 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7584 }
7585
7586 $tmpselect = '';
7587 $nbchoice = 0;
7588 foreach ($param['options'] as $keyb => $valb) {
7589 if ((string) $keyb == '') {
7590 continue;
7591 }
7592 if (strpos($valb, "|") !== false) {
7593 list($valb, $parent) = explode('|', $valb);
7594 }
7595 $nbchoice++;
7596 $tmpselect .= '<option value="'.$keyb.'"';
7597 $tmpselect .= (((string) $value == (string) $keyb) ? ' selected' : '');
7598 if (!empty($parent)) {
7599 $isDependList = 1;
7600 }
7601 $tmpselect .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
7602 $tmpselect .= '>'.$langs->trans($valb).'</option>';
7603 }
7604
7605 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7606 if ((!isset($this->fields[$key]['default'])) || ($this->fields[$key]['notnull'] != 1) || $nbchoice >= 2) {
7607 $out .= '<option value="0">&nbsp;</option>';
7608 }
7609 $out .= $tmpselect;
7610 $out .= '</select>';
7611 } elseif ($type == 'sellist') {
7612 $out = '';
7613 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
7614 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7615 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7616 }
7617
7618 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7619 if (is_array($param['options'])) {
7620 $tmpparamoptions = array_keys($param['options']);
7621 $paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
7622
7623 $InfoFieldList = explode(":", $paramoptions[0], 5);
7624 // 0 : tableName
7625 // 1 : label field name
7626 // 2 : key fields name (if different of rowid)
7627 // optional parameters...
7628 // 3 : key field parent (for dependent lists). How this is used ?
7629 // 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".
7630 // 5 : string category type. This replace the filter.
7631 // 6 : ids categories list separated by comma for category root. This replace the filter.
7632 // 7 : sort field
7633
7634 // If there is filter
7635 if (! empty($InfoFieldList[4])) {
7636 $pos = 0;
7637 $parenthesisopen = 0;
7638 while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
7639 if (substr($InfoFieldList[4], $pos, 1) == '(') {
7640 $parenthesisopen++;
7641 }
7642 if (substr($InfoFieldList[4], $pos, 1) == ')') {
7643 $parenthesisopen--;
7644 }
7645 $pos++;
7646 }
7647 $tmpbefore = substr($InfoFieldList[4], 0, $pos);
7648 $tmpafter = substr($InfoFieldList[4], $pos + 1);
7649 //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
7650 $InfoFieldList[4] = $tmpbefore;
7651 if ($tmpafter !== '') {
7652 $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
7653 }
7654 //var_dump($InfoFieldList);
7655
7656 // Fix better compatibility with some old extrafield syntax filter "(field=123)"
7657 $reg = array();
7658 if (preg_match('/^\‍(?([a-z0-9]+)([=<>]+)(\d+)\‍)?$/i', $InfoFieldList[4], $reg)) {
7659 $InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
7660 }
7661
7662 //var_dump($InfoFieldList);
7663 }
7664
7665 //$Usf = empty($paramoptions[1]) ? '' :$paramoptions[1];
7666
7667 $parentName = '';
7668 $parentField = '';
7669
7670 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7671
7672 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7673 if (strpos($InfoFieldList[4], 'extra.') !== false) {
7674 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7675 } else {
7676 $keyList = $InfoFieldList[2].' as rowid';
7677 }
7678 }
7679 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7680 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7681 $keyList .= ', '.$parentField;
7682 }
7683
7684 $filter_categorie = false;
7685 if (count($InfoFieldList) > 5) {
7686 if ($InfoFieldList[0] == 'categorie') {
7687 $filter_categorie = true;
7688 }
7689 }
7690
7691 if (!$filter_categorie) {
7692 $fields_label = explode('|', $InfoFieldList[1]);
7693 if (is_array($fields_label)) {
7694 $keyList .= ', ';
7695 $keyList .= implode(', ', $fields_label);
7696 }
7697
7698 $sqlwhere = '';
7699 $sql = "SELECT " . $keyList;
7700 $sql .= " FROM " . $this->db->prefix() . $InfoFieldList[0];
7701
7702 if (!empty($InfoFieldList[4])) {
7703 // can use SELECT request
7704 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7705 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7706 }
7707
7708 // current object id can be use into filter
7709 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7710 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
7711 } else {
7712 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7713 }
7714
7715 // We have to join on extrafield table
7716 $errstr = '';
7717 if (strpos($InfoFieldList[4], 'extra') !== false) {
7718 $sql .= " as main, " . $this->db->sanitize($this->db->prefix() . $InfoFieldList[0]) . "_extrafields as extra";
7719 $sqlwhere .= " WHERE extra.fk_object = main." . $this->db->sanitize($InfoFieldList[2]);
7720 $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
7721 } else {
7722 $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
7723 }
7724 } else {
7725 $sqlwhere .= ' WHERE 1=1';
7726 }
7727
7728 // Add Usf filter on second line
7729 /*
7730 if ($Usf) {
7731 $errorstr = '';
7732 $sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
7733 if (!$errorstr) {
7734 $sqlwhere .= $sqlusf;
7735 } else {
7736 $sqlwhere .= " AND invalid_usf_filter_of_extrafield";
7737 }
7738 }
7739 */
7740
7741 // Some tables may have field, some other not. For the moment we disable it.
7742 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7743 $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7744 }
7745 $sql .= $sqlwhere;
7746 //print $sql;
7747
7748 // Note: $InfoFieldList can be 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter[:CategoryIdType[:CategoryIdList[:Sortfield]]]]]]'
7749 if (isset($InfoFieldList[7]) && preg_match('/^[a-z0-9_\-,]+$/i', $InfoFieldList[7])) {
7750 $sql .= " ORDER BY ".$this->db->escape($InfoFieldList[7]);
7751 } else {
7752 $sql .= " ORDER BY ".$this->db->sanitize(implode(', ', $fields_label));
7753 }
7754
7755 dol_syslog(get_class($this) . '::showInputField type=sellist', LOG_DEBUG);
7756 $resql = $this->db->query($sql);
7757 if ($resql) {
7758 $out .= '<option value="0">&nbsp;</option>';
7759 $num = $this->db->num_rows($resql);
7760 $i = 0;
7761 while ($i < $num) {
7762 $labeltoshow = '';
7763 $obj = $this->db->fetch_object($resql);
7764
7765 // Several field into label (eq table:code|libelle:rowid)
7766 $notrans = false;
7767 $fields_label = explode('|', $InfoFieldList[1]);
7768 if (count($fields_label) > 1) {
7769 $notrans = true;
7770 foreach ($fields_label as $field_toshow) {
7771 $labeltoshow .= $obj->$field_toshow . ' ';
7772 }
7773 } else {
7774 $labeltoshow = $obj->{$InfoFieldList[1]};
7775 }
7776 $labeltoshow = dol_trunc($labeltoshow, 45);
7777
7778 if ($value == $obj->rowid) {
7779 foreach ($fields_label as $field_toshow) {
7780 $translabel = $langs->trans($obj->$field_toshow);
7781 if ($translabel != $obj->$field_toshow) {
7782 $labeltoshow = dol_trunc($translabel) . ' ';
7783 } else {
7784 $labeltoshow = dol_trunc($obj->$field_toshow) . ' ';
7785 }
7786 }
7787 $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7788 } else {
7789 if (!$notrans) {
7790 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7791 if ($translabel != $obj->{$InfoFieldList[1]}) {
7792 $labeltoshow = dol_trunc($translabel, 18);
7793 } else {
7794 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]});
7795 }
7796 }
7797 if (empty($labeltoshow)) {
7798 $labeltoshow = '(not defined)';
7799 }
7800 if ($value == $obj->rowid) {
7801 $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7802 }
7803
7804 if (!empty($InfoFieldList[3]) && $parentField) {
7805 $parent = $parentName . ':' . $obj->{$parentField};
7806 $isDependList = 1;
7807 }
7808
7809 $out .= '<option value="' . $obj->rowid . '"';
7810 $out .= ($value == $obj->rowid ? ' selected' : '');
7811 $out .= (!empty($parent) ? ' parent="' . $parent . '"' : '');
7812 $out .= '>' . $labeltoshow . '</option>';
7813 }
7814
7815 $i++;
7816 }
7817 $this->db->free($resql);
7818 } else {
7819 print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
7820 }
7821 } else {
7822 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7823 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
7824 $out .= '<option value="0">&nbsp;</option>';
7825 foreach ($data as $data_key => $data_value) {
7826 $out .= '<option value="' . $data_key . '"';
7827 $out .= ($value == $data_key ? ' selected' : '');
7828 $out .= '>' . $data_value . '</option>';
7829 }
7830 }
7831 }
7832 $out .= '</select>';
7833 } elseif ($type == 'checkbox') {
7834 $value_arr = explode(',', $value);
7835 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ? null : $param['options']), $value_arr, 0, 0, $morecss, 0, '100%');
7836 } elseif ($type == 'radio') {
7837 $out = '';
7838 foreach ($param['options'] as $keyopt => $valopt) {
7839 $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
7840 $out .= ' value="'.$keyopt.'"';
7841 $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
7842 $out .= ($value == $keyopt ? 'checked' : '');
7843 $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$valopt.'</label><br>';
7844 }
7845 } elseif ($type == 'chkbxlst') {
7846 if (is_array($value)) {
7847 $value_arr = $value;
7848 } else {
7849 $value_arr = explode(',', $value);
7850 }
7851
7852 if (is_array($param['options'])) {
7853 $tmpparamoptions = array_keys($param['options']);
7854 $paramoptions = preg_split('/[\r\n]+/', $tmpparamoptions[0]);
7855
7856 $InfoFieldList = explode(":", $paramoptions[0], 5);
7857 // 0 : tableName
7858 // 1 : label field name
7859 // 2 : key fields name (if different of rowid)
7860 // optional parameters...
7861 // 3 : key field parent (for dependent lists). How this is used ?
7862 // 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".
7863 // 5 : string category type. This replace the filter.
7864 // 6 : ids categories list separated by comma for category root. This replace the filter.
7865 // 7 : sort field
7866
7867 // If there is a filter
7868 if (! empty($InfoFieldList[4])) {
7869 $pos = 0;
7870 $parenthesisopen = 0;
7871 while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
7872 if (substr($InfoFieldList[4], $pos, 1) == '(') {
7873 $parenthesisopen++;
7874 }
7875 if (substr($InfoFieldList[4], $pos, 1) == ')') {
7876 $parenthesisopen--;
7877 }
7878 $pos++;
7879 }
7880 $tmpbefore = substr($InfoFieldList[4], 0, $pos);
7881 $tmpafter = substr($InfoFieldList[4], $pos + 1);
7882 //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
7883 $InfoFieldList[4] = $tmpbefore;
7884 if ($tmpafter !== '') {
7885 $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
7886 }
7887
7888 // Fix better compatibility with some old extrafield syntax filter "(field=123)"
7889 $reg = array();
7890 if (preg_match('/^\‍(?([a-z0-9]+)([=<>]+)(\d+)\‍)?$/i', $InfoFieldList[4], $reg)) {
7891 $InfoFieldList[4] = '('.$reg[1].':'.$reg[2].':'.$reg[3].')';
7892 }
7893
7894 //var_dump($InfoFieldList);
7895 }
7896
7897 //$Usf = empty($paramoptions[1]) ? '' :$paramoptions[1];
7898
7899 '@phan-var-force array{0:string,1:string,2:string,3:string,3:string,5:string,6:string} $InfoFieldList';
7900
7901 $parentName = '';
7902 $parentField = '';
7903
7904 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7905
7906 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7907 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7908 $keyList .= ', '.$parentField;
7909 }
7910 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7911 if (strpos($InfoFieldList[4], 'extra.') !== false) {
7912 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7913 } else {
7914 $keyList = $InfoFieldList[2].' as rowid';
7915 }
7916 }
7917
7918 $filter_categorie = false;
7919 if (count($InfoFieldList) > 5) {
7920 if ($InfoFieldList[0] == 'categorie') {
7921 $filter_categorie = true;
7922 }
7923 }
7924
7925 // Common filter
7926 if (!$filter_categorie) {
7927 $fields_label = explode('|', $InfoFieldList[1]);
7928 if (is_array($fields_label)) {
7929 $keyList .= ', ';
7930 $keyList .= implode(', ', $fields_label);
7931 }
7932
7933 $sqlwhere = '';
7934 $sql = "SELECT " . $keyList;
7935 $sql .= ' FROM ' . $this->db->prefix() . $InfoFieldList[0];
7936
7937 if (!empty($InfoFieldList[4])) {
7938 // can use SELECT request
7939 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7940 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7941 }
7942
7943 // current object id can be use into filter
7944 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7945 $InfoFieldList[4] = str_replace('$ID$', (string) $objectid, $InfoFieldList[4]);
7946 } else {
7947 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7948 }
7949
7950 // We have to join on extrafield table
7951 $errstr = '';
7952 if (strpos($InfoFieldList[4], 'extra') !== false) {
7953 $sql .= ' as main, ' . $this->db->sanitize($this->db->prefix() . $InfoFieldList[0]) . '_extrafields as extra';
7954 $sqlwhere .= " WHERE extra.fk_object = main." . $this->db->sanitize($InfoFieldList[2]);
7955 $sqlwhere .= " AND " . $InfoFieldList[4];
7956 } else {
7957 $sqlwhere .= " WHERE " . $InfoFieldList[4];
7958 }
7959 } else {
7960 $sqlwhere .= ' WHERE 1=1';
7961 }
7962
7963 // Add Usf filter on second line
7964 /*
7965 if ($Usf) {
7966 $errorstr = '';
7967 $sqlusf .= forgeSQLFromUniversalSearchCriteria($Usf, $errorstr);
7968 if (!$errorstr) {
7969 $sqlwhere .= $sqlusf;
7970 } else {
7971 $sqlwhere .= " AND invalid_usf_filter_of_extrafield";
7972 }
7973 }
7974 */
7975
7976 // Some tables may have field, some other not. For the moment we disable it.
7977 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7978 $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7979 }
7980 // $sql.=preg_replace('/^ AND /','',$sqlwhere);
7981 // print $sql;
7982
7983 $sql .= $sqlwhere;
7984
7985 dol_syslog(get_class($this) . '::showInputField type=chkbxlst', LOG_DEBUG);
7986
7987 $resql = $this->db->query($sql);
7988 if ($resql) {
7989 $num = $this->db->num_rows($resql);
7990 $i = 0;
7991
7992 $data = array();
7993
7994 while ($i < $num) {
7995 $labeltoshow = '';
7996 $obj = $this->db->fetch_object($resql);
7997
7998 $notrans = false;
7999 // Several field into label (eq table:code|libelle:rowid)
8000 $fields_label = explode('|', $InfoFieldList[1]);
8001 if (count($fields_label) > 1) {
8002 $notrans = true;
8003 foreach ($fields_label as $field_toshow) {
8004 $labeltoshow .= $obj->$field_toshow . ' ';
8005 }
8006 } else {
8007 $labeltoshow = $obj->{$InfoFieldList[1]};
8008 }
8009 $labeltoshow = dol_trunc($labeltoshow, 45);
8010
8011 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8012 foreach ($fields_label as $field_toshow) {
8013 $translabel = $langs->trans($obj->$field_toshow);
8014 if ($translabel != $obj->$field_toshow) {
8015 $labeltoshow = dol_trunc($translabel, 18) . ' ';
8016 } else {
8017 $labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
8018 }
8019 }
8020
8021 $data[$obj->rowid] = $labeltoshow;
8022 } else {
8023 if (!$notrans) {
8024 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
8025 if ($translabel != $obj->{$InfoFieldList[1]}) {
8026 $labeltoshow = dol_trunc($translabel, 18);
8027 } else {
8028 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
8029 }
8030 }
8031 if (empty($labeltoshow)) {
8032 $labeltoshow = '(not defined)';
8033 }
8034
8035 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8036 $data[$obj->rowid] = $labeltoshow;
8037 }
8038
8039 if (!empty($InfoFieldList[3]) && $parentField) {
8040 $parent = $parentName . ':' . $obj->{$parentField};
8041 $isDependList = 1;
8042 }
8043
8044 $data[$obj->rowid] = $labeltoshow;
8045 }
8046
8047 $i++;
8048 }
8049 $this->db->free($resql);
8050
8051 $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, 0, 0, $morecss, 0, '100%');
8052 } else {
8053 print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
8054 }
8055 } else {
8056 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
8057 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
8058 $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, 0, 0, $morecss, 0, '100%');
8059 }
8060 }
8061 } elseif ($type == 'link') {
8062 // $param_list='ObjectName:classPath[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
8063 // Filter can contains some ':' inside.
8064 $param_list = array_keys($param['options']);
8065 $param_list_array = explode(':', $param_list[0], 4);
8066
8067 $showempty = (($required && $default != '') ? 0 : 1);
8068
8069 if (!preg_match('/search_/', $keyprefix)) {
8070 if (!empty($param_list_array[2])) { // If the entry into $fields is set to add a create button
8071 if (!empty($this->fields[$key]['picto'])) {
8072 $morecss .= ' widthcentpercentminusxx';
8073 } else {
8074 $morecss .= ' widthcentpercentminusx';
8075 }
8076 } else {
8077 if (!empty($this->fields[$key]['picto'])) {
8078 $morecss .= ' widthcentpercentminusx';
8079 }
8080 }
8081 }
8082 $objectfield = $this->element.($this->module ? '@'.$this->module : '').':'.$key.$keysuffix;
8083 $out = $form->selectForForms($param_list_array[0], $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, $moreparam, 0, (empty($val['disabled']) ? 0 : 1), '', $objectfield);
8084
8085 if (!empty($param_list_array[2])) { // If the entry into $fields is set, we must add a create button
8086 if ((!GETPOSTISSET('backtopage') || strpos(GETPOST('backtopage'), $_SERVER['PHP_SELF']) === 0) // // To avoid to open several times the 'Plus' button (we accept only one level)
8087 && empty($val['disabled']) && empty($nonewbutton)) { // and to avoid to show the button if the field is protected by a "disabled".
8088 list($class, $classfile) = explode(':', $param_list[0]);
8089 if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) {
8090 $url_path = dol_buildpath(dirname(dirname($classfile)).'/card.php', 1);
8091 } else {
8092 $url_path = dol_buildpath(dirname(dirname($classfile)).'/'.strtolower($class).'_card.php', 1);
8093 }
8094 $paramforthenewlink = '';
8095 $paramforthenewlink .= (GETPOSTISSET('action') ? '&action='.GETPOST('action', 'aZ09') : '');
8096 $paramforthenewlink .= (GETPOSTISSET('id') ? '&id='.GETPOSTINT('id') : '');
8097 $paramforthenewlink .= (GETPOSTISSET('origin') ? '&origin='.GETPOST('origin', 'aZ09') : '');
8098 $paramforthenewlink .= (GETPOSTISSET('originid') ? '&originid='.GETPOSTINT('originid') : '');
8099 $paramforthenewlink .= '&fk_'.strtolower($class).'=--IDFORBACKTOPAGE--';
8100 // TODO Add JavaScript code to add input fields already filled into $paramforthenewlink so we won't loose them when going back to main page
8101 $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>';
8102 }
8103 }
8104 } elseif ($type == 'password') {
8105 // If prefix is 'search_', field is used as a filter, we use a common text field.
8106 if ($keyprefix.$key.$keysuffix == 'pass_crypted') {
8107 $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="pass" id="pass" value="" '.($moreparam ? $moreparam : '').'>';
8108 $out .= '<input type="hidden" name="pass_crypted" id="pass_crypted" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
8109 } else {
8110 $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
8111 }
8112 } elseif ($type == 'array') {
8113 $newval = $val;
8114 $newval['type'] = 'varchar(256)';
8115
8116 $out = '';
8117 if (!empty($value)) {
8118 foreach ($value as $option) {
8119 $out .= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
8120 $out .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $morecss).'<br></span>';
8121 }
8122 }
8123 $out .= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
8124
8125 $newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
8126 $newInput .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $morecss).'<br></span>';
8127
8128 if (!empty($conf->use_javascript_ajax)) {
8129 $out .= '
8130 <script nonce="'.getNonce().'">
8131 $(document).ready(function() {
8132 $("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
8133 $("'.dol_escape_js($newInput).'").insertBefore(this);
8134 });
8135
8136 $(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
8137 $(this).parent().remove();
8138 });
8139 });
8140 </script>';
8141 }
8142 }
8143 if (!empty($hidden)) {
8144 $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
8145 }
8146
8147 if ($isDependList == 1) {
8148 $out .= $this->getJSListDependancies('_common');
8149 }
8150 /* Add comments
8151 if ($type == 'date') $out.=' (YYYY-MM-DD)';
8152 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
8153 */
8154
8155 // Display error message for field
8156 if (!empty($fieldValidationErrorMsg) && function_exists('getFieldErrorIcon')) {
8157 $out .= ' '.getFieldErrorIcon($fieldValidationErrorMsg);
8158 }
8159
8160 return $out;
8161 }
8162
8176 public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '')
8177 {
8178 global $conf, $langs, $form;
8179
8180 if (!is_object($form)) {
8181 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
8182 $form = new Form($this->db);
8183 }
8184
8185 //$label = empty($val['label']) ? '' : $val['label'];
8186 $type = empty($val['type']) ? '' : $val['type'];
8187 $size = empty($val['css']) ? '' : $val['css'];
8188 $reg = array();
8189
8190 // Convert var to be able to share same code than showOutputField of extrafields
8191 if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
8192 $type = 'varchar'; // convert varchar(xx) int varchar
8193 $size = $reg[1];
8194 } elseif (preg_match('/varchar/', $type)) {
8195 $type = 'varchar'; // convert varchar(xx) int varchar
8196 }
8197 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8198 if (empty($this->fields[$key]['multiinput'])) {
8199 $type = (($this->fields[$key]['type'] == 'checkbox') ? $this->fields[$key]['type'] : 'select');
8200 }
8201 }
8202 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8203 $type = 'link';
8204 }
8205
8206 $default = empty($val['default']) ? '' : $val['default'];
8207 $computed = empty($val['computed']) ? '' : $val['computed'];
8208 $unique = empty($val['unique']) ? '' : $val['unique'];
8209 $required = empty($val['required']) ? '' : $val['required'];
8210 $param = array();
8211 $param['options'] = array();
8212
8213 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8214 $param['options'] = $val['arrayofkeyval'];
8215 }
8216 if (preg_match('/^integer:([^:]*):([^:]*)/i', $val['type'], $reg)) { // ex: integer:User:user/class/user.class.php
8217 $type = 'link';
8218 $stringforoptions = $reg[1].':'.$reg[2];
8219 // Special case: Force addition of getnomurlparam1 to -1 for users
8220 if ($reg[1] == 'User') {
8221 $stringforoptions .= ':#getnomurlparam1=-1';
8222 }
8223 $param['options'] = array($stringforoptions => $stringforoptions);
8224 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
8225 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
8226 $type = 'sellist';
8227 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
8228 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
8229 $type = 'sellist';
8230 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
8231 $param['options'] = array($reg[1].':'.$reg[2] => 'N');
8232 $type = 'sellist';
8233 } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
8234 $param['options'] = array($reg[1] => 'N');
8235 $type = 'chkbxlst';
8236 }
8237
8238 $langfile = empty($val['langfile']) ? '' : $val['langfile'];
8239 $list = (empty($val['list']) ? '' : $val['list']);
8240 $help = (empty($val['help']) ? '' : $val['help']);
8241 $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)
8242
8243 if ($hidden) {
8244 return '';
8245 }
8246
8247 // If field is a computed field, value must become result of compute
8248 if ($computed) {
8249 // Make the eval of compute string
8250 //var_dump($computed);
8251 $value = dol_eval($computed, 1, 0, '2');
8252 }
8253
8254 if (empty($morecss)) {
8255 if ($type == 'date') {
8256 $morecss = 'minwidth100imp';
8257 } elseif ($type == 'datetime' || $type == 'timestamp') {
8258 $morecss = 'minwidth200imp';
8259 } elseif (in_array($type, array('int', 'double', 'price'))) {
8260 $morecss = 'maxwidth75';
8261 } elseif ($type == 'url') {
8262 $morecss = 'minwidth400';
8263 } elseif ($type == 'boolean') {
8264 $morecss = '';
8265 } else {
8266 if (is_numeric($size) && round((float) $size) < 12) {
8267 $morecss = 'minwidth100';
8268 } elseif (is_numeric($size) && round((float) $size) <= 48) {
8269 $morecss = 'minwidth200';
8270 } else {
8271 $morecss = 'minwidth400';
8272 }
8273 }
8274 }
8275
8276 // Format output value differently according to properties of field
8277 if (in_array($key, array('rowid', 'ref')) && method_exists($this, 'getNomUrl')) {
8278 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.
8279 $value = $this->getNomUrl(1, '', 0, '', 1);
8280 }
8281 } elseif ($key == 'status' && method_exists($this, 'getLibStatut')) {
8282 $value = $this->getLibStatut(3);
8283 } elseif ($type == 'date') {
8284 if (!empty($value)) {
8285 $value = dol_print_date($value, 'day'); // We suppose dates without time are always gmt (storage of course + output)
8286 } else {
8287 $value = '';
8288 }
8289 } elseif ($type == 'datetime' || $type == 'timestamp') {
8290 if (!empty($value)) {
8291 $value = dol_print_date($value, 'dayhour', 'tzuserrel');
8292 } else {
8293 $value = '';
8294 }
8295 } elseif ($type == 'duration') {
8296 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
8297 if (!is_null($value) && $value !== '') {
8298 $value = convertSecondToTime($value, 'allhourmin');
8299 }
8300 } elseif ($type == 'double' || $type == 'real') {
8301 if (!is_null($value) && $value !== '') {
8302 $value = price($value);
8303 }
8304 } elseif ($type == 'boolean') {
8305 $checked = '';
8306 if (!empty($value)) {
8307 $checked = ' checked ';
8308 }
8309 if (getDolGlobalInt('MAIN_OPTIMIZEFORTEXTBROWSER') < 2) {
8310 $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
8311 } else {
8312 $value = yn($value ? 1 : 0);
8313 }
8314 } elseif ($type == 'mail' || $type == 'email') {
8315 $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
8316 } elseif ($type == 'url') {
8317 $value = dol_print_url($value, '_blank', 32, 1);
8318 } elseif ($type == 'phone') {
8319 $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
8320 } elseif ($type == 'ip') {
8321 $value = dol_print_ip($value, 0);
8322 } elseif ($type == 'price') {
8323 if (!is_null($value) && $value !== '') {
8324 $value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
8325 }
8326 } elseif ($type == 'select') {
8327 $value = isset($param['options'][(string) $value]) ? $param['options'][(string) $value] : '';
8328 if (strpos($value, "|") !== false) {
8329 $value = $langs->trans(explode('|', $value)[0]);
8330 } elseif (! is_numeric($value)) {
8331 $value = $langs->trans($value);
8332 }
8333 } elseif ($type == 'sellist') {
8334 $param_list = array_keys($param['options']);
8335 $InfoFieldList = explode(":", $param_list[0]);
8336
8337 $selectkey = "rowid";
8338 $keyList = 'rowid';
8339
8340 if (count($InfoFieldList) > 2 && !empty($InfoFieldList[2])) {
8341 $selectkey = $InfoFieldList[2];
8342 $keyList = $InfoFieldList[2].' as rowid';
8343 }
8344
8345 $fields_label = explode('|', $InfoFieldList[1]);
8346 if (is_array($fields_label)) {
8347 $keyList .= ', ';
8348 $keyList .= implode(', ', $fields_label);
8349 }
8350
8351 $filter_categorie = false;
8352 if (count($InfoFieldList) > 5) {
8353 if ($InfoFieldList[0] == 'categorie') {
8354 $filter_categorie = true;
8355 }
8356 }
8357
8358 $sql = "SELECT ".$keyList;
8359 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
8360 if (strpos($InfoFieldList[4], 'extra') !== false) {
8361 $sql .= ' as main';
8362 }
8363 if ($selectkey == 'rowid' && empty($value)) {
8364 $sql .= " WHERE ".$selectkey." = 0";
8365 } elseif ($selectkey == 'rowid') {
8366 $sql .= " WHERE ".$selectkey." = ".((int) $value);
8367 } else {
8368 $sql .= " WHERE ".$selectkey." = '".$this->db->escape($value)."'";
8369 }
8370
8371 //$sql.= ' AND entity = '.$conf->entity;
8372
8373 dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
8374 $resql = $this->db->query($sql);
8375 if ($resql) {
8376 if (!$filter_categorie) {
8377 $value = ''; // value was used, so now we reste it to use it to build final output
8378 $numrows = $this->db->num_rows($resql);
8379 if ($numrows) {
8380 $obj = $this->db->fetch_object($resql);
8381
8382 // Several field into label (eq table:code|libelle:rowid)
8383 $fields_label = explode('|', $InfoFieldList[1]);
8384
8385 if (is_array($fields_label) && count($fields_label) > 1) {
8386 foreach ($fields_label as $field_toshow) {
8387 $translabel = '';
8388 if (!empty($obj->$field_toshow)) {
8389 $translabel = $langs->trans($obj->$field_toshow);
8390 }
8391 if ($translabel != $field_toshow) {
8392 $value .= dol_trunc($translabel, 18) . ' ';
8393 } else {
8394 $value .= $obj->$field_toshow . ' ';
8395 }
8396 }
8397 } else {
8398 $translabel = '';
8399 if (!empty($obj->{$InfoFieldList[1]})) {
8400 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
8401 }
8402 if ($translabel != $obj->{$InfoFieldList[1]}) {
8403 $value = dol_trunc($translabel, 18);
8404 } else {
8405 $value = $obj->{$InfoFieldList[1]};
8406 }
8407 }
8408 }
8409 } else {
8410 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
8411
8412 $toprint = array();
8413 $obj = $this->db->fetch_object($resql);
8414 $c = new Categorie($this->db);
8415 $c->fetch($obj->rowid);
8416 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
8417 foreach ($ways as $way) {
8418 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
8419 }
8420 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
8421 }
8422 } else {
8423 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
8424 }
8425 } elseif ($type == 'radio') {
8426 $value = $param['options'][(string) $value];
8427 } elseif ($type == 'checkbox') {
8428 $value_arr = explode(',', (string) $value);
8429 $value = '';
8430 if (is_array($value_arr) && count($value_arr) > 0) {
8431 $toprint = array();
8432 foreach ($value_arr as $keyval => $valueval) {
8433 if (!empty($valueval)) {
8434 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $param['options'][$valueval] . '</li>';
8435 }
8436 }
8437 if (!empty($toprint)) {
8438 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
8439 }
8440 }
8441 } elseif ($type == 'chkbxlst') {
8442 $value_arr = (isset($value) ? explode(',', $value) : array());
8443
8444 $param_list = array_keys($param['options']);
8445 $InfoFieldList = explode(":", $param_list[0]);
8446
8447 $selectkey = "rowid";
8448 $keyList = 'rowid';
8449
8450 if (count($InfoFieldList) >= 3) {
8451 $selectkey = $InfoFieldList[2];
8452 $keyList = $InfoFieldList[2].' as rowid';
8453 }
8454
8455 $fields_label = explode('|', $InfoFieldList[1]);
8456 if (is_array($fields_label)) {
8457 $keyList .= ', ';
8458 $keyList .= implode(', ', $fields_label);
8459 }
8460
8461 $filter_categorie = false;
8462 if (count($InfoFieldList) > 5) {
8463 if ($InfoFieldList[0] == 'categorie') {
8464 $filter_categorie = true;
8465 }
8466 }
8467
8468 $sql = "SELECT ".$keyList;
8469 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
8470 if (isset($InfoFieldList[4]) && strpos($InfoFieldList[4], 'extra') !== false) {
8471 $sql .= ' as main';
8472 }
8473 // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
8474 // $sql.= ' AND entity = '.$conf->entity;
8475
8476 dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG);
8477 $resql = $this->db->query($sql);
8478 if ($resql) {
8479 if (!$filter_categorie) {
8480 $value = ''; // value was used, so now we reset it to use it to build final output
8481 $toprint = array();
8482 while ($obj = $this->db->fetch_object($resql)) {
8483 // Several field into label (eq table:code|libelle:rowid)
8484 $fields_label = explode('|', $InfoFieldList[1]);
8485 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8486 if (is_array($fields_label) && count($fields_label) > 1) {
8487 foreach ($fields_label as $field_toshow) {
8488 $translabel = '';
8489 if (!empty($obj->$field_toshow)) {
8490 $translabel = $langs->trans($obj->$field_toshow);
8491 }
8492 if ($translabel != $field_toshow) {
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->$field_toshow . '</li>';
8496 }
8497 }
8498 } else {
8499 $translabel = '';
8500 if (!empty($obj->{$InfoFieldList[1]})) {
8501 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
8502 }
8503 if ($translabel != $obj->{$InfoFieldList[1]}) {
8504 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
8505 } else {
8506 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->{$InfoFieldList[1]} . '</li>';
8507 }
8508 }
8509 }
8510 }
8511 } else {
8512 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
8513
8514 $toprint = array();
8515 while ($obj = $this->db->fetch_object($resql)) {
8516 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8517 $c = new Categorie($this->db);
8518 $c->fetch($obj->rowid);
8519 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
8520 foreach ($ways as $way) {
8521 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
8522 }
8523 }
8524 }
8525 }
8526 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
8527 } else {
8528 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
8529 }
8530 } elseif ($type == 'link') {
8531 $out = '';
8532
8533 // only if something to display (perf)
8534 if ($value) {
8535 $param_list = array_keys($param['options']);
8536 // Example: $param_list='ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
8537 // Example: $param_list='ObjectClass:PathToClass:#getnomurlparam1=-1#getnomurlparam2=customer'
8538
8539 $InfoFieldList = explode(":", $param_list[0]);
8540
8541 $classname = $InfoFieldList[0];
8542 $classpath = $InfoFieldList[1];
8543
8544 // Set $getnomurlparam1 et getnomurlparam2
8545 $getnomurlparam = 3;
8546 $getnomurlparam2 = '';
8547 $regtmp = array();
8548 if (preg_match('/#getnomurlparam1=([^#]*)/', $param_list[0], $regtmp)) {
8549 $getnomurlparam = $regtmp[1];
8550 }
8551 if (preg_match('/#getnomurlparam2=([^#]*)/', $param_list[0], $regtmp)) {
8552 $getnomurlparam2 = $regtmp[1];
8553 }
8554
8555 if (!empty($classpath)) {
8556 dol_include_once($InfoFieldList[1]);
8557
8558 if ($classname && !class_exists($classname)) {
8559 // from V19 of Dolibarr, In some cases link use element instead of class, example project_task
8560 // TODO use newObjectByElement() introduce in V20 by PR #30036 for better errors management
8561 $element_prop = getElementProperties($classname);
8562 if ($element_prop) {
8563 $classname = $element_prop['classname'];
8564 }
8565 }
8566
8567
8568 if ($classname && class_exists($classname)) {
8569 $object = new $classname($this->db);
8570 if ($object->element === 'product') { // Special case for product because default valut of fetch are wrong
8571 $result = $object->fetch($value, '', '', '', 0, 1, 1);
8572 } else {
8573 $result = $object->fetch($value);
8574 }
8575 if ($result > 0) {
8576 if ($object->element === 'product') {
8577 $get_name_url_param_arr = array($getnomurlparam, $getnomurlparam2, 0, -1, 0, '', 0);
8578 if (isset($val['get_name_url_params'])) {
8579 $get_name_url_params = explode(':', $val['get_name_url_params']);
8580 if (!empty($get_name_url_params)) {
8581 $param_num_max = count($get_name_url_param_arr) - 1;
8582 foreach ($get_name_url_params as $param_num => $param_value) {
8583 if ($param_num > $param_num_max) {
8584 break;
8585 }
8586 $get_name_url_param_arr[$param_num] = $param_value;
8587 }
8588 }
8589 }
8590
8594 $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]);
8595 } else {
8596 $value = $object->getNomUrl($getnomurlparam, $getnomurlparam2);
8597 }
8598 } else {
8599 $value = '';
8600 }
8601 }
8602 } else {
8603 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
8604 return 'Error bad setup of extrafield';
8605 }
8606 } else {
8607 $value = '';
8608 }
8609 } elseif ($type == 'password') {
8610 $value = '<span class="opacitymedium">'.$langs->trans("Encrypted").'</span>';
8611 //$value = preg_replace('/./i', '*', $value);
8612 } elseif ($type == 'array') {
8613 if (is_array($value)) {
8614 $value = implode('<br>', $value);
8615 } else {
8616 dol_syslog(__METHOD__.' Expected array from dol_eval, but got '.gettype($value), LOG_ERR);
8617 return 'Error unexpected result from code evaluation';
8618 }
8619 } else { // text|html|varchar
8620 $value = dol_htmlentitiesbr($value);
8621 }
8622
8623 //print $type.'-'.$size.'-'.$value;
8624 $out = $value;
8625
8626 return is_null($out) ? '' : $out;
8627 }
8628
8635 public function clearFieldError($fieldKey)
8636 {
8637 $this->error = '';
8638 unset($this->validateFieldsErrors[$fieldKey]);
8639 }
8640
8648 public function setFieldError($fieldKey, $msg = '')
8649 {
8650 global $langs;
8651 if (empty($msg)) {
8652 $msg = $langs->trans("UnknownError");
8653 }
8654
8655 $this->error = $this->validateFieldsErrors[$fieldKey] = $msg;
8656 }
8657
8664 public function getFieldError($fieldKey)
8665 {
8666 if (!empty($this->validateFieldsErrors[$fieldKey])) {
8667 return $this->validateFieldsErrors[$fieldKey];
8668 }
8669 return '';
8670 }
8671
8680 public function validateField($fields, $fieldKey, $fieldValue)
8681 {
8682 global $langs;
8683
8684 if (!class_exists('Validate')) {
8685 require_once DOL_DOCUMENT_ROOT . '/core/class/validate.class.php';
8686 }
8687
8688 $this->clearFieldError($fieldKey);
8689
8690 if (!isset($fields[$fieldKey]) || $fields[$fieldKey] === null) {
8691 $this->setFieldError($fieldKey, $langs->trans('FieldNotFoundInObject'));
8692 return false;
8693 }
8694
8695 $val = $fields[$fieldKey];
8696
8697 $param = array();
8698 $param['options'] = array();
8699 $type = $val['type'];
8700
8701 $required = false;
8702 if (isset($val['notnull']) && $val['notnull'] === 1) {
8703 // 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
8704 $required = true;
8705 }
8706
8707 $maxSize = 0;
8708 $minSize = 0;
8709
8710 //
8711 // PREPARE Elements
8712 //
8713 $reg = array();
8714
8715 // Convert var to be able to share same code than showOutputField of extrafields
8716 if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
8717 $type = 'varchar'; // convert varchar(xx) int varchar
8718 $maxSize = $reg[1];
8719 } elseif (preg_match('/varchar/', $type)) {
8720 $type = 'varchar'; // convert varchar(xx) int varchar
8721 }
8722
8723 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8724 $type = 'select';
8725 }
8726
8727 if (!empty($val['type']) && preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8728 $type = 'link';
8729 }
8730
8731 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8732 $param['options'] = $val['arrayofkeyval'];
8733 }
8734
8735 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8736 $type = 'link';
8737 $param['options'] = array($reg[1].':'.$reg[2] => $reg[1].':'.$reg[2]);
8738 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
8739 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
8740 $type = 'sellist';
8741 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
8742 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
8743 $type = 'sellist';
8744 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
8745 $param['options'] = array($reg[1].':'.$reg[2] => 'N');
8746 $type = 'sellist';
8747 }
8748
8749 //
8750 // TEST Value
8751 //
8752
8753 // Use Validate class to allow external Modules to use data validation part instead of concentrate all test here (factoring) or just for reuse
8754 $validate = new Validate($this->db, $langs);
8755
8756
8757 // little trick : to perform tests with good performances sort tests by quick to low
8758
8759 //
8760 // COMMON TESTS
8761 //
8762
8763 // Required test and empty value
8764 if ($required && !$validate->isNotEmptyString($fieldValue)) {
8765 $this->setFieldError($fieldKey, $validate->error);
8766 return false;
8767 } elseif (!$required && !$validate->isNotEmptyString($fieldValue)) {
8768 // if no value sent and the field is not mandatory, no need to perform tests
8769 return true;
8770 }
8771
8772 // MAX Size test
8773 if (!empty($maxSize) && !$validate->isMaxLength($fieldValue, $maxSize)) {
8774 $this->setFieldError($fieldKey, $validate->error);
8775 return false;
8776 }
8777
8778 // MIN Size test
8779 if (!empty($minSize) && !$validate->isMinLength($fieldValue, $minSize)) {
8780 $this->setFieldError($fieldKey, $validate->error);
8781 return false;
8782 }
8783
8784 //
8785 // TESTS for TYPE
8786 //
8787
8788 if (in_array($type, array('date', 'datetime', 'timestamp'))) {
8789 if (!$validate->isTimestamp($fieldValue)) {
8790 $this->setFieldError($fieldKey, $validate->error);
8791 return false;
8792 } else {
8793 return true;
8794 }
8795 } elseif ($type == 'duration') {
8796 if (!$validate->isDuration($fieldValue)) {
8797 $this->setFieldError($fieldKey, $validate->error);
8798 return false;
8799 } else {
8800 return true;
8801 }
8802 } elseif (in_array($type, array('double', 'real', 'price'))) {
8803 // is numeric
8804 if (!$validate->isNumeric($fieldValue)) {
8805 $this->setFieldError($fieldKey, $validate->error);
8806 return false;
8807 } else {
8808 return true;
8809 }
8810 } elseif ($type == 'boolean') {
8811 if (!$validate->isBool($fieldValue)) {
8812 $this->setFieldError($fieldKey, $validate->error);
8813 return false;
8814 } else {
8815 return true;
8816 }
8817 } elseif ($type == 'mail') {
8818 if (!$validate->isEmail($fieldValue)) {
8819 $this->setFieldError($fieldKey, $validate->error);
8820 return false;
8821 }
8822 } elseif ($type == 'url') {
8823 if (!$validate->isUrl($fieldValue)) {
8824 $this->setFieldError($fieldKey, $validate->error);
8825 return false;
8826 } else {
8827 return true;
8828 }
8829 } elseif ($type == 'phone') {
8830 if (!$validate->isPhone($fieldValue)) {
8831 $this->setFieldError($fieldKey, $validate->error);
8832 return false;
8833 } else {
8834 return true;
8835 }
8836 } elseif ($type == 'select' || $type == 'radio') {
8837 if (!isset($param['options'][$fieldValue])) {
8838 $this->error = $langs->trans('RequireValidValue');
8839 return false;
8840 } else {
8841 return true;
8842 }
8843 } elseif ($type == 'sellist' || $type == 'chkbxlst') {
8844 $param_list = array_keys($param['options']);
8845 $InfoFieldList = explode(":", $param_list[0]);
8846 $value_arr = explode(',', $fieldValue);
8847 $value_arr = array_map(array($this->db, 'escape'), $value_arr);
8848
8849 $selectkey = "rowid";
8850 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
8851 $selectkey = $InfoFieldList[2];
8852 }
8853
8854 if (!$validate->isInDb($value_arr, $InfoFieldList[0], $selectkey)) {
8855 $this->setFieldError($fieldKey, $validate->error);
8856 return false;
8857 } else {
8858 return true;
8859 }
8860 } elseif ($type == 'link') {
8861 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
8862 $InfoFieldList = explode(":", $param_list[0]);
8863 $classname = $InfoFieldList[0];
8864 $classpath = $InfoFieldList[1];
8865 if (!$validate->isFetchable($fieldValue, $classname, $classpath)) {
8866 $lastIsFetchableError = $validate->error;
8867
8868 // from V19 of Dolibarr, In some cases link use element instead of class, example project_task
8869 if ($validate->isFetchableElement($fieldValue, $classname)) {
8870 return true;
8871 }
8872
8873 $this->setFieldError($fieldKey, $lastIsFetchableError);
8874 return false;
8875 } else {
8876 return true;
8877 }
8878 }
8879
8880 // if no test failed all is ok
8881 return true;
8882 }
8883
8897 public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = '', $display_type = 'card')
8898 {
8899 global $db, $conf, $langs, $action, $form, $hookmanager;
8900
8901 if (!is_object($form)) {
8902 $form = new Form($db);
8903 }
8904 if (!is_object($extrafields)) {
8905 dol_syslog('Bad parameter extrafields for showOptionals', LOG_ERR);
8906 return 'Bad parameter extrafields for showOptionals';
8907 }
8908 if (!is_array($extrafields->attributes[$this->table_element])) {
8909 dol_syslog("extrafields->attributes was not loaded with extrafields->fetch_name_optionals_label(table_element);", LOG_WARNING);
8910 }
8911
8912 $out = '';
8913
8914 $parameters = array('mode' => $mode, 'params' => $params, 'keysuffix' => $keysuffix, 'keyprefix' => $keyprefix, 'display_type' => $display_type);
8915 $reshook = $hookmanager->executeHooks('showOptionals', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
8916
8917 if (empty($reshook)) {
8918 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) {
8919 $out .= "\n";
8920 $out .= '<!-- commonobject:showOptionals --> ';
8921 $out .= "\n";
8922
8923 $nbofextrafieldsshown = 0;
8924 $e = 0; // var to manage the modulo (odd/even)
8925
8926 $lastseparatorkeyfound = '';
8927 $extrafields_collapse_num = '';
8928 $extrafields_collapse_num_old = '';
8929 $i = 0;
8930
8931 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $label) {
8932 $i++;
8933
8934 // Show only the key field in params @phan-suppress-next-line PhanTypeArraySuspiciousNullable
8935 if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) {
8936 continue;
8937 }
8938
8939 // Test on 'enabled' ('enabled' is different than 'list' = 'visibility')
8940 $enabled = 1;
8941 if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
8942 $enabled = (int) dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
8943 }
8944 if (empty($enabled)) {
8945 continue;
8946 }
8947
8948 $visibility = 1;
8949 if (isset($extrafields->attributes[$this->table_element]['list'][$key])) {
8950 $visibility = (int) dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
8951 }
8952
8953 $perms = 1;
8954 if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
8955 $perms = (int) dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
8956 }
8957
8958 if (($mode == 'create') && !in_array(abs($visibility), array(1, 3))) {
8959 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list
8960 } elseif (($mode == 'edit') && !in_array(abs($visibility), array(1, 3, 4))) {
8961 // 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
8962 $ef_name = 'options_' . $key;
8963 $ef_value = $this->array_options[$ef_name];
8964 $out .= '<input type="hidden" name="' . $ef_name . '" id="' . $ef_name . '" value="' . $ef_value . '" />' . "\n";
8965 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list and <> 4 = not visible at the creation
8966 } elseif ($mode == 'view' && empty($visibility)) {
8967 continue;
8968 }
8969 if (empty($perms)) {
8970 continue;
8971 }
8972
8973 // Load language if required
8974 if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
8975 $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
8976 }
8977
8978 $colspan = 0;
8979 $value = null;
8980 if (is_array($params) && count($params) > 0 && $display_type == 'card') {
8981 if (array_key_exists('cols', $params)) {
8982 $colspan = $params['cols'];
8983 } elseif (array_key_exists('colspan', $params)) { // For backward compatibility. Use cols instead now.
8984 $reg = array();
8985 if (preg_match('/colspan="(\d+)"/', $params['colspan'], $reg)) {
8986 $colspan = $reg[1];
8987 } else {
8988 $colspan = $params['colspan'];
8989 }
8990 }
8991 }
8992 $colspan = intval($colspan);
8993
8994 switch ($mode) {
8995 case "view":
8996 $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
8997 break;
8998 case "create":
8999 case "edit":
9000 // We get the value of property found with GETPOST so it takes into account:
9001 // default values overwrite, restore back to list link, ... (but not 'default value in database' of field)
9002 $check = 'alphanohtml';
9003 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text'))) {
9004 $check = 'restricthtml';
9005 }
9006 $getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, $check, 3); // GETPOST can get value from GET, POST or setup of default values overwrite.
9007 // GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
9008 if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)) {
9009 if (is_array($getposttemp)) {
9010 // $getposttemp is an array but following code expects a comma separated string
9011 $value = implode(",", $getposttemp);
9012 } else {
9013 $value = $getposttemp;
9014 }
9015 } elseif (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('int'))) {
9016 $value =( !empty($this->array_options["options_".$key]) || (isset($this->array_options["options_".$key]) && $this->array_options["options_".$key] === '0')) ? $this->array_options["options_".$key] : '';
9017 } else {
9018 $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.
9019 }
9020 //var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
9021 break;
9022 }
9023
9024 $nbofextrafieldsshown++;
9025
9026 // Output value of the current field
9027 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
9028 $extrafields_collapse_num = $key;
9029 /*
9030 $extrafield_param = $extrafields->attributes[$this->table_element]['param'][$key];
9031 if (!empty($extrafield_param) && is_array($extrafield_param)) {
9032 $extrafield_param_list = array_keys($extrafield_param['options']);
9033
9034 if (count($extrafield_param_list) > 0) {
9035 $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
9036
9037 if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
9038 //$extrafields_collapse_num = $extrafields->attributes[$this->table_element]['pos'][$key];
9039 $extrafields_collapse_num = $key;
9040 }
9041 }
9042 }
9043 */
9044
9045 // if colspan=0 or 1, the second column is not extended, so the separator must be on 2 columns
9046 $out .= $extrafields->showSeparator($key, $this, ($colspan ? $colspan + 1 : 2), $display_type, $mode);
9047
9048 $lastseparatorkeyfound = $key;
9049 } else {
9050 $collapse_group = $extrafields_collapse_num.(!empty($this->id) ? '_'.$this->id : '');
9051
9052 $class = (!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
9053 $csstyle = '';
9054 if (is_array($params) && count($params) > 0) {
9055 if (array_key_exists('class', $params)) {
9056 $class .= $params['class'].' ';
9057 }
9058 if (array_key_exists('style', $params)) {
9059 $csstyle = $params['style'];
9060 }
9061 }
9062
9063 // add html5 elements
9064 $domData = ' data-element="extrafield"';
9065 $domData .= ' data-targetelement="'.$this->element.'"';
9066 $domData .= ' data-targetid="'.$this->id.'"';
9067
9068 $html_id = (empty($this->id) ? '' : 'extrarow-'.$this->element.'_'.$key.'_'.$this->id);
9069 if ($display_type == 'card') {
9070 if (getDolGlobalString('MAIN_EXTRAFIELDS_USE_TWO_COLUMS') && ($e % 2) == 0) {
9071 $colspan = 0;
9072 }
9073
9074 if ($action == 'selectlines') {
9075 $colspan++;
9076 }
9077 }
9078
9079 // Convert date into timestamp format (value in memory must be a timestamp)
9080 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date'))) {
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 $datekey = $keyprefix.'options_'.$key.$keysuffix;
9089 $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;
9090 }
9091 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('datetime'))) {
9092 $datenotinstring = null;
9093 if (array_key_exists('options_'.$key, $this->array_options)) {
9094 $datenotinstring = $this->array_options['options_'.$key];
9095 if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
9096 $datenotinstring = $this->db->jdate($datenotinstring);
9097 }
9098 }
9099 $timekey = $keyprefix.'options_'.$key.$keysuffix;
9100 $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;
9101 }
9102 // Convert float submitted string into real php numeric (value in memory must be a php numeric)
9103 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price', 'double'))) {
9104 if (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) {
9105 $value = price2num($value);
9106 } elseif (isset($this->array_options['options_'.$key])) {
9107 $value = $this->array_options['options_'.$key];
9108 }
9109 }
9110
9111 // HTML, text, select, integer and varchar: take into account default value in database if in create mode
9112 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text', 'varchar', 'select', 'radio', 'int', 'boolean'))) {
9113 if ($action == 'create' || $mode == 'create') {
9114 $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) ? $value : $extrafields->attributes[$this->table_element]['default'][$key];
9115 }
9116 }
9117
9118 $labeltoshow = $langs->trans($label);
9119 $helptoshow = $langs->trans($extrafields->attributes[$this->table_element]['help'][$key]);
9120 if ($display_type == 'card') {
9121 $out .= '<tr '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="field_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
9122 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER') && ($action == 'view' || $action == 'valid' || $action == 'editline' || $action == 'confirm_valid' || $action == 'confirm_cancel')) {
9123 $out .= '<td></td>';
9124 }
9125 $out .= '<td class="'.(empty($params['tdclass']) ? 'titlefieldmax45' : $params['tdclass']).' wordbreak';
9126 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'text') {
9127 $out .= ' tdtop';
9128 }
9129 } elseif ($display_type == 'line') {
9130 $out .= '<div '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="fieldline_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
9131 $out .= '<div style="display: inline-block; padding-right:4px" class="wordbreak';
9132 }
9133 //$out .= "titlefield";
9134 //if (GETPOST('action', 'restricthtml') == 'create') $out.='create';
9135 // BUG #11554 : For public page, use red dot for required fields, instead of bold label
9136 $tpl_context = isset($params["tpl_context"]) ? $params["tpl_context"] : "none";
9137 if ($tpl_context != "public") { // Public page : red dot instead of fieldrequired characters
9138 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
9139 $out .= ' fieldrequired';
9140 }
9141 }
9142 $out .= '">';
9143 if ($tpl_context == "public") { // Public page : red dot instead of fieldrequired characters
9144 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
9145 $out .= $form->textwithpicto($labeltoshow, $helptoshow);
9146 } else {
9147 $out .= $labeltoshow;
9148 }
9149 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
9150 $out .= '&nbsp;<span style="color: red">*</span>';
9151 }
9152 } else {
9153 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
9154 $out .= $form->textwithpicto($labeltoshow, $helptoshow);
9155 } else {
9156 $out .= $labeltoshow;
9157 }
9158 }
9159
9160 $out .= ($display_type == 'card' ? '</td>' : '</div>');
9161
9162 // Second column
9163 $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
9164 if ($display_type == 'card') {
9165 // 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
9166 $out .= '<td '.($html_id ? 'id="'.$html_id.'" ' : '').' class="valuefieldcreate '.$this->element.'_extras_'.$key;
9167 $out .= '" '.($colspan ? ' colspan="'.$colspan.'"' : '');
9168 $out .= '>';
9169 } elseif ($display_type == 'line') {
9170 $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].'">';
9171 }
9172
9173 switch ($mode) {
9174 case "view":
9175 $out .= $extrafields->showOutputField($key, $value, '', $this->table_element);
9176 break;
9177 case "create":
9178 $listoftypestoshowpicto = explode(',', getDolGlobalString('MAIN_TYPES_TO_SHOW_PICTO', 'email,phone,ip,password'));
9179 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], $listoftypestoshowpicto)) {
9180 $out .= getPictoForType($extrafields->attributes[$this->table_element]['type'][$key], ($extrafields->attributes[$this->table_element]['type'][$key] == 'text' ? 'tdtop' : ''));
9181 }
9182 //$out .= '<!-- type = '.$extrafields->attributes[$this->table_element]['type'][$key].' -->';
9183 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
9184 break;
9185 case "edit":
9186 $listoftypestoshowpicto = explode(',', getDolGlobalString('MAIN_TYPES_TO_SHOW_PICTO', 'email,phone,ip,password'));
9187 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], $listoftypestoshowpicto)) {
9188 $out .= getPictoForType($extrafields->attributes[$this->table_element]['type'][$key], ($extrafields->attributes[$this->table_element]['type'][$key] == 'text' ? 'tdtop' : ''));
9189 }
9190 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
9191 break;
9192 }
9193
9194 $out .= ($display_type == 'card' ? '</td>' : '</div>');
9195 $out .= ($display_type == 'card' ? '</tr>'."\n" : '</div>');
9196 $e++;
9197 }
9198 }
9199 $out .= "\n";
9200 // Add code to manage list depending on others
9201 if (!empty($conf->use_javascript_ajax)) {
9202 $out .= $this->getJSListDependancies();
9203 }
9204
9205 $out .= '<!-- commonobject:showOptionals end --> '."\n";
9206
9207 if (empty($nbofextrafieldsshown)) {
9208 $out = '';
9209 }
9210 }
9211 }
9212
9213 $out .= $hookmanager->resPrint;
9214
9215 return $out;
9216 }
9217
9222 public function getJSListDependancies($type = '_extra')
9223 {
9224 $out = '
9225 <script nonce="'.getNonce().'">
9226 jQuery(document).ready(function() {
9227 function showOptions'.$type.'(child_list, parent_list, orig_select)
9228 {
9229 var val = $("select[name=\""+parent_list+"\"]").val();
9230 var parentVal = parent_list + ":" + val;
9231 if(typeof val == "string"){
9232 if(val != "") {
9233 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
9234 $("select[name=\""+child_list+"\"] option[parent]").remove();
9235 $("select[name=\""+child_list+"\"]").append(options);
9236 } else {
9237 var options = orig_select.find("option[parent]").clone();
9238 $("select[name=\""+child_list+"\"] option[parent]").remove();
9239 $("select[name=\""+child_list+"\"]").append(options);
9240 }
9241 } else if(val > 0) {
9242 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
9243 $("select[name=\""+child_list+"\"] option[parent]").remove();
9244 $("select[name=\""+child_list+"\"]").append(options);
9245 } else {
9246 var options = orig_select.find("option[parent]").clone();
9247 $("select[name=\""+child_list+"\"] option[parent]").remove();
9248 $("select[name=\""+child_list+"\"]").append(options);
9249 }
9250 }
9251 function setListDependencies'.$type.'() {
9252 jQuery("select option[parent]").parent().each(function() {
9253 var orig_select = {};
9254 var child_list = $(this).attr("name");
9255 orig_select[child_list] = $(this).clone();
9256 var parent = $(this).find("option[parent]:first").attr("parent");
9257 var infos = parent.split(":");
9258 var parent_list = infos[0];
9259
9260 //Hide daughters lists
9261 if ($("#"+child_list).val() == 0 && $("#"+parent_list).val() == 0){
9262 $("#"+child_list).hide();
9263 //Show mother lists
9264 } else if ($("#"+parent_list).val() != 0){
9265 $("#"+parent_list).show();
9266 }
9267 //Show the child list if the parent list value is selected
9268 $("select[name=\""+parent_list+"\"]").click(function() {
9269 if ($(this).val() != 0){
9270 $("#"+child_list).show()
9271 }
9272 });
9273
9274 //When we change parent list
9275 $("select[name=\""+parent_list+"\"]").change(function() {
9276 showOptions'.$type.'(child_list, parent_list, orig_select[child_list]);
9277 //Select the value 0 on child list after a change on the parent list
9278 $("#"+child_list).val(0).trigger("change");
9279 //Hide child lists if the parent value is set to 0
9280 if ($(this).val() == 0){
9281 $("#"+child_list).hide();
9282 }
9283 });
9284 });
9285 }
9286
9287 setListDependencies'.$type.'();
9288 });
9289 </script>'."\n";
9290 return $out;
9291 }
9292
9298 public function getRights()
9299 {
9300 global $user;
9301
9302 $module = empty($this->module) ? '' : $this->module;
9303 $element = $this->element;
9304
9305 if ($element == 'facturerec') {
9306 $element = 'facture';
9307 } elseif ($element == 'invoice_supplier_rec') {
9308 return !$user->hasRight('fournisseur', 'facture') ? null : $user->hasRight('fournisseur', 'facture');
9309 } elseif ($module && $user->hasRight($module, $element)) {
9310 // for modules built with ModuleBuilder
9311 return $user->hasRight($module, $element);
9312 }
9313
9314 return $user->rights->$element;
9315 }
9316
9329 public static function commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
9330 {
9331 foreach ($tables as $table) {
9332 $sql = 'UPDATE '.$dbs->prefix().$table.' SET fk_soc = '.((int) $dest_id).' WHERE fk_soc = '.((int) $origin_id);
9333
9334 if (!$dbs->query($sql)) {
9335 if ($ignoreerrors) {
9336 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.
9337 }
9338 //$this->errors = $db->lasterror();
9339 return false;
9340 }
9341 }
9342
9343 return true;
9344 }
9345
9358 public static function commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
9359 {
9360 foreach ($tables as $table) {
9361 $sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_product = '.((int) $dest_id).' WHERE fk_product = '.((int) $origin_id);
9362
9363 if (!$dbs->query($sql)) {
9364 if ($ignoreerrors) {
9365 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.
9366 }
9367 //$this->errors = $db->lasterror();
9368 return false;
9369 }
9370 }
9371
9372 return true;
9373 }
9374
9387 public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
9388 {
9389 global $conf;
9390
9391 $buyPrice = 0;
9392
9393 if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && getDolGlobalInt('ForceBuyingPriceIfNull') > 0)) {
9394 // When ForceBuyingPriceIfNull is set
9395 $buyPrice = $unitPrice * (1 - $discountPercent / 100);
9396 } else {
9397 // Get cost price for margin calculation
9398 if (!empty($fk_product) && $fk_product > 0) {
9399 $result = 0;
9400 if (getDolGlobalString('MARGIN_TYPE') == 'costprice') {
9401 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
9402 $product = new Product($this->db);
9403 $result = $product->fetch($fk_product);
9404 if ($result <= 0) {
9405 $this->errors[] = 'ErrorProductIdDoesNotExists';
9406 return -1;
9407 }
9408 if ($product->cost_price > 0) {
9409 $buyPrice = $product->cost_price;
9410 } elseif ($product->pmp > 0) {
9411 $buyPrice = $product->pmp;
9412 }
9413 } elseif (getDolGlobalString('MARGIN_TYPE') == 'pmp') {
9414 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
9415 $product = new Product($this->db);
9416 $result = $product->fetch($fk_product);
9417 if ($result <= 0) {
9418 $this->errors[] = 'ErrorProductIdDoesNotExists';
9419 return -1;
9420 }
9421 if ($product->pmp > 0) {
9422 $buyPrice = $product->pmp;
9423 }
9424 }
9425
9426 if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1', 'pmp', 'costprice'))) {
9427 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
9428 $productFournisseur = new ProductFournisseur($this->db);
9429 if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0) {
9430 $buyPrice = $productFournisseur->fourn_unitprice;
9431 } elseif ($result < 0) {
9432 $this->errors[] = $productFournisseur->error;
9433 return -2;
9434 }
9435 }
9436 }
9437 }
9438 return $buyPrice;
9439 }
9440
9448 public function getDataToShowPhoto($modulepart, $imagesize)
9449 {
9450 // See getDataToShowPhoto() implemented by Product for example.
9451 return array('dir' => '', 'file' => '', 'originalfile' => '', 'altfile' => '', 'email' => '', 'capture' => '');
9452 }
9453
9454
9455 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
9475 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')
9476 {
9477 // phpcs:enable
9478 global $user, $langs;
9479
9480 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
9481 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
9482
9483 $sortfield = 'position_name';
9484 $sortorder = 'asc';
9485
9486 $dir = $sdir.'/';
9487 $pdir = '/';
9488
9489 $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
9490 $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
9491
9492 // For backward compatibility
9493 if ($modulepart == 'product') {
9494 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
9495 $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9496 $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9497 }
9498 }
9499 if ($modulepart == 'category') {
9500 $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9501 $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9502 }
9503
9504 // Defined relative dir to DOL_DATA_ROOT
9505 $relativedir = '';
9506 if ($dir) {
9507 $relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
9508 $relativedir = preg_replace('/^[\\/]/', '', $relativedir);
9509 $relativedir = preg_replace('/[\\/]$/', '', $relativedir);
9510 }
9511
9512 $dirthumb = $dir.'thumbs/';
9513 $pdirthumb = $pdir.'thumbs/';
9514
9515 $return = '<!-- Photo -->'."\n";
9516 $nbphoto = 0;
9517
9518 $filearray = dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ? SORT_DESC : SORT_ASC), 1);
9519
9520 /*if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) // For backward compatibility, we scan also old dirs
9521 {
9522 $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
9523 $filearray=array_merge($filearray, $filearrayold);
9524 }*/
9525
9526 completeFileArrayWithDatabaseInfo($filearray, $relativedir);
9527
9528 if (count($filearray)) {
9529 if ($sortfield && $sortorder) {
9530 $filearray = dol_sort_array($filearray, $sortfield, $sortorder);
9531 }
9532
9533 foreach ($filearray as $key => $val) {
9534 $photo = '';
9535 $file = $val['name'];
9536
9537 //if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
9538 if (image_format_supported($file) >= 0) {
9539 $nbphoto++;
9540 $photo = $file;
9541 $viewfilename = $file;
9542
9543 if ($size == 1 || $size == 'small') { // Format vignette
9544 // Find name of thumb file
9545 $photo_vignette = basename(getImageFileNameForSize($dir.$file, '_small'));
9546 if (!dol_is_file($dirthumb.$photo_vignette)) {
9547 // The thumb does not exists, so we will use the original file
9548 $dirthumb = $dir;
9549 $pdirthumb = $pdir;
9550 $photo_vignette = basename($file);
9551 }
9552
9553 // Get filesize of original file
9554 $imgarray = dol_getImageSize($dir.$photo);
9555
9556 if ($nbbyrow > 0) {
9557 if ($nbphoto == 1) {
9558 $return .= '<table class="valigntop center centpercent" style="border: 0; padding: 2px; border-spacing: 2px; border-collapse: separate;">';
9559 }
9560
9561 if ($nbphoto % $nbbyrow == 1) {
9562 $return .= '<tr class="center valignmiddle" style="border: 1px">';
9563 }
9564 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%" class="photo">'."\n";
9565 } elseif ($nbbyrow < 0) {
9566 $return .= '<div class="inline-block">'."\n";
9567 }
9568
9569 $relativefile = preg_replace('/^\//', '', $pdir.$photo);
9570 if (empty($nolink)) {
9571 $urladvanced = getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
9572 if ($urladvanced) {
9573 $return .= '<a href="'.$urladvanced.'">';
9574 } else {
9575 $return .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank" rel="noopener noreferrer">';
9576 }
9577 }
9578
9579 // Show image (width height=$maxHeight)
9580 // Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
9581 $alt = $langs->transnoentitiesnoconv('File').': '.$relativefile;
9582 $alt .= ' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
9583 if ($overwritetitle) {
9584 if (is_numeric($overwritetitle)) {
9585 $alt = '';
9586 } else {
9587 $alt = $overwritetitle;
9588 }
9589 }
9590 if (empty($cache) && !empty($val['label'])) {
9591 // label is md5 of file
9592 // use it in url to say we want to cache this version of the file
9593 $cache = $val['label'];
9594 }
9595 if ($usesharelink) {
9596 if (array_key_exists('share', $val) && $val['share']) {
9597 if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
9598 $return .= '<!-- Show original file (thumb not yet available with shared links) -->';
9599 $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).'">';
9600 } else {
9601 $return .= '<!-- Show original file -->';
9602 $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).'">';
9603 }
9604 } else {
9605 $return .= '<!-- Show nophoto file (because file is not shared) -->';
9606 $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
9607 }
9608 } else {
9609 if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
9610 $return .= '<!-- Show thumb -->';
9611 $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).'">';
9612 } else {
9613 $return .= '<!-- Show original file -->';
9614 $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).'">';
9615 }
9616 }
9617
9618 if (empty($nolink)) {
9619 $return .= '</a>';
9620 }
9621
9622 if ($showfilename) {
9623 $return .= '<br>'.$viewfilename;
9624 }
9625 if ($showaction) {
9626 $return .= '<br>';
9627 // If $photo_vignette set, we add a link to generate thumbs if file is an image and width or height higher than limits
9628 if ($photo_vignette && (image_format_supported($photo) > 0) && ((isset($imgarray['width']) && $imgarray['width'] > $maxWidth) || (isset($imgarray['width']) && $imgarray['width'] > $maxHeight))) {
9629 $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>';
9630 }
9631 // Special case for product
9632 if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
9633 // Link to resize
9634 $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; ';
9635
9636 // Link to delete
9637 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
9638 $return .= img_delete().'</a>';
9639 }
9640 }
9641 $return .= "\n";
9642
9643 if ($nbbyrow > 0) {
9644 $return .= '</td>';
9645 if (($nbphoto % $nbbyrow) == 0) {
9646 $return .= '</tr>';
9647 }
9648 } elseif ($nbbyrow < 0) {
9649 $return .= '</div>'."\n";
9650 }
9651 }
9652
9653 if (empty($size)) { // Format origine
9654 $return .= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
9655
9656 if ($showfilename) {
9657 $return .= '<br>'.$viewfilename;
9658 }
9659 if ($showaction) {
9660 // Special case for product
9661 if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
9662 // Link to resize
9663 $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; ';
9664
9665 // Link to delete
9666 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
9667 $return .= img_delete().'</a>';
9668 }
9669 }
9670 }
9671
9672 // On continue ou on arrete de boucler ?
9673 if ($nbmax && $nbphoto >= $nbmax) {
9674 break;
9675 }
9676 }
9677 }
9678
9679 if ($size == 1 || $size == 'small') {
9680 if ($nbbyrow > 0) {
9681 // Ferme tableau
9682 while ($nbphoto % $nbbyrow) {
9683 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%">&nbsp;</td>';
9684 $nbphoto++;
9685 }
9686
9687 if ($nbphoto) {
9688 $return .= '</table>';
9689 }
9690 }
9691 }
9692 }
9693
9694 $this->nbphoto = $nbphoto;
9695
9696 return $return;
9697 }
9698
9699
9706 protected function isArray($info)
9707 {
9708 if (is_array($info)) {
9709 if (isset($info['type']) && $info['type'] == 'array') {
9710 return true;
9711 } else {
9712 return false;
9713 }
9714 }
9715 return false;
9716 }
9717
9724 public function isDate($info)
9725 {
9726 if (isset($info['type']) && ($info['type'] == 'date' || $info['type'] == 'datetime' || $info['type'] == 'timestamp')) {
9727 return true;
9728 }
9729 return false;
9730 }
9731
9738 public function isDuration($info)
9739 {
9740 if (is_array($info)) {
9741 if (isset($info['type']) && ($info['type'] == 'duration')) {
9742 return true;
9743 } else {
9744 return false;
9745 }
9746 } else {
9747 return false;
9748 }
9749 }
9750
9757 public function isInt($info)
9758 {
9759 if (is_array($info)) {
9760 if (isset($info['type']) && (preg_match('/(^int|int$)/i', $info['type']))) {
9761 return true;
9762 } else {
9763 return false;
9764 }
9765 } else {
9766 return false;
9767 }
9768 }
9769
9776 public function isFloat($info)
9777 {
9778 if (is_array($info)) {
9779 if (isset($info['type']) && (preg_match('/^(double|real|price)/i', $info['type']))) {
9780 return true;
9781 } else {
9782 return false;
9783 }
9784 }
9785 return false;
9786 }
9787
9794 public function isText($info)
9795 {
9796 if (is_array($info)) {
9797 if (isset($info['type']) && $info['type'] == 'text') {
9798 return true;
9799 } else {
9800 return false;
9801 }
9802 }
9803 return false;
9804 }
9805
9812 protected function canBeNull($info)
9813 {
9814 if (is_array($info)) {
9815 if (array_key_exists('notnull', $info) && $info['notnull'] != '1') {
9816 return true;
9817 } else {
9818 return false;
9819 }
9820 }
9821 return true;
9822 }
9823
9830 protected function isForcedToNullIfZero($info)
9831 {
9832 if (is_array($info)) {
9833 if (array_key_exists('notnull', $info) && $info['notnull'] == '-1') {
9834 return true;
9835 } else {
9836 return false;
9837 }
9838 }
9839 return false;
9840 }
9841
9848 protected function isIndex($info)
9849 {
9850 if (is_array($info)) {
9851 if (array_key_exists('index', $info) && $info['index'] == true) {
9852 return true;
9853 } else {
9854 return false;
9855 }
9856 }
9857 return false;
9858 }
9859
9860
9869 protected function setSaveQuery()
9870 {
9871 global $conf;
9872
9873 $queryarray = array();
9874 foreach ($this->fields as $field => $info) { // Loop on definition of fields
9875 // Depending on field type ('datetime', ...)
9876 if ($this->isDate($info)) {
9877 if (empty($this->{$field})) {
9878 $queryarray[$field] = null;
9879 } else {
9880 $queryarray[$field] = $this->db->idate($this->{$field});
9881 }
9882 } elseif ($this->isDuration($info)) {
9883 // $this->{$field} may be null, '', 0, '0', 123, '123'
9884 if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
9885 if (!isset($this->{$field})) {
9886 if (!empty($info['default'])) {
9887 $queryarray[$field] = $info['default'];
9888 } else {
9889 $queryarray[$field] = 0;
9890 }
9891 } else {
9892 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9893 }
9894 } else {
9895 $queryarray[$field] = null;
9896 }
9897 } elseif ($this->isInt($info) || $this->isFloat($info)) {
9898 if ($field == 'entity' && is_null($this->{$field})) {
9899 $queryarray[$field] = ((int) $conf->entity);
9900 } else {
9901 // $this->{$field} may be null, '', 0, '0', 123, '123'
9902 if ((isset($this->{$field}) && ((string) $this->{$field}) != '') || !empty($info['notnull'])) {
9903 if (!isset($this->{$field})) {
9904 $queryarray[$field] = 0;
9905 } elseif ($this->isInt($info)) {
9906 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9907 } elseif ($this->isFloat($info)) {
9908 $queryarray[$field] = (float) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9909 }
9910 } else {
9911 $queryarray[$field] = null;
9912 }
9913 }
9914 } else {
9915 // Note: If $this->{$field} is not defined, it means there is a bug into definition of ->fields or a missing declaration of property
9916 // We should keep the warning generated by this because it is a bug somewhere else in code, not here.
9917 $queryarray[$field] = $this->{$field};
9918 }
9919
9920 if (array_key_exists('type', $info) && $info['type'] == 'timestamp' && empty($queryarray[$field])) {
9921 unset($queryarray[$field]);
9922 }
9923 if (!empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) {
9924 $queryarray[$field] = null; // May force 0 to null
9925 }
9926 }
9927
9928 return $queryarray;
9929 }
9930
9937 public function setVarsFromFetchObj(&$obj)
9938 {
9939 global $db;
9940
9941 foreach ($this->fields as $field => $info) {
9942 if ($this->isDate($info)) {
9943 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') {
9944 $this->$field = '';
9945 } else {
9946 $this->$field = $db->jdate($obj->$field);
9947 }
9948 } elseif ($this->isInt($info)) {
9949 if ($field == 'rowid') {
9950 $this->id = (int) $obj->$field;
9951 } else {
9952 if ($this->isForcedToNullIfZero($info)) {
9953 if (empty($obj->$field)) {
9954 $this->$field = null;
9955 } else {
9956 $this->$field = (int) $obj->$field;
9957 }
9958 } else {
9959 if (isset($obj->$field) && (!is_null($obj->$field) || (array_key_exists('notnull', $info) && $info['notnull'] == 1))) {
9960 $this->$field = (int) $obj->$field;
9961 } else {
9962 $this->$field = null;
9963 }
9964 }
9965 }
9966 } elseif ($this->isFloat($info)) {
9967 if ($this->isForcedToNullIfZero($info)) {
9968 if (empty($obj->$field)) {
9969 $this->$field = null;
9970 } else {
9971 $this->$field = (float) $obj->$field;
9972 }
9973 } else {
9974 if (isset($obj->$field) && (!is_null($obj->$field) || (array_key_exists('notnull', $info) && $info['notnull'] == 1))) {
9975 $this->$field = (float) $obj->$field;
9976 } else {
9977 $this->$field = null;
9978 }
9979 }
9980 } else {
9981 $this->$field = isset($obj->$field) ? $obj->$field : null;
9982 }
9983 }
9984
9985 // If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
9986 if (!isset($this->fields['ref']) && isset($this->id)) {
9987 $this->ref = (string) $this->id;
9988 }
9989 }
9990
9995 public function emtpyObjectVars()
9996 {
9997 foreach ($this->fields as $field => $arr) {
9998 $this->$field = null;
9999 }
10000 }
10001
10009 public function getFieldList($alias = '', $excludefields = array())
10010 {
10011 $keys = array_keys($this->fields);
10012 if (!empty($alias)) {
10013 $keys_with_alias = array();
10014 foreach ($keys as $fieldname) {
10015 if (!empty($excludefields)) {
10016 if (in_array($fieldname, $excludefields)) { // The field is excluded and must not be in output
10017 continue;
10018 }
10019 }
10020 $keys_with_alias[] = $alias . '.' . $fieldname;
10021 }
10022 return implode(',', $keys_with_alias);
10023 } else {
10024 return implode(',', $keys);
10025 }
10026 }
10027
10035 protected function quote($value, $fieldsentry)
10036 {
10037 if (is_null($value)) {
10038 return 'NULL';
10039 } elseif (preg_match('/^(int|double|real|price)/i', $fieldsentry['type'])) {
10040 return price2num((string) $value);
10041 } elseif (preg_match('/int$/i', $fieldsentry['type'])) {
10042 return (int) $value;
10043 } elseif ($fieldsentry['type'] == 'boolean') {
10044 if ($value) {
10045 return 'true';
10046 } else {
10047 return 'false';
10048 }
10049 } else {
10050 return "'".$this->db->escape($value)."'";
10051 }
10052 }
10053
10054
10062 public function createCommon(User $user, $notrigger = 0)
10063 {
10064 global $langs;
10065
10066 dol_syslog(get_class($this)."::createCommon create", LOG_DEBUG);
10067
10068 $error = 0;
10069
10070 $now = dol_now();
10071
10072 $fieldvalues = $this->setSaveQuery();
10073
10074 // Note: Here, $fieldvalues contains same keys (or less) that are inside ->fields
10075
10076 if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) {
10077 $fieldvalues['date_creation'] = $this->db->idate($now);
10078 }
10079 // For backward compatibility, if a property ->fk_user_creat exists and not filled.
10080 if (array_key_exists('fk_user_creat', $fieldvalues) && !($fieldvalues['fk_user_creat'] > 0)) {
10081 $fieldvalues['fk_user_creat'] = $user->id;
10082 $this->fk_user_creat = $user->id;
10083 }
10084 if (array_key_exists('user_creation_id', $fieldvalues) && !($fieldvalues['user_creation_id'] > 0)) {
10085 $fieldvalues['user_creation_id'] = $user->id;
10086 $this->user_creation_id = $user->id;
10087 }
10088 if (array_key_exists('pass_crypted', $fieldvalues) && property_exists($this, 'pass')) {
10089 // @phan-suppress-next-line PhanUndeclaredProperty
10090 $fieldvalues['pass_crypted'] = dol_hash($this->pass);
10091 }
10092 if (array_key_exists('ref', $fieldvalues)) {
10093 $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
10094 }
10095
10096 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
10097
10098 $keys = array();
10099 $values = array(); // Array to store string forged for SQL syntax
10100 foreach ($fieldvalues as $k => $v) {
10101 $keys[$k] = $k;
10102 $value = $this->fields[$k];
10103 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
10104 $values[$k] = $this->quote($v, $value); // May return string 'NULL' if $value is null
10105 }
10106
10107 // Clean and check mandatory
10108 foreach ($keys as $key) {
10109 if (!isset($this->fields[$key])) {
10110 continue;
10111 }
10112 $key_fields = $this->fields[$key];
10113
10114 // If field is an implicit foreign key field (so type = 'integer:...')
10115 if (preg_match('/^integer:/i', $key_fields['type']) && $values[$key] == '-1') {
10116 $values[$key] = '';
10117 }
10118 if (!empty($key_fields['foreignkey']) && $values[$key] == '-1') {
10119 $values[$key] = '';
10120 }
10121
10122 if (isset($key_fields['notnull']) && $key_fields['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && (!isset($key_fields['default']) || is_null($key_fields['default']))) {
10123 $error++;
10124 $langs->load("errors");
10125 dol_syslog("Mandatory field '".$key."' is empty and required into ->fields definition of class");
10126 $this->errors[] = $langs->trans("ErrorFieldRequired", isset($key_fields['label']) ? $key_fields['label'] : $key);
10127 }
10128
10129 // If value is null and there is a default value for field @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
10130 if (isset($key_fields['notnull']) && $key_fields['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && !is_null($key_fields['default'])) {
10131 $values[$key] = $this->quote($key_fields['default'], $key_fields);
10132 }
10133
10134 // If field is an implicit foreign key field (so type = 'integer:...')
10135 if (isset($key_fields['type']) && preg_match('/^integer:/i', $key_fields['type']) && empty($values[$key])) {
10136 if (isset($key_fields['default'])) {
10137 $values[$key] = ((int) $key_fields['default']);
10138 } else {
10139 $values[$key] = 'null';
10140 }
10141 }
10142 if (!empty($key_fields['foreignkey']) && empty($values[$key])) {
10143 $values[$key] = 'null';
10144 }
10145 }
10146
10147 if ($error) {
10148 return -1;
10149 }
10150
10151 $this->db->begin();
10152
10153 if (!$error) {
10154 $sql = "INSERT INTO ".$this->db->prefix().$this->table_element;
10155 $sql .= " (".implode(", ", $keys).')';
10156 $sql .= " VALUES (".implode(", ", $values).")"; // $values can contains 'abc' or 123
10157
10158 $res = $this->db->query($sql);
10159 if (!$res) {
10160 $error++;
10161 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
10162 $this->errors[] = "ErrorRefAlreadyExists";
10163 } else {
10164 $this->errors[] = $this->db->lasterror();
10165 }
10166 }
10167 }
10168
10169 if (!$error) {
10170 $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
10171 }
10172
10173 // If we have a field ref with a default value of (PROV)
10174 if (!$error) {
10175 // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset
10176 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)') {
10177 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ref = '(PROV".((int) $this->id).")' WHERE (ref = '(PROV)' OR ref = '') AND rowid = ".((int) $this->id);
10178 $resqlupdate = $this->db->query($sql);
10179
10180 if ($resqlupdate === false) {
10181 $error++;
10182 $this->errors[] = $this->db->lasterror();
10183 } else {
10184 $this->ref = '(PROV'.$this->id.')';
10185 }
10186 }
10187 }
10188
10189 // Create extrafields
10190 if (!$error) {
10191 $result = $this->insertExtraFields();
10192 if ($result < 0) {
10193 $error++;
10194 }
10195 }
10196
10197 // Create lines
10198 if (!empty($this->table_element_line) && !empty($this->fk_element) && !empty($this->lines)) {
10199 foreach ($this->lines as $line) {
10200 $keyforparent = $this->fk_element;
10201 $line->$keyforparent = $this->id;
10202
10203 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
10204 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
10205 if (!is_object($line)) {
10206 $line = (object) $line;
10207 }
10208
10209 $result = 0;
10210 if (method_exists($line, 'insert')) {
10211 $result = $line->insert($user, 1);
10212 } elseif (method_exists($line, 'create')) {
10213 $result = $line->create($user, 1);
10214 }
10215 if ($result < 0) {
10216 $this->error = $line->error;
10217 $this->db->rollback();
10218 return -1;
10219 }
10220 }
10221 }
10222
10223 // Triggers
10224 if (!$error && !$notrigger) {
10225 // Call triggers
10226 $result = $this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user);
10227 if ($result < 0) {
10228 $error++;
10229 }
10230 // End call triggers
10231 }
10232
10233 // Commit or rollback
10234 if ($error) {
10235 $this->db->rollback();
10236 return -1;
10237 } else {
10238 $this->db->commit();
10239 return $this->id;
10240 }
10241 }
10242
10243
10253 public function fetchCommon($id, $ref = null, $morewhere = '', $noextrafields = 0)
10254 {
10255 if (empty($id) && empty($ref) && empty($morewhere)) {
10256 return -1;
10257 }
10258
10259 $fieldlist = $this->getFieldList('t');
10260 if (empty($fieldlist)) {
10261 return 0;
10262 }
10263
10264 $sql = "SELECT ".$fieldlist;
10265 $sql .= " FROM ".$this->db->prefix().$this->table_element.' as t';
10266
10267 if (!empty($id)) {
10268 $sql .= ' WHERE t.rowid = '.((int) $id);
10269 } elseif (!empty($ref)) {
10270 $sql .= " WHERE t.ref = '".$this->db->escape($ref)."'";
10271 } else {
10272 $sql .= ' WHERE 1 = 1'; // usage with empty id and empty ref is very rare
10273 }
10274 if (empty($id) && isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
10275 $sql .= ' AND t.entity IN ('.getEntity($this->element).')';
10276 }
10277 if ($morewhere) {
10278 $sql .= $morewhere;
10279 }
10280 $sql .= ' LIMIT 1'; // This is a fetch, to be certain to get only one record
10281
10282 $res = $this->db->query($sql);
10283 if ($res) {
10284 $obj = $this->db->fetch_object($res);
10285 if ($obj) {
10286 $this->setVarsFromFetchObj($obj);
10287
10288 // Retrieve all extrafield
10289 // fetch optionals attributes and labels
10290 if (empty($noextrafields)) {
10291 $result = $this->fetch_optionals();
10292 if ($result < 0) {
10293 $this->error = $this->db->lasterror();
10294 $this->errors[] = $this->error;
10295 return -4;
10296 }
10297 }
10298
10299 return $this->id;
10300 } else {
10301 return 0;
10302 }
10303 } else {
10304 $this->error = $this->db->lasterror();
10305 $this->errors[] = $this->error;
10306 return -1;
10307 }
10308 }
10309
10317 public function fetchLinesCommon($morewhere = '', $noextrafields = 0)
10318 {
10319 $objectlineclassname = get_class($this).'Line';
10320 if (!class_exists($objectlineclassname)) {
10321 $this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
10322 return -1;
10323 }
10324
10325 $objectline = new $objectlineclassname($this->db);
10326 '@phan-var-force CommonObjectLine $objectline';
10327
10328 $sql = "SELECT ".$objectline->getFieldList('l');
10329 $sql .= " FROM ".$this->db->prefix().$objectline->table_element." as l";
10330 $sql .= " WHERE l.fk_".$this->db->escape($this->element)." = ".((int) $this->id);
10331 if ($morewhere) {
10332 $sql .= $morewhere;
10333 }
10334 if (isset($objectline->fields['position'])) {
10335 $sql .= $this->db->order('position', 'ASC');
10336 }
10337
10338 $resql = $this->db->query($sql);
10339 if ($resql) {
10340 $num_rows = $this->db->num_rows($resql);
10341 $i = 0;
10342 $this->lines = array();
10343 while ($i < $num_rows) {
10344 $obj = $this->db->fetch_object($resql);
10345 if ($obj) {
10346 $newline = new $objectlineclassname($this->db);
10347 '@phan-var-force CommonObjectLine $newline';
10348 $newline->setVarsFromFetchObj($obj);
10349
10350 // Note: extrafields load of line not yet supported
10351 /*
10352 if (empty($noextrafields)) {
10353 // Load extrafields of line
10354 }*/
10355
10356 $this->lines[] = $newline;
10357 }
10358 $i++;
10359 }
10360
10361 return 1;
10362 } else {
10363 $this->error = $this->db->lasterror();
10364 $this->errors[] = $this->error;
10365 return -1;
10366 }
10367 }
10368
10376 public function updateCommon(User $user, $notrigger = 0)
10377 {
10378 dol_syslog(get_class($this)."::updateCommon update", LOG_DEBUG);
10379
10380 $error = 0;
10381
10382 $now = dol_now();
10383
10384 // $this->oldcopy should have been set by the caller of update
10385 //if (empty($this->oldcopy)) {
10386 // dol_syslog("this->oldcopy should have been set by the caller of update (here properties were already modified)", LOG_WARNING);
10387 // $this->oldcopy = dol_clone($this, 2);
10388 //}
10389
10390 $fieldvalues = $this->setSaveQuery();
10391
10392 // Note: Here, $fieldvalues contains same keys (or less) that are inside ->fields
10393
10394 if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) {
10395 $fieldvalues['date_modification'] = $this->db->idate($now);
10396 }
10397 if (array_key_exists('fk_user_modif', $fieldvalues) && !($fieldvalues['fk_user_modif'] > 0)) {
10398 $fieldvalues['fk_user_modif'] = $user->id;
10399 }
10400 if (array_key_exists('user_modification_id', $fieldvalues) && !($fieldvalues['user_modification_id'] > 0)) {
10401 $fieldvalues['user_modification_id'] = $user->id;
10402 }
10403 if (array_key_exists('ref', $fieldvalues)) {
10404 $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
10405 }
10406
10407 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
10408
10409 // Add quotes and escape on fields with type string
10410 $keys = array();
10411 $values = array();
10412 $tmp = array();
10413 foreach ($fieldvalues as $k => $v) {
10414 $keys[$k] = $k;
10415 $value = $this->fields[$k];
10416 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
10417 $values[$k] = $this->quote($v, $value);
10418 // @phan-suppress-next-line PhanPluginSuspiciousParamPosition
10419 $tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
10420 }
10421
10422 // Clean and check mandatory fields
10423 foreach ($keys as $key) {
10424 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
10425 $values[$key] = ''; // This is an implicit foreign key field
10426 }
10427 if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
10428 $values[$key] = ''; // This is an explicit foreign key field
10429 }
10430
10431 //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
10432 /*
10433 if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
10434 {
10435 $error++;
10436 $this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
10437 }*/
10438 }
10439
10440 $sql = 'UPDATE '.$this->db->prefix().$this->table_element.' SET '.implode(', ', $tmp).' WHERE rowid='.((int) $this->id);
10441
10442 $this->db->begin();
10443
10444 if (!$error) {
10445 $res = $this->db->query($sql);
10446 if (!$res) {
10447 $error++;
10448 $this->errors[] = $this->db->lasterror();
10449 }
10450 }
10451
10452 // Update extrafield
10453 if (!$error) {
10454 $result = $this->insertExtraFields(); // This delete and reinsert extrafields
10455 if ($result < 0) {
10456 $error++;
10457 }
10458 }
10459
10460 // Triggers
10461 if (!$error && !$notrigger) {
10462 // Call triggers
10463 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
10464 if ($result < 0) {
10465 $error++;
10466 } //Do also here what you must do to rollback action if trigger fail
10467 // End call triggers
10468 }
10469
10470 // Commit or rollback
10471 if ($error) {
10472 $this->db->rollback();
10473 return -1;
10474 } else {
10475 $this->db->commit();
10476 return $this->id;
10477 }
10478 }
10479
10488 public function deleteCommon(User $user, $notrigger = 0, $forcechilddeletion = 0)
10489 {
10490 dol_syslog(get_class($this)."::deleteCommon delete", LOG_DEBUG);
10491
10492 $error = 0;
10493
10494 $this->db->begin();
10495
10496 if ($forcechilddeletion) { // Force also delete of childtables that should lock deletion in standard case when option force is off
10497 foreach ($this->childtables as $table) {
10498 $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
10499 $resql = $this->db->query($sql);
10500 if (!$resql) {
10501 $this->error = $this->db->lasterror();
10502 $this->errors[] = $this->error;
10503 $this->db->rollback();
10504 return -1;
10505 }
10506 }
10507 } elseif (!empty($this->childtables)) { // If object has children linked with a foreign key field, we check all child tables.
10508 $objectisused = $this->isObjectUsed($this->id);
10509 if (!empty($objectisused)) {
10510 dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
10511 $this->error = 'ErrorRecordHasChildren';
10512 $this->errors[] = $this->error;
10513 $this->db->rollback();
10514 return 0;
10515 }
10516 }
10517
10518 // Delete cascade first
10519 if (is_array($this->childtablesoncascade) && !empty($this->childtablesoncascade)) {
10520 foreach ($this->childtablesoncascade as $tabletodelete) {
10521 $deleteFromObject = explode(':', $tabletodelete, 4);
10522 if (count($deleteFromObject) >= 2) {
10523 $className = str_replace('@', '', $deleteFromObject[0]);
10524 $filePath = $deleteFromObject[1];
10525 $columnName = $deleteFromObject[2];
10526 $filter = '';
10527 if (!empty($deleteFromObject[3])) {
10528 $filter = $deleteFromObject[3];
10529 }
10530 if (dol_include_once($filePath)) {
10531 $childObject = new $className($this->db);
10532 if (method_exists($childObject, 'deleteByParentField')) {
10533 '@phan-var-force CommonObject $childObject';
10534 $result = $childObject->deleteByParentField($this->id, $columnName, $filter);
10535 if ($result < 0) {
10536 $error++;
10537 $this->errors[] = $childObject->error;
10538 break;
10539 }
10540 } else {
10541 $error++;
10542 $this->errors[] = "You defined a cascade delete on an object $className/$this->id but there is no method deleteByParentField for it";
10543 break;
10544 }
10545 } else {
10546 $error++;
10547 $this->errors[] = 'Cannot include child class file '.$filePath;
10548 break;
10549 }
10550 } else {
10551 // Delete record in child table
10552 $sql = "DELETE FROM ".$this->db->prefix().$tabletodelete." WHERE ".$this->fk_element." = ".((int) $this->id);
10553
10554 $resql = $this->db->query($sql);
10555 if (!$resql) {
10556 $error++;
10557 $this->error = $this->db->lasterror();
10558 $this->errors[] = $this->error;
10559 break;
10560 }
10561 }
10562 }
10563 }
10564
10565 if (!$error) {
10566 if (!$notrigger) {
10567 // Call triggers
10568 $result = $this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
10569 if ($result < 0) {
10570 $error++;
10571 } // Do also here what you must do to rollback action if trigger fail
10572 // End call triggers
10573 }
10574 }
10575
10576 // Delete llx_ecm_files
10577 if (!$error) {
10578 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
10579 if (!$res) {
10580 $error++;
10581 }
10582 }
10583
10584 // Delete linked object
10585 $res = $this->deleteObjectLinked();
10586 if ($res < 0) {
10587 $error++;
10588 }
10589
10590 if (!$error && !empty($this->isextrafieldmanaged)) {
10591 $result = $this->deleteExtraFields();
10592 if ($result < 0) {
10593 $error++;
10594 }
10595 }
10596
10597 if (!$error) {
10598 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.' WHERE rowid='.((int) $this->id);
10599
10600 $resql = $this->db->query($sql);
10601 if (!$resql) {
10602 $error++;
10603 $this->errors[] = $this->db->lasterror();
10604 }
10605 }
10606
10607 // Commit or rollback
10608 if ($error) {
10609 $this->db->rollback();
10610 return -1;
10611 } else {
10612 $this->db->commit();
10613 return 1;
10614 }
10615 }
10616
10628 public function deleteByParentField($parentId = 0, $parentField = '', $filter = '', $filtermode = "AND")
10629 {
10630 global $user;
10631
10632 $error = 0;
10633 $deleted = 0;
10634
10635 if (!empty($parentId) && !empty($parentField)) {
10636 $this->db->begin();
10637
10638 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
10639 $sql .= " WHERE ".$this->db->sanitize($parentField)." = ".(int) $parentId;
10640
10641 // Manage filter
10642 $errormessage = '';
10643 $sql .= forgeSQLFromUniversalSearchCriteria($filter, $errormessage);
10644 if ($errormessage) {
10645 $this->errors[] = $errormessage;
10646 dol_syslog(__METHOD__.' '.implode(',', $this->errors), LOG_ERR);
10647 return -1;
10648 }
10649
10650 $resql = $this->db->query($sql);
10651 if (!$resql) {
10652 $this->errors[] = $this->db->lasterror();
10653 $error++;
10654 } else {
10655 while ($obj = $this->db->fetch_object($resql)) {
10656 $result = $this->fetch($obj->rowid); // @phpstan-ignore-line
10657 if ($result < 0) {
10658 $error++;
10659 $this->errors[] = $this->error;
10660 } else {
10661 $result = $this->delete($user); // @phpstan-ignore-line
10662 if ($result < 0) {
10663 $error++;
10664 $this->errors[] = $this->error;
10665 } else {
10666 $deleted++;
10667 }
10668 }
10669 }
10670 }
10671
10672 if (empty($error)) {
10673 $this->db->commit();
10674 return $deleted;
10675 } else {
10676 $this->error = implode(', ', $this->errors);
10677 $this->db->rollback();
10678 return $error * -1;
10679 }
10680 }
10681
10682 return $deleted;
10683 }
10684
10693 public function deleteLineCommon(User $user, $idline, $notrigger = 0)
10694 {
10695 $error = 0;
10696
10697 $tmpforobjectclass = get_class($this);
10698 $tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line';
10699
10700 $this->db->begin();
10701
10702 // Call trigger
10703 $result = $this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user);
10704 if ($result < 0) {
10705 $error++;
10706 }
10707 // End call triggers
10708
10709 if (empty($error)) {
10710 $sql = "DELETE FROM ".$this->db->prefix().$this->table_element_line;
10711 $sql .= " WHERE rowid = ".((int) $idline);
10712
10713 $resql = $this->db->query($sql);
10714 if (!$resql) {
10715 $this->error = "Error ".$this->db->lasterror();
10716 $error++;
10717 }
10718 }
10719
10720 if (empty($error)) {
10721 // Remove extrafields
10722 $tmpobjectline = new $tmpforobjectlineclass($this->db);
10723 '@phan-var-force CommonObjectLine $tmpobjectline';
10724 if (!isset($tmpobjectline->isextrafieldmanaged) || !empty($tmpobjectline->isextrafieldmanaged)) {
10725 $tmpobjectline->id = $idline;
10726 $result = $tmpobjectline->deleteExtraFields();
10727 if ($result < 0) {
10728 $error++;
10729 $this->error = "Error ".get_class($this)."::deleteLineCommon deleteExtraFields error -4 ".$tmpobjectline->error;
10730 }
10731 }
10732 }
10733
10734 if (empty($error)) {
10735 $this->db->commit();
10736 return 1;
10737 } else {
10738 dol_syslog(get_class($this)."::deleteLineCommon ERROR:".$this->error, LOG_ERR);
10739 $this->db->rollback();
10740 return -1;
10741 }
10742 }
10743
10744
10754 public function setStatusCommon($user, $status, $notrigger = 0, $triggercode = '')
10755 {
10756 $error = 0;
10757
10758 $this->db->begin();
10759
10760 $statusfield = 'status';
10761 if (in_array($this->element, array('don', 'donation', 'shipping'))) {
10762 $statusfield = 'fk_statut';
10763 }
10764
10765 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
10766 $sql .= " SET ".$statusfield." = ".((int) $status);
10767 $sql .= " WHERE rowid = ".((int) $this->id);
10768
10769 if ($this->db->query($sql)) {
10770 if (!$error) {
10771 $this->oldcopy = clone $this;
10772 }
10773
10774 if (!$error && !$notrigger) {
10775 // Call trigger
10776 $result = $this->call_trigger($triggercode, $user);
10777 if ($result < 0) {
10778 $error++;
10779 }
10780 }
10781
10782 if (!$error) {
10783 $this->status = $status;
10784 if (property_exists($this, 'statut')) { // For backward compatibility
10785 $this->statut = $status;
10786 }
10787 $this->db->commit();
10788 return 1;
10789 } else {
10790 $this->db->rollback();
10791 return -1;
10792 }
10793 } else {
10794 $this->error = $this->db->error();
10795 $this->db->rollback();
10796 return -1;
10797 }
10798 }
10799
10809 public function setSignedStatusCommon($user, $status, $notrigger = 0, $triggercode = '')
10810 {
10811 $error = 0;
10812
10813 $this->db->begin();
10814
10815 $statusfield = 'signed_status';
10816
10817 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
10818 $sql .= " SET ".$statusfield." = ".((int) $status);
10819 $sql .= " WHERE rowid = ".((int) $this->id);
10820
10821 if ($this->db->query($sql)) {
10822 if (!$error) {
10823 $this->oldcopy = clone $this;
10824 }
10825
10826 if (!$error && !$notrigger) {
10827 // Call trigger
10828 $result = $this->call_trigger($triggercode, $user);
10829 if ($result < 0) {
10830 $error++;
10831 }
10832 }
10833
10834 if (!$error) {
10835 $this->status = $status;
10836 $this->db->commit();
10837 return 1;
10838 } else {
10839 $this->db->rollback();
10840 return -1;
10841 }
10842 } else {
10843 $this->error = $this->db->error();
10844 $this->db->rollback();
10845 return -1;
10846 }
10847 }
10848
10849
10856 public function initAsSpecimenCommon()
10857 {
10858 global $user;
10859
10860 $this->id = 0;
10861 $this->specimen = 1;
10862 $fields = array(
10863 'label' => 'This is label',
10864 'ref' => 'ABCD1234',
10865 'description' => 'This is a description',
10866 'qty' => 123.12,
10867 'note_public' => 'Public note',
10868 'note_private' => 'Private note',
10869 'date_creation' => (dol_now() - 3600 * 48),
10870 'date_modification' => (dol_now() - 3600 * 24),
10871 'fk_user_creat' => $user->id,
10872 'fk_user_modif' => $user->id,
10873 'date' => dol_now(),
10874 );
10875 foreach ($fields as $key => $value) {
10876 if (array_key_exists($key, $this->fields)) {
10877 $this->{$key} = $value; // @phpstan-ignore-line
10878 }
10879 }
10880
10881 // Force values to default values when known
10882 if (property_exists($this, 'fields')) {
10883 foreach ($this->fields as $key => $value) {
10884 // If fields are already set, do nothing
10885 if (array_key_exists($key, $fields)) {
10886 continue;
10887 }
10888
10889 if (!empty($value['default'])) {
10890 $this->$key = $value['default'];
10891 }
10892 }
10893 }
10894
10895 return 1;
10896 }
10897
10898
10899 /* Part for comments */
10900
10906 public function fetchComments()
10907 {
10908 require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
10909
10910 $comment = new Comment($this->db);
10911 $result = $comment->fetchAllFor($this->element, $this->id);
10912 if ($result < 0) {
10913 $this->errors = array_merge($this->errors, $comment->errors);
10914 return -1;
10915 } else {
10916 $this->comments = $comment->comments;
10917 }
10918 return count($this->comments);
10919 }
10920
10926 public function getNbComments()
10927 {
10928 return count($this->comments);
10929 }
10930
10937 public function trimParameters($parameters)
10938 {
10939 if (!is_array($parameters)) {
10940 return;
10941 }
10942 foreach ($parameters as $parameter) {
10943 if (isset($this->$parameter)) {
10944 $this->$parameter = trim($this->$parameter);
10945 }
10946 }
10947 }
10948
10949 /* Part for categories/tags */
10950
10961 public function getCategoriesCommon($type_categ)
10962 {
10963 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10964
10965 // Get current categories
10966 $c = new Categorie($this->db);
10967 $existing = $c->containing($this->id, $type_categ, 'id');
10968
10969 return $existing;
10970 }
10971
10984 public function setCategoriesCommon($categories, $type_categ = '', $remove_existing = true)
10985 {
10986 // Handle single category
10987 if (!is_array($categories)) {
10988 $categories = array($categories);
10989 }
10990
10991 dol_syslog(get_class($this)."::setCategoriesCommon Object Id:".$this->id.' type_categ:'.$type_categ.' nb tag add:'.count($categories), LOG_DEBUG);
10992
10993 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10994
10995 if (empty($type_categ)) {
10996 dol_syslog(__METHOD__.': Type '.$type_categ.'is an unknown category type. Done nothing.', LOG_ERR);
10997 return -1;
10998 }
10999
11000 // Get current categories
11001 $c = new Categorie($this->db);
11002 $existing = $c->containing($this->id, $type_categ, 'id');
11003 if ($remove_existing) {
11004 // Diff
11005 if (is_array($existing)) {
11006 $to_del = array_diff($existing, $categories);
11007 $to_add = array_diff($categories, $existing);
11008 } else {
11009 $to_del = array(); // Nothing to delete
11010 $to_add = $categories;
11011 }
11012 } else {
11013 $to_del = array(); // Nothing to delete
11014 $to_add = array_diff($categories, $existing);
11015 }
11016
11017 $error = 0;
11018 $ok = 0;
11019
11020 // Process
11021 foreach ($to_del as $del) {
11022 if ($c->fetch($del) > 0) {
11023 $result = $c->del_type($this, $type_categ);
11024 if ($result < 0) {
11025 $error++;
11026 $this->error = $c->error;
11027 $this->errors = $c->errors;
11028 break;
11029 } else {
11030 $ok += $result;
11031 }
11032 }
11033 }
11034 foreach ($to_add as $add) {
11035 if ($c->fetch($add) > 0) {
11036 $result = $c->add_type($this, $type_categ);
11037 if ($result < 0) {
11038 $error++;
11039 $this->error = $c->error;
11040 $this->errors = $c->errors;
11041 break;
11042 } else {
11043 $ok += $result;
11044 }
11045 }
11046 }
11047
11048 return $error ? (-1 * $error) : $ok;
11049 }
11050
11059 public function cloneCategories($fromId, $toId, $type = '')
11060 {
11061 $this->db->begin();
11062
11063 if (empty($type)) {
11064 $type = $this->table_element;
11065 }
11066
11067 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
11068 $categorystatic = new Categorie($this->db);
11069
11070 $sql = "INSERT INTO ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type])." (fk_categorie, fk_product)";
11071 $sql .= " SELECT fk_categorie, $toId FROM ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type]);
11072 $sql .= " WHERE fk_product = ".((int) $fromId);
11073
11074 if (!$this->db->query($sql)) {
11075 $this->error = $this->db->lasterror();
11076 $this->db->rollback();
11077 return -1;
11078 }
11079
11080 $this->db->commit();
11081 return 1;
11082 }
11083
11090 public function deleteEcmFiles($mode = 0)
11091 {
11092 global $conf;
11093
11094 $this->db->begin();
11095
11096 // Delete in database with mode 0
11097 if ($mode == 0) {
11098 switch ($this->element) {
11099 case 'propal':
11100 $element = 'propale';
11101 break;
11102 case 'product':
11103 $element = 'produit';
11104 break;
11105 case 'order_supplier':
11106 $element = 'fournisseur/commande';
11107 break;
11108 case 'invoice_supplier':
11109 // Special cases that need to use get_exdir to get real dir of object
11110 // In future, all object should use this to define path of documents.
11111 $element = 'fournisseur/facture/'.get_exdir($this->id, 2, 0, 1, $this, 'invoice_supplier');
11112 break;
11113 case 'shipping':
11114 $element = 'expedition/sending';
11115 break;
11116 case 'task':
11117 case 'project_task':
11118 require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
11119
11120 $project_result = $this->fetch_projet();
11121 if ($project_result >= 0) {
11122 $element = 'projet/'.dol_sanitizeFileName($this->project->ref).'/';
11123 }
11124 // no break
11125 default:
11126 $element = $this->element;
11127 }
11128 '@phan-var-force string $element';
11129
11130 // Delete ecm_files_extrafields with mode 0 (using name)
11131 $sql = "DELETE FROM ".$this->db->prefix()."ecm_files_extrafields WHERE fk_object IN (";
11132 $sql .= " SELECT rowid FROM ".$this->db->prefix()."ecm_files 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 $sql .= ")";
11135
11136 if (!$this->db->query($sql)) {
11137 $this->error = $this->db->lasterror();
11138 $this->db->rollback();
11139 return false;
11140 }
11141
11142 // Delete ecm_files with mode 0 (using name)
11143 $sql = "DELETE FROM ".$this->db->prefix()."ecm_files";
11144 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
11145 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
11146
11147 if (!$this->db->query($sql)) {
11148 $this->error = $this->db->lasterror();
11149 $this->db->rollback();
11150 return false;
11151 }
11152 }
11153
11154 // Delete in database with mode 1
11155 if ($mode == 1) {
11156 $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files_extrafields";
11157 $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).")";
11158 $resql = $this->db->query($sql);
11159 if (!$resql) {
11160 $this->error = $this->db->lasterror();
11161 $this->db->rollback();
11162 return false;
11163 }
11164
11165 $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files";
11166 $sql .= " WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? "" : "@".$this->module))."' AND src_object_id = ".((int) $this->id);
11167 $resql = $this->db->query($sql);
11168 if (!$resql) {
11169 $this->error = $this->db->lasterror();
11170 $this->db->rollback();
11171 return false;
11172 }
11173 }
11174
11175 $this->db->commit();
11176 return true;
11177 }
11178}
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