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