dolibarr 19.0.3
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 <dolipar@dolipar.org>
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-2023 Frédéric France <frederic.france@netlogic.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 *
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 3 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program. If not, see <https://www.gnu.org/licenses/>.
33 */
34
45abstract class CommonObject
46{
47 const TRIGGER_PREFIX = ''; // to be overriden in child class implementations, i.e. 'BILL', 'TASK', 'PROPAL', etc.
48
52 public $module;
53
57 public $db;
58
62 public $id;
63
67 public $entity;
68
73 public $error;
74
78 public $errorhidden;
79
83 public $errors = array();
84
88 private $validateFieldsErrors = array();
89
93 public $element;
94
98 public $fk_element;
99
104 public $element_for_permission;
105
109 public $table_element;
110
114 public $table_element_line = '';
115
119 public $ismultientitymanaged;
120
124 public $import_key;
125
129 public $array_options = array();
130
134 public $fields = array();
135
139 public $array_languages = null; // Value is array() when load already tried
140
144 public $contacts_ids;
145
149 public $linked_objects;
150
154 public $linkedObjectsIds;
155
159 public $linkedObjects;
160
164 private $linkedObjectsFullLoaded = array();
165
169 public $oldcopy;
170
174 public $oldref;
175
179 protected $table_ref_field = '';
180
184 public $restrictiononfksoc = 0;
185
186
187 // Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them.
188
192 public $context = array();
193
194 // Properties set and used by Agenda trigger
195 public $actionmsg;
196 public $actionmsg2;
197
201 public $canvas;
202
207 public $project;
208
213 public $fk_project;
214
220 public $projet;
221
227
232 public $contact;
233
238 public $contact_id;
239
244 public $thirdparty;
245
250 public $user;
251
256 public $origin;
257
262 public $origin_id;
263
267 public $origin_object;
268
269 // TODO Remove this. Has been replaced with ->origin_object.
270 // This is set by fetch_origin() from this->origin and this->origin_id
277
278
282 public $ref;
283
287 public $ref_ext;
288
292 public $ref_previous;
293
297 public $ref_next;
298
302 public $newref;
303
309 public $statut;
310
315 public $status;
316
317
322 public $country;
323
328 public $country_id;
329
334 public $country_code;
335
340 public $state;
341
346 public $state_id;
347
352 public $state_code;
353
358 public $region_id;
359
364 public $region_code;
365
370 public $region;
371
372
377 public $barcode_type;
378
383 public $barcode_type_code;
384
389 public $barcode_type_label;
390
395 public $barcode_type_coder;
396
401 public $mode_reglement_id;
402
407 public $cond_reglement_id;
408
412 public $demand_reason_id;
413
418 public $transport_mode_id;
419
425 public $cond_reglement;
426
432 public $fk_delivery_address;
433
438 public $shipping_method_id;
439
444 public $shipping_method;
445
446 // Multicurrency
450 public $fk_multicurrency;
451
455 public $multicurrency_code;
456
460 public $multicurrency_tx;
461
465 public $multicurrency_total_ht;
466
470 public $multicurrency_total_tva;
471
475 public $multicurrency_total_ttc;
476
480 public $multicurrency_total_localtax1; // not in database
481
485 public $multicurrency_total_localtax2; // not in database
486
491 public $model_pdf;
492
498 public $modelpdf;
499
504 public $last_main_doc;
505
511 public $fk_bank;
512
517 public $fk_account;
518
523 public $note_public;
524
529 public $note_private;
530
535 public $note;
536
541 public $total_ht;
542
547 public $total_tva;
548
553 public $total_localtax1;
554
559 public $total_localtax2;
560
565 public $total_ttc;
566
570 public $lines;
571
576 public $comments = array();
577
581 public $name;
582
586 public $lastname;
587
591 public $firstname;
592
596 public $civility_id;
597
598 // Dates
602 public $date_creation;
603
607 public $date_validation; // Date validation
608
612 public $date_modification; // Date last change (tms field)
613
618 public $date_update;
619
623 public $date_cloture; // Date closing (tms field)
624
629 public $user_author;
630
635 public $user_creation;
636
640 public $user_creation_id;
641
646 public $user_valid;
647
652 public $user_validation;
653
657 public $user_validation_id;
658
662 public $user_closing_id;
663
668 public $user_modification;
669
673 public $user_modification_id;
674
675
676 public $next_prev_filter;
677
681 public $specimen = 0;
682
686 public $sendtoid;
687
692 public $alreadypaid;
696 public $totalpaid;
697
701 public $labelStatus = array();
702
706 public $labelStatusShort = array();
707
711 public $tpl;
712
713
717 public $showphoto_on_popup;
718
722 public $nb = array();
723
727 public $nbphoto;
728
732 public $output;
733
737 public $extraparams = array();
738
742 protected $childtables = array();
743
749 protected $childtablesoncascade = array();
750
754 public $product;
755
759 public $cond_reglement_supplier_id;
760
764 public $deposit_percent;
765
766
770 public $retained_warranty_fk_cond_reglement;
771
775 public $warehouse_id;
776
777 // No constructor as it is an abstract class
778
779
790 public static function isExistingObject($element, $id, $ref = '', $ref_ext = '')
791 {
792 global $db, $conf;
793
794 $sql = "SELECT rowid, ref, ref_ext";
795 $sql .= " FROM ".$db->prefix().$element;
796 $sql .= " WHERE entity IN (".getEntity($element).")";
797
798 if ($id > 0) {
799 $sql .= " AND rowid = ".((int) $id);
800 } elseif ($ref) {
801 $sql .= " AND ref = '".$db->escape($ref)."'";
802 } elseif ($ref_ext) {
803 $sql .= " AND ref_ext = '".$db->escape($ref_ext)."'";
804 } else {
805 $error = 'ErrorWrongParameters';
806 dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
807 return -1;
808 }
809 if ($ref || $ref_ext) { // Because the same ref can exists in 2 different entities, we force the current one in priority
810 $sql .= " AND entity = ".((int) $conf->entity);
811 }
812
813 dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
814 $resql = $db->query($sql);
815 if ($resql) {
816 $num = $db->num_rows($resql);
817 if ($num > 0) {
818 return 1;
819 } else {
820 return 0;
821 }
822 }
823 return -1;
824 }
825
832 public function setErrorsFromObject($object)
833 {
834 if (!empty($object->error)) {
835 $this->error = $object->error;
836 }
837 if (!empty($object->errors)) {
838 $this->errors = array_merge($this->errors, $object->errors);
839 }
840 }
841
849 public function getTooltipContentArray($params)
850 {
851 return [];
852 }
853
861 public function getTooltipContent($params)
862 {
863 global $action, $extrafields, $langs, $hookmanager;
864
865 // If there is too much extrafields, we do not include them into tooltip
866 $MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP = getDolGlobalInt('MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP', 3);
867
868 $datas = $this->getTooltipContentArray($params);
869 $count = 0;
870
871 // Add extrafields
872 if (!empty($extrafields->attributes[$this->table_element]['label'])) {
873 $datas['opendivextra'] = '<div class="centpercent wordbreak divtooltipextra">';
874 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
875 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
876 continue;
877 }
878 if ($count >= abs($MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP)) {
879 $datas['more_extrafields'] = '<br>...';
880 break;
881 }
882 $enabled = 1;
883 if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
884 $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
885 }
886 if ($enabled && isset($extrafields->attributes[$this->table_element]['list'][$key])) {
887 $enabled = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
888 }
889 $perms = 1;
890 if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
891 $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
892 }
893 if (empty($enabled)) {
894 continue; // 0 = Never visible field
895 }
896 if (abs($enabled) != 1 && abs($enabled) != 3 && abs($enabled) != 5 && abs($enabled) != 4) {
897 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list <> 4 = not visible at the creation
898 }
899 if (empty($perms)) {
900 continue; // 0 = Not visible
901 }
902 if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
903 $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
904 }
905 $labelextra = $langs->trans((string) $extrafields->attributes[$this->table_element]['label'][$key]);
906 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
907 $datas[$key]= '<br><b><u>'. $labelextra . '</u></b>';
908 } else {
909 $value = (empty($this->array_options['options_' . $key]) ? '' : $this->array_options['options_' . $key]);
910 $datas[$key]= '<br><b>'. $labelextra . ':</b> ' . $extrafields->showOutputField($key, $value, '', $this->table_element);
911 $count++;
912 }
913 }
914 $datas['closedivextra'] = '</div>';
915 }
916
917 $hookmanager->initHooks(array($this->element . 'dao'));
918 $parameters = array(
919 'tooltipcontentarray' => &$datas,
920 'params' => $params,
921 );
922 // Note that $action and $object may have been modified by some hooks
923 $hookmanager->executeHooks('getTooltipContent', $parameters, $this, $action);
924
925 //var_dump($datas);
926 $label = implode($datas);
927
928 return $label;
929 }
930
931
937 public function errorsToString()
938 {
939 return $this->error.(is_array($this->errors) ? (($this->error != '' ? ', ' : '').join(', ', $this->errors)) : '');
940 }
941
942
949 public function getFormatedCustomerRef($objref)
950 {
951 global $hookmanager;
952
953 $parameters = array('objref'=>$objref);
954 $action = '';
955 $reshook = $hookmanager->executeHooks('getFormatedCustomerRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
956 if ($reshook > 0) {
957 return $hookmanager->resArray['objref'];
958 }
959 return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
960 }
961
968 public function getFormatedSupplierRef($objref)
969 {
970 global $hookmanager;
971
972 $parameters = array('objref'=>$objref);
973 $action = '';
974 $reshook = $hookmanager->executeHooks('getFormatedSupplierRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
975 if ($reshook > 0) {
976 return $hookmanager->resArray['objref'];
977 }
978 return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
979 }
980
990 public function getFullAddress($withcountry = 0, $sep = "\n", $withregion = 0, $extralangcode = '')
991 {
992 if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country))) {
993 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
994 $tmparray = getCountry($this->country_id, 'all');
995 $this->country_code = $tmparray['code'];
996 $this->country = $tmparray['label'];
997 }
998
999 if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_code))) {
1000 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
1001 $tmparray = getState($this->state_id, 'all', 0, 1);
1002 $this->state_code = $tmparray['code'];
1003 $this->state = $tmparray['label'];
1004 $this->region_code = $tmparray['region_code'];
1005 $this->region = $tmparray['region'];
1006 }
1007
1008 return dol_format_address($this, $withcountry, $sep, '', 0, $extralangcode);
1009 }
1010
1011
1020 public function getLastMainDocLink($modulepart, $initsharekey = 0, $relativelink = 0)
1021 {
1022 global $user, $dolibarr_main_url_root;
1023
1024 if (empty($this->last_main_doc)) {
1025 return ''; // No way to known which document name to use
1026 }
1027
1028 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
1029 $ecmfile = new EcmFiles($this->db);
1030 $result = $ecmfile->fetch(0, '', $this->last_main_doc);
1031 if ($result < 0) {
1032 $this->error = $ecmfile->error;
1033 $this->errors = $ecmfile->errors;
1034 return -1;
1035 }
1036
1037 if (empty($ecmfile->id)) {
1038 // Add entry into index
1039 if ($initsharekey) {
1040 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
1041
1042 // TODO We can't, we dont' have full path of file, only last_main_doc and ->element, so we must first rebuild full path $destfull
1043 /*
1044 $ecmfile->filepath = $rel_dir;
1045 $ecmfile->filename = $filename;
1046 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
1047 $ecmfile->fullpath_orig = '';
1048 $ecmfile->gen_or_uploaded = 'generated';
1049 $ecmfile->description = ''; // indexed content
1050 $ecmfile->keywords = ''; // keyword content
1051 $ecmfile->share = getRandomPassword(true);
1052 $result = $ecmfile->create($user);
1053 if ($result < 0)
1054 {
1055 $this->error = $ecmfile->error;
1056 $this->errors = $ecmfile->errors;
1057 }
1058 */
1059 } else {
1060 return '';
1061 }
1062 } elseif (empty($ecmfile->share)) {
1063 // Add entry into index
1064 if ($initsharekey) {
1065 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
1066 $ecmfile->share = getRandomPassword(true);
1067 $ecmfile->update($user);
1068 } else {
1069 return '';
1070 }
1071 }
1072 // Define $urlwithroot
1073 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1074 // This is to use external domain name found into config file
1075 //if (DOL_URL_ROOT && ! preg_match('/\/$/', $urlwithouturlroot) && ! preg_match('/^\//', DOL_URL_ROOT)) $urlwithroot=$urlwithouturlroot.'/'.DOL_URL_ROOT;
1076 //else
1077 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT;
1078 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1079
1080 $forcedownload = 0;
1081
1082 $paramlink = '';
1083 //if (!empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart; // For sharing with hash (so public files), modulepart is not required.
1084 //if (!empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; // For sharing with hash (so public files), entity is not required.
1085 //$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath); // No need of name of file for public link, we will use the hash
1086 if (!empty($ecmfile->share)) {
1087 $paramlink .= ($paramlink ? '&' : '').'hashp='.$ecmfile->share; // Hash for public share
1088 }
1089 if ($forcedownload) {
1090 $paramlink .= ($paramlink ? '&' : '').'attachment=1';
1091 }
1092
1093 if ($relativelink) {
1094 $linktoreturn = 'document.php'.($paramlink ? '?'.$paramlink : '');
1095 } else {
1096 $linktoreturn = $urlwithroot.'/document.php'.($paramlink ? '?'.$paramlink : '');
1097 }
1098
1099 // Here $ecmfile->share is defined
1100 return $linktoreturn;
1101 }
1102
1103
1104 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1114 public function add_contact($fk_socpeople, $type_contact, $source = 'external', $notrigger = 0)
1115 {
1116 // phpcs:enable
1117 global $user, $langs;
1118
1119
1120 dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
1121
1122 // Check parameters
1123 if ($fk_socpeople <= 0) {
1124 $langs->load("errors");
1125 $this->error = $langs->trans("ErrorWrongValueForParameterX", "1");
1126 dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
1127 return -1;
1128 }
1129 if (!$type_contact) {
1130 $langs->load("errors");
1131 $this->error = $langs->trans("ErrorWrongValueForParameterX", "2");
1132 dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
1133 return -2;
1134 }
1135
1136 $id_type_contact = 0;
1137 if (is_numeric($type_contact)) {
1138 $id_type_contact = $type_contact;
1139 } else {
1140 // We look for id type_contact
1141 $sql = "SELECT tc.rowid";
1142 $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1143 $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1144 $sql .= " AND tc.source='".$this->db->escape($source)."'";
1145 $sql .= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
1146 //print $sql;
1147 $resql = $this->db->query($sql);
1148 if ($resql) {
1149 $obj = $this->db->fetch_object($resql);
1150 if ($obj) {
1151 $id_type_contact = $obj->rowid;
1152 }
1153 }
1154 }
1155
1156 if ($id_type_contact == 0) {
1157 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");
1158 return 0;
1159 }
1160
1161 $datecreate = dol_now();
1162
1163 // Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
1164 $TListeContacts = $this->liste_contact(-1, $source);
1165 $already_added = false;
1166 if (is_array($TListeContacts) && !empty($TListeContacts)) {
1167 foreach ($TListeContacts as $array_contact) {
1168 if ($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
1169 $already_added = true;
1170 break;
1171 }
1172 }
1173 }
1174
1175 if (!$already_added) {
1176 $this->db->begin();
1177
1178 // Insert into database
1179 $sql = "INSERT INTO ".$this->db->prefix()."element_contact";
1180 $sql .= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
1181 $sql .= " VALUES (".$this->id.", ".((int) $fk_socpeople)." , ";
1182 $sql .= "'".$this->db->idate($datecreate)."'";
1183 $sql .= ", 4, ".((int) $id_type_contact);
1184 $sql .= ")";
1185
1186 $resql = $this->db->query($sql);
1187 if ($resql) {
1188 if (!$notrigger) {
1189 $result = $this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
1190 if ($result < 0) {
1191 $this->db->rollback();
1192 return -1;
1193 }
1194 }
1195
1196 $this->db->commit();
1197 return 1;
1198 } else {
1199 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1200 $this->error = $this->db->errno();
1201 $this->db->rollback();
1202 return -2;
1203 } else {
1204 $this->error = $this->db->lasterror();
1205 $this->db->rollback();
1206 return -1;
1207 }
1208 }
1209 } else {
1210 return 0;
1211 }
1212 }
1213
1214 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1222 public function copy_linked_contact($objFrom, $source = 'internal')
1223 {
1224 // phpcs:enable
1225 $contacts = $objFrom->liste_contact(-1, $source);
1226 foreach ($contacts as $contact) {
1227 if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0) {
1228 return -1;
1229 }
1230 }
1231 return 1;
1232 }
1233
1234 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1244 public function update_contact($rowid, $statut, $type_contact_id = 0, $fk_socpeople = 0)
1245 {
1246 // phpcs:enable
1247 // Insert into database
1248 $sql = "UPDATE ".$this->db->prefix()."element_contact set";
1249 $sql .= " statut = ".$statut;
1250 if ($type_contact_id) {
1251 $sql .= ", fk_c_type_contact = ".((int) $type_contact_id);
1252 }
1253 if ($fk_socpeople) {
1254 $sql .= ", fk_socpeople = ".((int) $fk_socpeople);
1255 }
1256 $sql .= " where rowid = ".((int) $rowid);
1257 $resql = $this->db->query($sql);
1258 if ($resql) {
1259 return 0;
1260 } else {
1261 $this->error = $this->db->lasterror();
1262 return -1;
1263 }
1264 }
1265
1266 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1274 public function delete_contact($rowid, $notrigger = 0)
1275 {
1276 // phpcs:enable
1277 global $user;
1278
1279 $error = 0;
1280
1281 $this->db->begin();
1282
1283 if (!$error && empty($notrigger)) {
1284 // Call trigger
1285 $this->context['contact_id'] = ((int) $rowid);
1286 $result = $this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
1287 if ($result < 0) {
1288 $error++;
1289 }
1290 // End call triggers
1291 }
1292
1293 if (!$error) {
1294 dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
1295
1296 $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
1297 $sql .= " WHERE rowid = ".((int) $rowid);
1298
1299 $result = $this->db->query($sql);
1300 if (!$result) {
1301 $error++;
1302 $this->errors[] = $this->db->lasterror();
1303 }
1304 }
1305
1306 if (!$error) {
1307 $this->db->commit();
1308 return 1;
1309 } else {
1310 $this->error = $this->db->lasterror();
1311 $this->db->rollback();
1312 return -1;
1313 }
1314 }
1315
1316 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1324 public function delete_linked_contact($source = '', $code = '')
1325 {
1326 // phpcs:enable
1327 $listId = '';
1328 $temp = array();
1329 $typeContact = $this->liste_type_contact($source, '', 0, 0, $code);
1330
1331 if (!empty($typeContact)) {
1332 foreach ($typeContact as $key => $value) {
1333 array_push($temp, $key);
1334 }
1335 $listId = implode(",", $temp);
1336 }
1337
1338 // If $listId is empty, we have not criteria on fk_c_type_contact so we will delete record on element_id for
1339 // any type or record instead of only the ones of the current object. So we do nothing in such a case.
1340 if (empty($listId)) {
1341 return 0;
1342 }
1343
1344 $sql = "DELETE FROM ".$this->db->prefix()."element_contact";
1345 $sql .= " WHERE element_id = ".((int) $this->id);
1346 $sql .= " AND fk_c_type_contact IN (".$this->db->sanitize($listId).")";
1347
1348 dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
1349 if ($this->db->query($sql)) {
1350 return 1;
1351 } else {
1352 $this->error = $this->db->lasterror();
1353 return -1;
1354 }
1355 }
1356
1357 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1369 public function liste_contact($statusoflink = -1, $source = 'external', $list = 0, $code = '', $status = -1, $arrayoftcids = array())
1370 {
1371 // phpcs:enable
1372 global $langs;
1373
1374 $tab = array();
1375
1376 $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
1377 if ($source == 'internal') {
1378 $sql .= ", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
1379 }
1380 if ($source == 'external' || $source == 'thirdparty') {
1381 $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
1382 }
1383 $sql .= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
1384 $sql .= ", tc.source, tc.element, tc.code, tc.libelle as type_label";
1385 $sql .= " FROM ".$this->db->prefix()."c_type_contact tc,";
1386 $sql .= " ".$this->db->prefix()."element_contact ec";
1387 if ($source == 'internal') { // internal contact (user)
1388 $sql .= " LEFT JOIN ".$this->db->prefix()."user t on ec.fk_socpeople = t.rowid";
1389 }
1390 if ($source == 'external' || $source == 'thirdparty') { // external contact (socpeople)
1391 $sql .= " LEFT JOIN ".$this->db->prefix()."socpeople t on ec.fk_socpeople = t.rowid";
1392 }
1393 $sql .= " WHERE ec.element_id = ".((int) $this->id);
1394 $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1395 $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1396 if ($code) {
1397 $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1398 }
1399 if ($source == 'internal') {
1400 $sql .= " AND tc.source = 'internal'";
1401 if ($status >= 0) {
1402 $sql .= " AND t.statut = ".((int) $status);
1403 }
1404 }
1405 if ($source == 'external' || $source == 'thirdparty') {
1406 $sql .= " AND tc.source = 'external'";
1407 if ($status >= 0) {
1408 $sql .= " AND t.statut = ".((int) $status); // t is llx_socpeople
1409 }
1410 }
1411 $sql .= " AND tc.active = 1";
1412 if ($statusoflink >= 0) {
1413 $sql .= " AND ec.statut = ".((int) $statusoflink);
1414 }
1415 $sql .= " ORDER BY t.lastname ASC";
1416
1417 dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
1418 $resql = $this->db->query($sql);
1419 if ($resql) {
1420 $num = $this->db->num_rows($resql);
1421 $i = 0;
1422 while ($i < $num) {
1423 $obj = $this->db->fetch_object($resql);
1424
1425 if (!$list) {
1426 $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
1427 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_label);
1428 $tab[$i] = array(
1429 'parentId' => $this->id,
1430 'source' => $obj->source,
1431 'socid' => $obj->socid,
1432 'id' => $obj->id,
1433 'nom' => $obj->lastname, // For backward compatibility
1434 'civility' => $obj->civility,
1435 'lastname' => $obj->lastname,
1436 'firstname' => $obj->firstname,
1437 'email'=>$obj->email,
1438 'login'=> (empty($obj->login) ? '' : $obj->login),
1439 'photo' => (empty($obj->photo) ? '' : $obj->photo),
1440 'statuscontact' => $obj->statuscontact,
1441 'rowid' => $obj->rowid,
1442 'code' => $obj->code,
1443 'libelle' => $libelle_type,
1444 'status' => $obj->statuslink,
1445 'fk_c_type_contact' => $obj->fk_c_type_contact
1446 );
1447 } else {
1448 $tab[$i] = $obj->id;
1449 }
1450
1451 $i++;
1452 }
1453
1454 return $tab;
1455 } else {
1456 $this->error = $this->db->lasterror();
1457 dol_print_error($this->db);
1458 return -1;
1459 }
1460 }
1461
1462
1469 public function swapContactStatus($rowid)
1470 {
1471 $sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1472 $sql .= " tc.code, tc.libelle as type_label";
1473 $sql .= " FROM (".$this->db->prefix()."element_contact as ec, ".$this->db->prefix()."c_type_contact as tc)";
1474 $sql .= " WHERE ec.rowid =".((int) $rowid);
1475 $sql .= " AND ec.fk_c_type_contact=tc.rowid";
1476 $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1477
1478 dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1479 $resql = $this->db->query($sql);
1480 if ($resql) {
1481 $obj = $this->db->fetch_object($resql);
1482 $newstatut = ($obj->statut == 4) ? 5 : 4;
1483 $result = $this->update_contact($rowid, $newstatut);
1484 $this->db->free($resql);
1485 return $result;
1486 } else {
1487 $this->error = $this->db->error();
1488 dol_print_error($this->db);
1489 return -1;
1490 }
1491 }
1492
1493 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1504 public function liste_type_contact($source = 'internal', $order = 'position', $option = 0, $activeonly = 0, $code = '')
1505 {
1506 // phpcs:enable
1507 global $langs;
1508
1509 if (empty($order)) {
1510 $order = 'position';
1511 }
1512 if ($order == 'position') {
1513 $order .= ',code';
1514 }
1515
1516 $tab = array();
1517 $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle as type_label, tc.position";
1518 $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1519 $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1520 if ($activeonly == 1) {
1521 $sql .= " AND tc.active=1"; // only the active types
1522 }
1523 if (!empty($source) && $source != 'all') {
1524 $sql .= " AND tc.source='".$this->db->escape($source)."'";
1525 }
1526 if (!empty($code)) {
1527 $sql .= " AND tc.code='".$this->db->escape($code)."'";
1528 }
1529 $sql .= $this->db->order($order, 'ASC');
1530
1531 //print "sql=".$sql;
1532 $resql = $this->db->query($sql);
1533 if ($resql) {
1534 $num = $this->db->num_rows($resql);
1535 $i = 0;
1536 while ($i < $num) {
1537 $obj = $this->db->fetch_object($resql);
1538
1539 $transkey = "TypeContact_".$this->element."_".$source."_".$obj->code;
1540 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_label);
1541 if (empty($option)) {
1542 $tab[$obj->rowid] = $libelle_type;
1543 } else {
1544 $tab[$obj->code] = $libelle_type;
1545 }
1546 $i++;
1547 }
1548 return $tab;
1549 } else {
1550 $this->error = $this->db->lasterror();
1551 //dol_print_error($this->db);
1552 return null;
1553 }
1554 }
1555
1567 public function listeTypeContacts($source = 'internal', $option = 0, $activeonly = 0, $code = '', $element = '', $excludeelement = '')
1568 {
1569 global $langs, $conf;
1570
1571 $langs->loadLangs(array('bills', 'contracts', 'interventions', 'orders', 'projects', 'propal', 'ticket', 'agenda'));
1572
1573 $tab = array();
1574
1575 $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle as type_label, tc.position, tc.element";
1576 $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1577
1578 $sqlWhere = array();
1579 if (!empty($element)) {
1580 $sqlWhere[] = " tc.element='".$this->db->escape($element)."'";
1581 }
1582 if (!empty($excludeelement)) {
1583 $sqlWhere[] = " tc.element <> '".$this->db->escape($excludeelement)."'";
1584 }
1585
1586 if ($activeonly == 1) {
1587 $sqlWhere[] = " tc.active=1"; // only the active types
1588 }
1589
1590 if (!empty($source) && $source != 'all') {
1591 $sqlWhere[] = " tc.source='".$this->db->escape($source)."'";
1592 }
1593
1594 if (!empty($code)) {
1595 $sqlWhere[] = " tc.code='".$this->db->escape($code)."'";
1596 }
1597
1598 if (count($sqlWhere) > 0) {
1599 $sql .= " WHERE ".implode(' AND ', $sqlWhere);
1600 }
1601
1602 $sql .= $this->db->order('tc.element, tc.position', 'ASC');
1603
1604 dol_syslog(__METHOD__, LOG_DEBUG);
1605 $resql = $this->db->query($sql);
1606 if ($resql) {
1607 $num = $this->db->num_rows($resql);
1608 if ($num > 0) {
1609 $langs->loadLangs(array("propal", "orders", "bills", "suppliers", "contracts", "supplier_proposal"));
1610
1611 while ($obj = $this->db->fetch_object($resql)) {
1612 $modulename = $obj->element;
1613 if (strpos($obj->element, 'project') !== false) {
1614 $modulename = 'projet';
1615 } elseif ($obj->element == 'contrat') {
1616 $element = 'contract';
1617 } elseif ($obj->element == 'action') {
1618 $modulename = 'agenda';
1619 } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1620 $modulename = 'fournisseur';
1621 } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1622 $modulename = 'fournisseur';
1623 }
1624 if (!empty($conf->{$modulename}->enabled)) {
1625 $libelle_element = $langs->trans('ContactDefault_'.$obj->element);
1626 $tmpelement = $obj->element;
1627 $transkey = "TypeContact_".$tmpelement."_".$source."_".$obj->code;
1628 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->type_label);
1629 if (empty($option)) {
1630 $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1631 } else {
1632 $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1633 }
1634 }
1635 }
1636 }
1637 return $tab;
1638 } else {
1639 $this->error = $this->db->lasterror();
1640 return null;
1641 }
1642 }
1643
1655 public function getIdContact($source, $code, $status = 0)
1656 {
1657 global $conf;
1658
1659 $result = array();
1660 $i = 0;
1661 //cas particulier pour les expeditions
1662 if ($this->element == 'shipping' && $this->origin_id != 0) {
1663 $id = $this->origin_id;
1664 $element = 'commande';
1665 } elseif ($this->element == 'reception' && $this->origin_id != 0) {
1666 $id = $this->origin_id;
1667 $element = 'order_supplier';
1668 } else {
1669 $id = $this->id;
1670 $element = $this->element;
1671 }
1672
1673 $sql = "SELECT ec.fk_socpeople";
1674 $sql .= " FROM ".$this->db->prefix()."element_contact as ec,";
1675 if ($source == 'internal') {
1676 $sql .= " ".$this->db->prefix()."user as c,";
1677 }
1678 if ($source == 'external') {
1679 $sql .= " ".$this->db->prefix()."socpeople as c,";
1680 }
1681 $sql .= " ".$this->db->prefix()."c_type_contact as tc";
1682 $sql .= " WHERE ec.element_id = ".((int) $id);
1683 $sql .= " AND ec.fk_socpeople = c.rowid";
1684 if ($source == 'internal') {
1685 $sql .= " AND c.entity IN (".getEntity('user').")";
1686 }
1687 if ($source == 'external') {
1688 $sql .= " AND c.entity IN (".getEntity('societe').")";
1689 }
1690 $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1691 $sql .= " AND tc.element = '".$this->db->escape($element)."'";
1692 $sql .= " AND tc.source = '".$this->db->escape($source)."'";
1693 if ($code) {
1694 $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1695 }
1696 $sql .= " AND tc.active = 1";
1697 if ($status) {
1698 $sql .= " AND ec.statut = ".((int) $status);
1699 }
1700
1701 dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1702 $resql = $this->db->query($sql);
1703 if ($resql) {
1704 while ($obj = $this->db->fetch_object($resql)) {
1705 $result[$i] = $obj->fk_socpeople;
1706 $i++;
1707 }
1708 } else {
1709 $this->error = $this->db->error();
1710 return null;
1711 }
1712
1713 return $result;
1714 }
1715
1716 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1723 public function fetch_contact($contactid = null)
1724 {
1725 // phpcs:enable
1726 if (empty($contactid)) {
1727 $contactid = $this->contact_id;
1728 }
1729
1730 if (empty($contactid)) {
1731 return 0;
1732 }
1733
1734 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1735 $contact = new Contact($this->db);
1736 $result = $contact->fetch($contactid);
1737 $this->contact = $contact;
1738 return $result;
1739 }
1740
1741 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1748 public function fetch_thirdparty($force_thirdparty_id = 0)
1749 {
1750 // phpcs:enable
1751 if (empty($this->socid) && empty($this->fk_soc) && empty($force_thirdparty_id)) {
1752 return 0;
1753 }
1754
1755 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1756
1757 $idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : 0);
1758 if ($force_thirdparty_id) {
1759 $idtofetch = $force_thirdparty_id;
1760 }
1761
1762 if ($idtofetch) {
1763 $thirdparty = new Societe($this->db);
1764 $result = $thirdparty->fetch($idtofetch);
1765 if ($result<0) {
1766 $this->errors=array_merge($this->errors, $thirdparty->errors);
1767 }
1768 $this->thirdparty = $thirdparty;
1769
1770 // Use first price level if level not defined for third party
1771 if (getDolGlobalString('PRODUIT_MULTIPRICES') && empty($this->thirdparty->price_level)) {
1772 $this->thirdparty->price_level = 1;
1773 }
1774
1775 return $result;
1776 } else {
1777 return -1;
1778 }
1779 }
1780
1781
1789 public function fetchOneLike($ref)
1790 {
1791 if (!$this->table_ref_field) {
1792 return 0;
1793 }
1794
1795 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
1796 $sql .= " WHERE ".$this->table_ref_field." LIKE '".$this->db->escape($ref)."'"; // no escapeforlike here
1797 $sql .= " LIMIT 1";
1798
1799 $query = $this->db->query($sql);
1800
1801 if (!$this->db->num_rows($query)) {
1802 return 0;
1803 }
1804
1805 $result = $this->db->fetch_object($query);
1806
1807 if (method_exists($this, 'fetch')) {
1808 return $this->fetch($result->rowid);
1809 } else {
1810 $this->error = 'Fetch method not implemented on '.get_class($this);
1811 dol_syslog(get_class($this).'::fetchOneLike Error='.$this->error, LOG_ERR);
1812 array_push($this->errors, $this->error);
1813 return -1;
1814 }
1815 }
1816
1817 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1825 public function fetch_barcode()
1826 {
1827 // phpcs:enable
1828 global $conf;
1829
1830 dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1831
1832 $idtype = $this->barcode_type;
1833 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
1834 if ($this->element == 'product' && getDolGlobalString('PRODUIT_DEFAULT_BARCODE_TYPE')) {
1835 $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1836 } elseif ($this->element == 'societe') {
1837 $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1838 } else {
1839 dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1840 }
1841 }
1842
1843 if ($idtype > 0) {
1844 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
1845 $sql = "SELECT rowid, code, libelle as label, coder";
1846 $sql .= " FROM ".$this->db->prefix()."c_barcode_type";
1847 $sql .= " WHERE rowid = ".((int) $idtype);
1848 dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1849 $resql = $this->db->query($sql);
1850 if ($resql) {
1851 $obj = $this->db->fetch_object($resql);
1852 $this->barcode_type = $obj->rowid;
1853 $this->barcode_type_code = $obj->code;
1854 $this->barcode_type_label = $obj->label;
1855 $this->barcode_type_coder = $obj->coder;
1856 return 1;
1857 } else {
1858 dol_print_error($this->db);
1859 return -1;
1860 }
1861 }
1862 }
1863 return 0;
1864 }
1865
1866 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1872 public function fetch_project()
1873 {
1874 // phpcs:enable
1875 return $this->fetch_projet();
1876 }
1877
1878 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1884 public function fetch_projet()
1885 {
1886 // phpcs:enable
1887 include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1888
1889 if (empty($this->fk_project) && !empty($this->fk_projet)) {
1890 $this->fk_project = $this->fk_projet; // For backward compatibility
1891 }
1892 if (empty($this->fk_project)) {
1893 return 0;
1894 }
1895
1896 $project = new Project($this->db);
1897 $result = $project->fetch($this->fk_project);
1898
1899 $this->projet = $project; // deprecated
1900 $this->project = $project;
1901 return $result;
1902 }
1903
1904 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1910 public function fetch_product()
1911 {
1912 // phpcs:enable
1913 include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1914
1915 if (empty($this->fk_product)) {
1916 return 0;
1917 }
1918
1919 $product = new Product($this->db);
1920 $result = $product->fetch($this->fk_product);
1921
1922 $this->product = $product;
1923 return $result;
1924 }
1925
1926 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1933 public function fetch_user($userid)
1934 {
1935 // phpcs:enable
1936 $user = new User($this->db);
1937 $result = $user->fetch($userid);
1938 $this->user = $user;
1939 return $result;
1940 }
1941
1942 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1950 public function fetch_origin()
1951 {
1952 // phpcs:enable
1953 if ($this->origin == 'shipping') {
1954 $this->origin = 'expedition';
1955 }
1956 if ($this->origin == 'delivery') {
1957 $this->origin = 'livraison';
1958 }
1959 if ($this->origin == 'order_supplier' || $this->origin == 'supplier_order') {
1960 $this->origin = 'commandeFournisseur';
1961 }
1962
1963 $origin = $this->origin;
1964
1965 $classname = ucfirst($origin);
1966 $this->origin_object = new $classname($this->db);
1967 $this->origin_object->fetch($this->origin_id);
1968
1969 // TODO Remove this line
1970 $this->$origin = $this->origin_object;
1971 }
1972
1982 public function fetchObjectFrom($table, $field, $key, $element = null)
1983 {
1984 global $conf;
1985
1986 $result = false;
1987
1988 $sql = "SELECT rowid FROM ".$this->db->prefix().$table;
1989 $sql .= " WHERE ".$field." = '".$this->db->escape($key)."'";
1990 if (!empty($element)) {
1991 $sql .= " AND entity IN (".getEntity($element).")";
1992 } else {
1993 $sql .= " AND entity = ".((int) $conf->entity);
1994 }
1995
1996 dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
1997 $resql = $this->db->query($sql);
1998 if ($resql) {
1999 $obj = $this->db->fetch_object($resql);
2000 // Test for avoid error -1
2001 if ($obj) {
2002 if (method_exists($this, 'fetch')) {
2003 return $this->fetch($obj->rowid);
2004 } else {
2005 $this->error = 'fetch() method not implemented on '.get_class($this);
2006 dol_syslog(get_class($this).'::fetchOneLike Error='.$this->error, LOG_ERR);
2007 array_push($this->errors, $this->error);
2008 return -1;
2009 }
2010 }
2011 }
2012
2013 return $result;
2014 }
2015
2024 public function getValueFrom($table, $id, $field)
2025 {
2026 $result = false;
2027 if (!empty($id) && !empty($field) && !empty($table)) {
2028 $sql = "SELECT ".$field." FROM ".$this->db->prefix().$table;
2029 $sql .= " WHERE rowid = ".((int) $id);
2030
2031 dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
2032 $resql = $this->db->query($sql);
2033 if ($resql) {
2034 $row = $this->db->fetch_row($resql);
2035 $result = $row[0];
2036 }
2037 }
2038 return $result;
2039 }
2040
2057 public function setValueFrom($field, $value, $table = '', $id = null, $format = '', $id_field = '', $fuser = null, $trigkey = '', $fk_user_field = 'fk_user_modif')
2058 {
2059 global $user;
2060
2061 if (empty($table)) {
2062 $table = $this->table_element;
2063 }
2064 if (empty($id)) {
2065 $id = $this->id;
2066 }
2067 if (empty($format)) {
2068 $format = 'text';
2069 }
2070 if (empty($id_field)) {
2071 $id_field = 'rowid';
2072 }
2073
2074 // Special case
2075 if ($table == 'product' && $field == 'note_private') {
2076 $field = 'note';
2077 }
2078
2079 if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
2080 $fk_user_field = 'fk_user_mod';
2081 }
2082 if (in_array($table, array('prelevement_bons'))) { // TODO Add a field fk_user_modif into llx_prelevement_bons
2083 $fk_user_field = '';
2084 }
2085
2086 if ($trigkey) {
2087 $oldvalue = null;
2088
2089 $sql = "SELECT " . $field;
2090 $sql .= " FROM " . MAIN_DB_PREFIX . $table;
2091 $sql .= " WHERE " . $id_field . " = " . ((int) $id);
2092
2093 $resql = $this->db->query($sql);
2094 if ($resql) {
2095 if ($obj = $this->db->fetch_object($resql)) {
2096 if ($format == 'date') {
2097 $oldvalue = $this->db->jdate($obj->$field);
2098 } else {
2099 $oldvalue = $obj->$field;
2100 }
2101 }
2102 } else {
2103 $this->error = $this->db->lasterror();
2104 return -1;
2105 }
2106 }
2107
2108 $error = 0;
2109
2110 dol_syslog(__METHOD__, LOG_DEBUG);
2111
2112 $this->db->begin();
2113
2114 $sql = "UPDATE ".$this->db->prefix().$table." SET ";
2115
2116 if ($format == 'text') {
2117 $sql .= $field." = '".$this->db->escape($value)."'";
2118 } elseif ($format == 'int') {
2119 $sql .= $field." = ".((int) $value);
2120 } elseif ($format == 'date') {
2121 $sql .= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
2122 } elseif ($format == 'dategmt') {
2123 $sql .= $field." = ".($value ? "'".$this->db->idate($value, 'gmt')."'" : "null");
2124 }
2125
2126 if ($fk_user_field) {
2127 if (!empty($fuser) && is_object($fuser)) {
2128 $sql .= ", ".$fk_user_field." = ".((int) $fuser->id);
2129 } elseif (empty($fuser) || $fuser != 'none') {
2130 $sql .= ", ".$fk_user_field." = ".((int) $user->id);
2131 }
2132 }
2133
2134 $sql .= " WHERE ".$id_field." = ".((int) $id);
2135
2136 $resql = $this->db->query($sql);
2137 if ($resql) {
2138 if ($trigkey) {
2139 // call trigger with updated object values
2140 if (method_exists($this, 'fetch')) {
2141 $result = $this->fetch($id);
2142 } else {
2143 $result = $this->fetchCommon($id);
2144 }
2145 $this->oldcopy = clone $this;
2146 if (property_exists($this->oldcopy, $field)) {
2147 $this->oldcopy->$field = $oldvalue;
2148 }
2149
2150 if ($result >= 0) {
2151 $result = $this->call_trigger($trigkey, (!empty($fuser) && is_object($fuser)) ? $fuser : $user); // This may set this->errors
2152 }
2153 if ($result < 0) {
2154 $error++;
2155 }
2156 }
2157
2158 if (!$error) {
2159 if (property_exists($this, $field)) {
2160 $this->$field = $value;
2161 }
2162 $this->db->commit();
2163 return 1;
2164 } else {
2165 $this->db->rollback();
2166 return -2;
2167 }
2168 } else {
2169 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2170 $this->error = 'DB_ERROR_RECORD_ALREADY_EXISTS';
2171 } else {
2172 $this->error = $this->db->lasterror();
2173 }
2174 $this->db->rollback();
2175 return -1;
2176 }
2177 }
2178
2179 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2189 public function load_previous_next_ref($filter, $fieldid, $nodbprefix = 0)
2190 {
2191 // phpcs:enable
2192 global $conf, $user;
2193
2194 if (!$this->table_element) {
2195 dol_print_error('', get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
2196 return -1;
2197 }
2198 if ($fieldid == 'none') {
2199 return 1;
2200 }
2201
2202 // For backward compatibility
2203 if (in_array($this->table_element, array('facture_rec', 'facture_fourn_rec')) && $fieldid == 'title') {
2204 $fieldid = 'titre';
2205 }
2206
2207 // Security on socid
2208 $socid = 0;
2209 if ($user->socid > 0) {
2210 $socid = $user->socid;
2211 }
2212
2213 // this->ismultientitymanaged contains
2214 // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
2215 $aliastablesociete = 's';
2216 if ($this->element == 'societe') {
2217 $aliastablesociete = 'te'; // te as table_element
2218 }
2219 $restrictiononfksoc = empty($this->restrictiononfksoc) ? 0 : $this->restrictiononfksoc;
2220 $sql = "SELECT MAX(te.".$fieldid.")";
2221 $sql .= " FROM ".(empty($nodbprefix) ? $this->db->prefix() : '').$this->table_element." as te";
2222 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2223 $tmparray = explode('@', $this->ismultientitymanaged);
2224 $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
2225 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2226 $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
2227 } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2228 $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
2229 }
2230 if ($restrictiononfksoc && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2231 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
2232 }
2233 if ($fieldid == 'rowid') {
2234 $sql .= " WHERE te.".$fieldid." < ".((int) $this->id);
2235 } else {
2236 $sql .= " WHERE te.".$fieldid." < '".$this->db->escape($this->ref)."'"; // ->ref must always be defined (set to id if field does not exists)
2237 }
2238 if ($restrictiononfksoc == 1 && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2239 $sql .= " AND sc.fk_user = ".((int) $user->id);
2240 }
2241 if ($restrictiononfksoc == 2 && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2242 $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
2243 }
2244 if (!empty($filter)) {
2245 if (!preg_match('/^\s*AND/i', $filter)) {
2246 $sql .= " AND ";
2247 }
2248 $sql .= $filter;
2249 }
2250 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2251 $tmparray = explode('@', $this->ismultientitymanaged);
2252 $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
2253 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2254 $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
2255 }
2256 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2257 if ($this->element == 'user' && getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE')) {
2258 if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
2259 $sql .= " AND te.entity IS NOT NULL"; // Show all users
2260 } else {
2261 $sql .= " AND te.rowid IN (SELECT ug.fk_user FROM ".$this->db->prefix()."usergroup_user as ug WHERE ug.entity IN (".getEntity('usergroup')."))";
2262 }
2263 } else {
2264 $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
2265 }
2266 }
2267 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
2268 $tmparray = explode('@', $this->ismultientitymanaged);
2269 $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
2270 }
2271 if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
2272 $sql .= ' AND te.fk_soc = '.((int) $socid);
2273 }
2274 if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
2275 $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
2276 }
2277 if ($restrictiononfksoc && $socid && $this->element == 'societe') {
2278 $sql .= ' AND te.rowid = '.((int) $socid);
2279 }
2280 //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
2281
2282 $result = $this->db->query($sql);
2283 if (!$result) {
2284 $this->error = $this->db->lasterror();
2285 return -1;
2286 }
2287 $row = $this->db->fetch_row($result);
2288 $this->ref_previous = $row[0];
2289
2290 $sql = "SELECT MIN(te.".$fieldid.")";
2291 $sql .= " FROM ".(empty($nodbprefix) ? $this->db->prefix() : '').$this->table_element." as te";
2292 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2293 $tmparray = explode('@', $this->ismultientitymanaged);
2294 $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
2295 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2296 $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
2297 } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2298 $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
2299 }
2300 if ($restrictiononfksoc && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2301 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
2302 }
2303 if ($fieldid == 'rowid') {
2304 $sql .= " WHERE te.".$fieldid." > ".((int) $this->id);
2305 } else {
2306 $sql .= " WHERE te.".$fieldid." > '".$this->db->escape($this->ref)."'"; // ->ref must always be defined (set to id if field does not exists)
2307 }
2308 if ($restrictiononfksoc == 1 && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2309 $sql .= " AND sc.fk_user = ".((int) $user->id);
2310 }
2311 if ($restrictiononfksoc == 2 && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2312 $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
2313 }
2314 if (!empty($filter)) {
2315 if (!preg_match('/^\s*AND/i', $filter)) {
2316 $sql .= " AND "; // For backward compatibility
2317 }
2318 $sql .= $filter;
2319 }
2320 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2321 $tmparray = explode('@', $this->ismultientitymanaged);
2322 $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
2323 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && !$user->hasRight('societe', 'client', 'voir') && !$socid) {
2324 $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
2325 }
2326 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2327 if ($this->element == 'user' && getDolGlobalInt('MULTICOMPANY_TRANSVERSE_MODE')) {
2328 if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
2329 $sql .= " AND te.entity IS NOT NULL"; // Show all users
2330 } else {
2331 $sql .= " AND te.rowid IN (SELECT ug.fk_user FROM ".$this->db->prefix()."usergroup_user as ug WHERE ug.entity IN (".getEntity('usergroup')."))";
2332 }
2333 } else {
2334 $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
2335 }
2336 }
2337 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
2338 $tmparray = explode('@', $this->ismultientitymanaged);
2339 $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
2340 }
2341 if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
2342 $sql .= ' AND te.fk_soc = '.((int) $socid);
2343 }
2344 if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
2345 $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
2346 }
2347 if ($restrictiononfksoc && $socid && $this->element == 'societe') {
2348 $sql .= ' AND te.rowid = '.((int) $socid);
2349 }
2350 //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
2351 // 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
2352
2353 $result = $this->db->query($sql);
2354 if (!$result) {
2355 $this->error = $this->db->lasterror();
2356 return -2;
2357 }
2358 $row = $this->db->fetch_row($result);
2359 $this->ref_next = $row[0];
2360
2361 return 1;
2362 }
2363
2364
2372 public function getListContactId($source = 'external')
2373 {
2374 $contactAlreadySelected = array();
2375 $tab = $this->liste_contact(-1, $source);
2376 $num = count($tab);
2377 $i = 0;
2378 while ($i < $num) {
2379 if ($source == 'thirdparty') {
2380 $contactAlreadySelected[$i] = $tab[$i]['socid'];
2381 } else {
2382 $contactAlreadySelected[$i] = $tab[$i]['id'];
2383 }
2384 $i++;
2385 }
2386 return $contactAlreadySelected;
2387 }
2388
2389
2397 public function setProject($projectid, $notrigger = 0)
2398 {
2399 global $user;
2400 $error = 0;
2401
2402 if (!$this->table_element) {
2403 dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined", LOG_ERR);
2404 return -1;
2405 }
2406
2407 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2408 if (!empty($this->fields['fk_project'])) { // Common case
2409 if ($projectid) {
2410 $sql .= " SET fk_project = ".((int) $projectid);
2411 } else {
2412 $sql .= " SET fk_project = NULL";
2413 }
2414 $sql .= ' WHERE rowid = '.((int) $this->id);
2415 } elseif ($this->table_element == 'actioncomm') { // Special case for actioncomm
2416 if ($projectid) {
2417 $sql .= " SET fk_project = ".((int) $projectid);
2418 } else {
2419 $sql .= " SET fk_project = NULL";
2420 }
2421 $sql .= ' WHERE id = '.((int) $this->id);
2422 } else { // Special case for old architecture objects
2423 if ($projectid) {
2424 $sql .= ' SET fk_projet = '.((int) $projectid);
2425 } else {
2426 $sql .= ' SET fk_projet = NULL';
2427 }
2428 $sql .= " WHERE rowid = ".((int) $this->id);
2429 }
2430
2431 $this->db->begin();
2432
2433 dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
2434 if ($this->db->query($sql)) {
2435 $this->fk_project = ((int) $projectid);
2436 } else {
2437 dol_print_error($this->db);
2438 $error++;
2439 }
2440
2441 // Triggers
2442 if (!$error && !$notrigger) {
2443 // Call triggers
2444 $result = $this->call_trigger(strtoupper($this->element) . '_MODIFY', $user);
2445 if ($result < 0) {
2446 $error++;
2447 } //Do also here what you must do to rollback action if trigger fail
2448 // End call triggers
2449 }
2450
2451 // Commit or rollback
2452 if ($error) {
2453 $this->db->rollback();
2454 return -1;
2455 } else {
2456 $this->db->commit();
2457 return 1;
2458 }
2459 }
2460
2467 public function setPaymentMethods($id)
2468 {
2469 global $user;
2470
2471 $error = 0;
2472 $notrigger = 0;
2473
2474 dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
2475
2476 if ($this->statut >= 0 || $this->element == 'societe') {
2477 // TODO uniformize field name
2478 $fieldname = 'fk_mode_reglement';
2479 if ($this->element == 'societe') {
2480 $fieldname = 'mode_reglement';
2481 }
2482 if (get_class($this) == 'Fournisseur') {
2483 $fieldname = 'mode_reglement_supplier';
2484 }
2485 if (get_class($this) == 'Tva') {
2486 $fieldname = 'fk_typepayment';
2487 }
2488 if (get_class($this) == 'Salary') {
2489 $fieldname = 'fk_typepayment';
2490 }
2491
2492 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2493 $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2494 $sql .= ' WHERE rowid='.((int) $this->id);
2495
2496 if ($this->db->query($sql)) {
2497 $this->mode_reglement_id = $id;
2498 // for supplier
2499 if (get_class($this) == 'Fournisseur') {
2500 $this->mode_reglement_supplier_id = $id;
2501 }
2502 // Triggers
2503 if (!$error && !$notrigger) {
2504 // Call triggers
2505 if (get_class($this) == 'Commande') {
2506 $result = $this->call_trigger('ORDER_MODIFY', $user);
2507 } else {
2508 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
2509 }
2510 if ($result < 0) {
2511 $error++;
2512 }
2513 // End call triggers
2514 }
2515 return 1;
2516 } else {
2517 dol_syslog(get_class($this).'::setPaymentMethods Error '.$this->db->error());
2518 $this->error = $this->db->error();
2519 return -1;
2520 }
2521 } else {
2522 dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
2523 $this->error = 'Status of the object is incompatible '.$this->statut;
2524 return -2;
2525 }
2526 }
2527
2534 public function setMulticurrencyCode($code)
2535 {
2536 dol_syslog(get_class($this).'::setMulticurrencyCode('.$code.')');
2537 if ($this->statut >= 0 || $this->element == 'societe') {
2538 $fieldname = 'multicurrency_code';
2539
2540 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2541 $sql .= " SET ".$fieldname." = '".$this->db->escape($code)."'";
2542 $sql .= ' WHERE rowid='.((int) $this->id);
2543
2544 if ($this->db->query($sql)) {
2545 $this->multicurrency_code = $code;
2546
2547 list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
2548 if ($rate) {
2549 $this->setMulticurrencyRate($rate, 2);
2550 }
2551
2552 return 1;
2553 } else {
2554 dol_syslog(get_class($this).'::setMulticurrencyCode Error '.$sql.' - '.$this->db->error());
2555 $this->error = $this->db->error();
2556 return -1;
2557 }
2558 } else {
2559 dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
2560 $this->error = 'Status of the object is incompatible '.$this->statut;
2561 return -2;
2562 }
2563 }
2564
2572 public function setMulticurrencyRate($rate, $mode = 1)
2573 {
2574 dol_syslog(get_class($this).'::setMulticurrencyRate('.$rate.','.$mode.')');
2575 if ($this->statut >= 0 || $this->element == 'societe') {
2576 $fieldname = 'multicurrency_tx';
2577
2578 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2579 $sql .= " SET ".$fieldname." = ".((float) $rate);
2580 $sql .= ' WHERE rowid='.((int) $this->id);
2581
2582 if ($this->db->query($sql)) {
2583 $this->multicurrency_tx = $rate;
2584
2585 // Update line price
2586 if (!empty($this->lines)) {
2587 foreach ($this->lines as &$line) {
2588 // Amounts in company currency will be recalculated
2589 if ($mode == 1) {
2590 $line->subprice = 0;
2591 }
2592
2593 // Amounts in foreign currency will be recalculated
2594 if ($mode == 2) {
2595 $line->multicurrency_subprice = 0;
2596 }
2597
2598 switch ($this->element) {
2599 case 'propal':
2602 $this->updateline(
2603 $line->id,
2604 $line->subprice,
2605 $line->qty,
2606 $line->remise_percent,
2607 $line->tva_tx,
2608 $line->localtax1_tx,
2609 $line->localtax2_tx,
2610 ($line->description ? $line->description : $line->desc),
2611 'HT',
2612 $line->info_bits,
2613 $line->special_code,
2614 $line->fk_parent_line,
2615 $line->skip_update_total,
2616 $line->fk_fournprice,
2617 $line->pa_ht,
2618 $line->label,
2619 $line->product_type,
2620 $line->date_start,
2621 $line->date_end,
2622 $line->array_options,
2623 $line->fk_unit,
2624 $line->multicurrency_subprice
2625 );
2626 break;
2627 case 'commande':
2630 $this->updateline(
2631 $line->id,
2632 ($line->description ? $line->description : $line->desc),
2633 $line->subprice,
2634 $line->qty,
2635 $line->remise_percent,
2636 $line->tva_tx,
2637 $line->localtax1_tx,
2638 $line->localtax2_tx,
2639 'HT',
2640 $line->info_bits,
2641 $line->date_start,
2642 $line->date_end,
2643 $line->product_type,
2644 $line->fk_parent_line,
2645 $line->skip_update_total,
2646 $line->fk_fournprice,
2647 $line->pa_ht,
2648 $line->label,
2649 $line->special_code,
2650 $line->array_options,
2651 $line->fk_unit,
2652 $line->multicurrency_subprice
2653 );
2654 break;
2655 case 'facture':
2658 $this->updateline(
2659 $line->id,
2660 ($line->description ? $line->description : $line->desc),
2661 $line->subprice,
2662 $line->qty,
2663 $line->remise_percent,
2664 $line->date_start,
2665 $line->date_end,
2666 $line->tva_tx,
2667 $line->localtax1_tx,
2668 $line->localtax2_tx,
2669 'HT',
2670 $line->info_bits,
2671 $line->product_type,
2672 $line->fk_parent_line,
2673 $line->skip_update_total,
2674 $line->fk_fournprice,
2675 $line->pa_ht,
2676 $line->label,
2677 $line->special_code,
2678 $line->array_options,
2679 $line->situation_percent,
2680 $line->fk_unit,
2681 $line->multicurrency_subprice
2682 );
2683 break;
2684 case 'supplier_proposal':
2687 $this->updateline(
2688 $line->id,
2689 $line->subprice,
2690 $line->qty,
2691 $line->remise_percent,
2692 $line->tva_tx,
2693 $line->localtax1_tx,
2694 $line->localtax2_tx,
2695 ($line->description ? $line->description : $line->desc),
2696 'HT',
2697 $line->info_bits,
2698 $line->special_code,
2699 $line->fk_parent_line,
2700 $line->skip_update_total,
2701 $line->fk_fournprice,
2702 $line->pa_ht,
2703 $line->label,
2704 $line->product_type,
2705 $line->array_options,
2706 $line->ref_fourn,
2707 $line->multicurrency_subprice
2708 );
2709 break;
2710 case 'order_supplier':
2713 $this->updateline(
2714 $line->id,
2715 ($line->description ? $line->description : $line->desc),
2716 $line->subprice,
2717 $line->qty,
2718 $line->remise_percent,
2719 $line->tva_tx,
2720 $line->localtax1_tx,
2721 $line->localtax2_tx,
2722 'HT',
2723 $line->info_bits,
2724 $line->product_type,
2725 false,
2726 $line->date_start,
2727 $line->date_end,
2728 $line->array_options,
2729 $line->fk_unit,
2730 $line->multicurrency_subprice,
2731 $line->ref_supplier
2732 );
2733 break;
2734 case 'invoice_supplier':
2737 $this->updateline(
2738 $line->id,
2739 ($line->description ? $line->description : $line->desc),
2740 $line->subprice,
2741 $line->tva_tx,
2742 $line->localtax1_tx,
2743 $line->localtax2_tx,
2744 $line->qty,
2745 0,
2746 'HT',
2747 $line->info_bits,
2748 $line->product_type,
2749 $line->remise_percent,
2750 false,
2751 $line->date_start,
2752 $line->date_end,
2753 $line->array_options,
2754 $line->fk_unit,
2755 $line->multicurrency_subprice,
2756 $line->ref_supplier
2757 );
2758 break;
2759 default:
2760 dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
2761 break;
2762 }
2763 }
2764 }
2765
2766 return 1;
2767 } else {
2768 dol_syslog(get_class($this).'::setMulticurrencyRate Error '.$sql.' - '.$this->db->error());
2769 $this->error = $this->db->error();
2770 return -1;
2771 }
2772 } else {
2773 dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
2774 $this->error = 'Status of the object is incompatible '.$this->statut;
2775 return -2;
2776 }
2777 }
2778
2786 public function setPaymentTerms($id, $deposit_percent = null)
2787 {
2788 dol_syslog(get_class($this).'::setPaymentTerms('.$id.', '.var_export($deposit_percent, true).')');
2789 if ($this->statut >= 0 || $this->element == 'societe') {
2790 // TODO uniformize field name
2791 $fieldname = 'fk_cond_reglement';
2792 if ($this->element == 'societe') {
2793 $fieldname = 'cond_reglement';
2794 }
2795 if (get_class($this) == 'Fournisseur') {
2796 $fieldname = 'cond_reglement_supplier';
2797 }
2798
2799 if (empty($deposit_percent) || $deposit_percent < 0) {
2800 $deposit_percent = getDictionaryValue('c_payment_term', 'deposit_percent', $id);
2801 }
2802
2803 if ($deposit_percent > 100) {
2804 $deposit_percent = 100;
2805 }
2806
2807 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2808 $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2809 if (in_array($this->table_element, array('propal', 'commande', 'societe'))) {
2810 $sql .= " , deposit_percent = " . (empty($deposit_percent) ? 'NULL' : "'".$this->db->escape($deposit_percent)."'");
2811 }
2812 $sql .= ' WHERE rowid='.((int) $this->id);
2813
2814 if ($this->db->query($sql)) {
2815 $this->cond_reglement_id = $id;
2816 // for supplier
2817 if (get_class($this) == 'Fournisseur') {
2818 $this->cond_reglement_supplier_id = $id;
2819 }
2820 $this->cond_reglement = $id; // for compatibility
2821 $this->deposit_percent = $deposit_percent;
2822 return 1;
2823 } else {
2824 dol_syslog(get_class($this).'::setPaymentTerms Error '.$sql.' - '.$this->db->error());
2825 $this->error = $this->db->error();
2826 return -1;
2827 }
2828 } else {
2829 dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
2830 $this->error = 'Status of the object is incompatible '.$this->statut;
2831 return -2;
2832 }
2833 }
2834
2841 public function setTransportMode($id)
2842 {
2843 dol_syslog(get_class($this).'::setTransportMode('.$id.')');
2844 if ($this->statut >= 0 || $this->element == 'societe') {
2845 $fieldname = 'fk_transport_mode';
2846 if ($this->element == 'societe') {
2847 $fieldname = 'transport_mode';
2848 }
2849 if (get_class($this) == 'Fournisseur') {
2850 $fieldname = 'transport_mode_supplier';
2851 }
2852
2853 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2854 $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2855 $sql .= ' WHERE rowid='.((int) $this->id);
2856
2857 if ($this->db->query($sql)) {
2858 $this->transport_mode_id = $id;
2859 // for supplier
2860 if (get_class($this) == 'Fournisseur') {
2861 $this->transport_mode_supplier_id = $id;
2862 }
2863 return 1;
2864 } else {
2865 dol_syslog(get_class($this).'::setTransportMode Error '.$sql.' - '.$this->db->error());
2866 $this->error = $this->db->error();
2867 return -1;
2868 }
2869 } else {
2870 dol_syslog(get_class($this).'::setTransportMode, status of the object is incompatible');
2871 $this->error = 'Status of the object is incompatible '.$this->statut;
2872 return -2;
2873 }
2874 }
2875
2883 {
2884 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms('.$id.')');
2885 if ($this->statut >= 0 || $this->element == 'societe') {
2886 $fieldname = 'retained_warranty_fk_cond_reglement';
2887
2888 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2889 $sql .= " SET ".$fieldname." = ".((int) $id);
2890 $sql .= ' WHERE rowid='.((int) $this->id);
2891
2892 if ($this->db->query($sql)) {
2893 $this->retained_warranty_fk_cond_reglement = $id;
2894 return 1;
2895 } else {
2896 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms Error '.$sql.' - '.$this->db->error());
2897 $this->error = $this->db->error();
2898 return -1;
2899 }
2900 } else {
2901 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms, status of the object is incompatible');
2902 $this->error = 'Status of the object is incompatible '.$this->statut;
2903 return -2;
2904 }
2905 }
2906
2914 public function setDeliveryAddress($id)
2915 {
2916 $fieldname = 'fk_delivery_address';
2917 if ($this->element == 'delivery' || $this->element == 'shipping') {
2918 $fieldname = 'fk_address';
2919 }
2920
2921 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ".$fieldname." = ".((int) $id);
2922 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
2923
2924 if ($this->db->query($sql)) {
2925 $this->fk_delivery_address = $id;
2926 return 1;
2927 } else {
2928 $this->error = $this->db->error();
2929 dol_syslog(get_class($this).'::setDeliveryAddress Error '.$this->error);
2930 return -1;
2931 }
2932 }
2933
2934
2944 public function setShippingMethod($shipping_method_id, $notrigger = false, $userused = null)
2945 {
2946 global $user;
2947
2948 if (empty($userused)) {
2949 $userused = $user;
2950 }
2951
2952 $error = 0;
2953
2954 if (!$this->table_element) {
2955 dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined", LOG_ERR);
2956 return -1;
2957 }
2958
2959 $this->db->begin();
2960
2961 if ($shipping_method_id < 0) {
2962 $shipping_method_id = 'NULL';
2963 }
2964 dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
2965
2966 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2967 $sql .= " SET fk_shipping_method = ".((int) $shipping_method_id);
2968 $sql .= " WHERE rowid=".((int) $this->id);
2969 $resql = $this->db->query($sql);
2970 if (!$resql) {
2971 dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
2972 $this->error = $this->db->lasterror();
2973 $error++;
2974 } else {
2975 if (!$notrigger) {
2976 // Call trigger
2977 $this->context = array('shippingmethodupdate'=>1);
2978 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused);
2979 if ($result < 0) {
2980 $error++;
2981 }
2982 // End call trigger
2983 }
2984 }
2985 if ($error) {
2986 $this->db->rollback();
2987 return -1;
2988 } else {
2989 $this->shipping_method_id = ($shipping_method_id == 'NULL') ? null : $shipping_method_id;
2990 $this->db->commit();
2991 return 1;
2992 }
2993 }
2994
2995
3002 public function setWarehouse($warehouse_id)
3003 {
3004 if (!$this->table_element) {
3005 dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined", LOG_ERR);
3006 return -1;
3007 }
3008 if ($warehouse_id < 0) {
3009 $warehouse_id = 'NULL';
3010 }
3011 dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
3012
3013 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3014 $sql .= " SET fk_warehouse = ".((int) $warehouse_id);
3015 $sql .= " WHERE rowid=".((int) $this->id);
3016
3017 if ($this->db->query($sql)) {
3018 $this->warehouse_id = ($warehouse_id == 'NULL') ? null : $warehouse_id;
3019 return 1;
3020 } else {
3021 dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
3022 $this->error = $this->db->error();
3023 return 0;
3024 }
3025 }
3026
3027
3035 public function setDocModel($user, $modelpdf)
3036 {
3037 if (!$this->table_element) {
3038 dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined", LOG_ERR);
3039 return -1;
3040 }
3041
3042 $newmodelpdf = dol_trunc($modelpdf, 255);
3043
3044 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3045 $sql .= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
3046 $sql .= " WHERE rowid = ".((int) $this->id);
3047
3048 dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
3049 $resql = $this->db->query($sql);
3050 if ($resql) {
3051 $this->model_pdf = $modelpdf;
3052 return 1;
3053 } else {
3054 dol_print_error($this->db);
3055 return 0;
3056 }
3057 }
3058
3059
3068 public function setBankAccount($fk_account, $notrigger = false, $userused = null)
3069 {
3070 global $user;
3071
3072 if (empty($userused)) {
3073 $userused = $user;
3074 }
3075
3076 $error = 0;
3077
3078 if (!$this->table_element) {
3079 dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined", LOG_ERR);
3080 return -1;
3081 }
3082 $this->db->begin();
3083
3084 if ($fk_account < 0) {
3085 $fk_account = 'NULL';
3086 }
3087 dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
3088
3089 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3090 $sql .= " SET fk_account = ".((int) $fk_account);
3091 $sql .= " WHERE rowid=".((int) $this->id);
3092
3093 $resql = $this->db->query($sql);
3094 if (!$resql) {
3095 dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
3096 $this->error = $this->db->lasterror();
3097 $error++;
3098 } else {
3099 if (!$notrigger) {
3100 // Call trigger
3101 $this->context['bankaccountupdate'] = 1;
3102 $triggerName = strtoupper(get_class($this)).'_MODIFY';
3103 // Special cases
3104 if ($triggerName == 'FACTUREREC_MODIFY') {
3105 $triggerName = 'BILLREC_MODIFY';
3106 }
3107 $result = $this->call_trigger($triggerName, $userused);
3108 if ($result < 0) {
3109 $error++;
3110 }
3111 // End call trigger
3112 }
3113 }
3114 if ($error) {
3115 $this->db->rollback();
3116 return -1;
3117 } else {
3118 $this->fk_account = ($fk_account == 'NULL') ? null : $fk_account;
3119 $this->db->commit();
3120 return 1;
3121 }
3122 }
3123
3124
3125 // TODO: Move line related operations to CommonObjectLine?
3126
3127 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3137 public function line_order($renum = false, $rowidorder = 'ASC', $fk_parent_line = true)
3138 {
3139 // phpcs:enable
3140 if (!$this->table_element_line) {
3141 dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined", LOG_ERR);
3142 return -1;
3143 }
3144 if (!$this->fk_element) {
3145 dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined", LOG_ERR);
3146 return -1;
3147 }
3148
3149 $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3150 if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3151 $fieldposition = 'position';
3152 }
3153
3154 // Count number of lines to reorder (according to choice $renum)
3155 $nl = 0;
3156 $sql = "SELECT count(rowid) FROM ".$this->db->prefix().$this->table_element_line;
3157 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3158 if (!$renum) {
3159 $sql .= " AND " . $fieldposition . " = 0";
3160 }
3161 if ($renum) {
3162 $sql .= " AND " . $fieldposition . " <> 0";
3163 }
3164
3165 dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
3166 $resql = $this->db->query($sql);
3167 if ($resql) {
3168 $row = $this->db->fetch_row($resql);
3169 $nl = $row[0];
3170 } else {
3171 dol_print_error($this->db);
3172 }
3173 if ($nl > 0) {
3174 // The goal of this part is to reorder all lines, with all children lines sharing the same counter that parents.
3175 $rows = array();
3176
3177 // We first search all lines that are parent lines (for multilevel details lines)
3178 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3179 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3180 if ($fk_parent_line) {
3181 $sql .= ' AND fk_parent_line IS NULL';
3182 }
3183 $sql .= " ORDER BY " . $fieldposition . " ASC, rowid " . $rowidorder;
3184
3185 dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
3186 $resql = $this->db->query($sql);
3187 if ($resql) {
3188 $i = 0;
3189 $num = $this->db->num_rows($resql);
3190 while ($i < $num) {
3191 $row = $this->db->fetch_row($resql);
3192 $rows[] = $row[0]; // Add parent line into array rows
3193 $childrens = $this->getChildrenOfLine($row[0]);
3194 if (!empty($childrens)) {
3195 foreach ($childrens as $child) {
3196 array_push($rows, $child);
3197 }
3198 }
3199 $i++;
3200 }
3201
3202 // Now we set a new number for each lines (parent and children with children included into parent tree)
3203 if (!empty($rows)) {
3204 foreach ($rows as $key => $row) {
3205 $this->updateRangOfLine($row, ($key + 1));
3206 }
3207 }
3208 } else {
3209 dol_print_error($this->db);
3210 }
3211 }
3212 return 1;
3213 }
3214
3222 public function getChildrenOfLine($id, $includealltree = 0)
3223 {
3224 $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3225 if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3226 $fieldposition = 'position';
3227 }
3228
3229 $rows = array();
3230
3231 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3232 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3233 $sql .= ' AND fk_parent_line = '.((int) $id);
3234 $sql .= " ORDER BY " . $fieldposition . " ASC";
3235
3236 dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id, LOG_DEBUG);
3237 $resql = $this->db->query($sql);
3238 if ($resql) {
3239 if ($this->db->num_rows($resql) > 0) {
3240 while ($row = $this->db->fetch_row($resql)) {
3241 $rows[] = $row[0];
3242 if (!empty($includealltree)) {
3243 $rows = array_merge($rows, $this->getChildrenOfLine($row[0], $includealltree));
3244 }
3245 }
3246 }
3247 }
3248 return $rows;
3249 }
3250
3251 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3259 public function line_up($rowid, $fk_parent_line = true)
3260 {
3261 // phpcs:enable
3262 $this->line_order(false, 'ASC', $fk_parent_line);
3263
3264 // Get rang of line
3265 $rang = $this->getRangOfLine($rowid);
3266
3267 // Update position of line
3268 $this->updateLineUp($rowid, $rang);
3269 }
3270
3271 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3279 public function line_down($rowid, $fk_parent_line = true)
3280 {
3281 // phpcs:enable
3282 $this->line_order(false, 'ASC', $fk_parent_line);
3283
3284 // Get rang of line
3285 $rang = $this->getRangOfLine($rowid);
3286
3287 // Get max value for rang
3288 $max = $this->line_max();
3289
3290 // Update position of line
3291 $this->updateLineDown($rowid, $rang, $max);
3292 }
3293
3301 public function updateRangOfLine($rowid, $rang)
3302 {
3303 global $hookmanager;
3304 $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3305 if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3306 $fieldposition = 'position';
3307 }
3308
3309 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3310 $sql .= ' WHERE rowid = '.((int) $rowid);
3311
3312 dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
3313 if (!$this->db->query($sql)) {
3314 dol_print_error($this->db);
3315 return -1;
3316 } else {
3317 $parameters=array('rowid'=>$rowid, 'rang'=>$rang, 'fieldposition' => $fieldposition);
3318 $action='';
3319 $reshook = $hookmanager->executeHooks('afterRankOfLineUpdate', $parameters, $this, $action);
3320 return 1;
3321 }
3322 }
3323
3324 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3331 public function line_ajaxorder($rows)
3332 {
3333 // phpcs:enable
3334 $num = count($rows);
3335 for ($i = 0; $i < $num; $i++) {
3336 $this->updateRangOfLine($rows[$i], ($i + 1));
3337 }
3338 }
3339
3347 public function updateLineUp($rowid, $rang)
3348 {
3349 if ($rang > 1) {
3350 $fieldposition = 'rang';
3351 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3352 $fieldposition = 'position';
3353 }
3354
3355 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3356 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3357 $sql .= " AND " . $fieldposition . " = " . ((int) ($rang - 1));
3358 if ($this->db->query($sql)) {
3359 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang - 1));
3360 $sql .= ' WHERE rowid = '.((int) $rowid);
3361 if (!$this->db->query($sql)) {
3362 dol_print_error($this->db);
3363 }
3364 } else {
3365 dol_print_error($this->db);
3366 }
3367 }
3368 }
3369
3378 public function updateLineDown($rowid, $rang, $max)
3379 {
3380 if ($rang < $max) {
3381 $fieldposition = 'rang';
3382 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3383 $fieldposition = 'position';
3384 }
3385
3386 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3387 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3388 $sql .= " AND " . $fieldposition . " = " . ((int) ($rang + 1));
3389 if ($this->db->query($sql)) {
3390 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang + 1));
3391 $sql .= ' WHERE rowid = '.((int) $rowid);
3392 if (!$this->db->query($sql)) {
3393 dol_print_error($this->db);
3394 }
3395 } else {
3396 dol_print_error($this->db);
3397 }
3398 }
3399 }
3400
3407 public function getRangOfLine($rowid)
3408 {
3409 $fieldposition = 'rang';
3410 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3411 $fieldposition = 'position';
3412 }
3413
3414 $sql = "SELECT " . $fieldposition . " FROM ".$this->db->prefix().$this->table_element_line;
3415 $sql .= " WHERE rowid = ".((int) $rowid);
3416
3417 dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
3418 $resql = $this->db->query($sql);
3419 if ($resql) {
3420 $row = $this->db->fetch_row($resql);
3421 return $row[0];
3422 }
3423
3424 return 0;
3425 }
3426
3433 public function getIdOfLine($rang)
3434 {
3435 $fieldposition = 'rang';
3436 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3437 $fieldposition = 'position';
3438 }
3439
3440 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3441 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3442 $sql .= " AND " . $fieldposition . " = ".((int) $rang);
3443 $resql = $this->db->query($sql);
3444 if ($resql) {
3445 $row = $this->db->fetch_row($resql);
3446 return $row[0];
3447 }
3448
3449 return 0;
3450 }
3451
3452 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3459 public function line_max($fk_parent_line = 0)
3460 {
3461 // phpcs:enable
3462 $positionfield = 'rang';
3463 if (in_array($this->table_element, array('bom_bom', 'product_attribute'))) {
3464 $positionfield = 'position';
3465 }
3466
3467 // Search the last rang with fk_parent_line
3468 if ($fk_parent_line) {
3469 $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
3470 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3471 $sql .= " AND fk_parent_line = ".((int) $fk_parent_line);
3472
3473 dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
3474 $resql = $this->db->query($sql);
3475 if ($resql) {
3476 $row = $this->db->fetch_row($resql);
3477 if (!empty($row[0])) {
3478 return $row[0];
3479 } else {
3480 return $this->getRangOfLine($fk_parent_line);
3481 }
3482 }
3483 } else {
3484 // If not, search the last rang of element
3485 $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
3486 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3487
3488 dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
3489 $resql = $this->db->query($sql);
3490 if ($resql) {
3491 $row = $this->db->fetch_row($resql);
3492 return $row[0];
3493 }
3494 }
3495
3496 return 0;
3497 }
3498
3499 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3506 public function update_ref_ext($ref_ext)
3507 {
3508 // phpcs:enable
3509 if (!$this->table_element) {
3510 dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
3511 return -1;
3512 }
3513
3514 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3515 $sql .= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
3516 $sql .= " WHERE ".(isset($this->table_rowid) ? $this->table_rowid : 'rowid')." = ".((int) $this->id);
3517
3518 dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
3519 if ($this->db->query($sql)) {
3520 $this->ref_ext = $ref_ext;
3521 return 1;
3522 } else {
3523 $this->error = $this->db->error();
3524 return -1;
3525 }
3526 }
3527
3528 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3537 public function update_note($note, $suffix = '', $notrigger = 0)
3538 {
3539 // phpcs:enable
3540 global $user;
3541
3542 if (!$this->table_element) {
3543 $this->error = 'update_note was called on objet with property table_element not defined';
3544 dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
3545 return -1;
3546 }
3547 if (!in_array($suffix, array('', '_public', '_private'))) {
3548 $this->error = 'update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
3549 dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
3550 return -2;
3551 }
3552
3553 $newsuffix = $suffix;
3554
3555 // Special cas
3556 if ($this->table_element == 'product' && $newsuffix == '_private') {
3557 $newsuffix = '';
3558 }
3559 if (in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
3560 $fieldusermod = "fk_user_mod";
3561 } elseif ($this->table_element == 'ecm_files') {
3562 $fieldusermod = "fk_user_m";
3563 } else {
3564 $fieldusermod = "fk_user_modif";
3565 }
3566 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3567 $sql .= " SET note".$newsuffix." = ".(!empty($note) ? ("'".$this->db->escape($note)."'") : "NULL");
3568 $sql .= ", ".$fieldusermod." = ".((int) $user->id);
3569 $sql .= " WHERE rowid = ".((int) $this->id);
3570
3571 dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
3572 if ($this->db->query($sql)) {
3573 if ($suffix == '_public') {
3574 $this->note_public = $note;
3575 } elseif ($suffix == '_private') {
3576 $this->note_private = $note;
3577 } else {
3578 $this->note = $note; // deprecated
3579 $this->note_private = $note;
3580 }
3581 if (empty($notrigger)) {
3582 switch ($this->element) {
3583 case 'societe':
3584 $trigger_name = 'COMPANY_MODIFY';
3585 break;
3586 case 'commande':
3587 $trigger_name = 'ORDER_MODIFY';
3588 break;
3589 case 'facture':
3590 $trigger_name = 'BILL_MODIFY';
3591 break;
3592 case 'invoice_supplier':
3593 $trigger_name = 'BILL_SUPPLIER_MODIFY';
3594 break;
3595 case 'facturerec':
3596 $trigger_name = 'BILLREC_MODIFIY';
3597 break;
3598 case 'expensereport':
3599 $trigger_name = 'EXPENSE_REPORT_MODIFY';
3600 break;
3601 default:
3602 $trigger_name = strtoupper($this->element) . '_MODIFY';
3603 }
3604 $ret = $this->call_trigger($trigger_name, $user);
3605 if ($ret < 0) {
3606 return -1;
3607 }
3608 }
3609 return 1;
3610 } else {
3611 $this->error = $this->db->lasterror();
3612 return -1;
3613 }
3614 }
3615
3616 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3625 public function update_note_public($note)
3626 {
3627 // phpcs:enable
3628 return $this->update_note($note, '_public');
3629 }
3630
3631 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3642 public function update_price($exclspec = 0, $roundingadjust = 'none', $nodatabaseupdate = 0, $seller = null)
3643 {
3644 // phpcs:enable
3645 global $conf, $hookmanager, $action;
3646
3647 $parameters = array('exclspec' => $exclspec, 'roundingadjust' => $roundingadjust, 'nodatabaseupdate' => $nodatabaseupdate, 'seller' => $seller);
3648 $reshook = $hookmanager->executeHooks('updateTotalPrice', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3649 if ($reshook > 0) {
3650 return 1; // replacement code
3651 } elseif ($reshook < 0) {
3652 return -1; // failure
3653 } // reshook = 0 => execute normal code
3654
3655 // Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
3656 $MODULE = "";
3657 if ($this->element == 'propal') {
3658 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
3659 } elseif ($this->element == 'commande' || $this->element == 'order') {
3660 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
3661 } elseif ($this->element == 'facture' || $this->element == 'invoice') {
3662 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
3663 } elseif ($this->element == 'facture_fourn' || $this->element == 'supplier_invoice' || $this->element == 'invoice_supplier' || $this->element == 'invoice_supplier_rec') {
3664 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
3665 } elseif ($this->element == 'order_supplier' || $this->element == 'supplier_order') {
3666 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
3667 } elseif ($this->element == 'supplier_proposal') {
3668 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
3669 }
3670
3671 if (!empty($MODULE)) {
3672 if (getDolGlobalString($MODULE)) {
3673 $modsactivated = explode(',', getDolGlobalString($MODULE));
3674 foreach ($modsactivated as $mod) {
3675 if (isModEnabled($mod)) {
3676 return 1; // update was disabled by specific setup
3677 }
3678 }
3679 }
3680 }
3681
3682 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3683
3684 $forcedroundingmode = $roundingadjust;
3685 if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) {
3686 $forcedroundingmode = getDolGlobalString('MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND');
3687 } elseif ($forcedroundingmode == 'auto') {
3688 $forcedroundingmode = '0';
3689 }
3690
3691 $error = 0;
3692
3693 $multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
3694
3695 // Define constants to find lines to sum (field name int the table_element_line not into table_element)
3696 $fieldtva = 'total_tva';
3697 $fieldlocaltax1 = 'total_localtax1';
3698 $fieldlocaltax2 = 'total_localtax2';
3699 $fieldup = 'subprice';
3700 if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
3701 $fieldtva = 'tva';
3702 $fieldup = 'pu_ht';
3703 }
3704 if ($this->element == 'invoice_supplier_rec') {
3705 $fieldup = 'pu_ht';
3706 }
3707 if ($this->element == 'expensereport') {
3708 $fieldup = 'value_unit';
3709 }
3710
3711 $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,";
3712 $sql .= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
3713 if ($this->table_element_line == 'facturedet') {
3714 $sql .= ', situation_percent';
3715 }
3716 $sql .= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
3717 $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
3718 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3719 if ($exclspec) {
3720 $product_field = 'product_type';
3721 if ($this->table_element_line == 'contratdet') {
3722 $product_field = ''; // contratdet table has no product_type field
3723 }
3724 if ($product_field) {
3725 $sql .= " AND ".$product_field." <> 9";
3726 }
3727 }
3728 $sql .= ' ORDER by rowid'; // We want to be sure to always use same order of line to not change lines differently when option MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND is used
3729
3730 dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3731
3732 $resql = $this->db->query($sql);
3733 if ($resql) {
3734 $this->total_ht = 0;
3735 $this->total_tva = 0;
3736 $this->total_localtax1 = 0;
3737 $this->total_localtax2 = 0;
3738 $this->total_ttc = 0;
3739 $total_ht_by_vats = array();
3740 $total_tva_by_vats = array();
3741 $total_ttc_by_vats = array();
3742 $this->multicurrency_total_ht = 0;
3743 $this->multicurrency_total_tva = 0;
3744 $this->multicurrency_total_ttc = 0;
3745
3746 $this->db->begin();
3747
3748 $num = $this->db->num_rows($resql);
3749 $i = 0;
3750 while ($i < $num) {
3751 $obj = $this->db->fetch_object($resql);
3752
3753 // Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
3754 $parameters = array('fk_element' => $obj->rowid);
3755 $reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3756
3757 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'
3758 // This part of code is to fix data. We should not call it too often.
3759 $localtax_array = array($obj->localtax1_type, $obj->localtax1_tx, $obj->localtax2_type, $obj->localtax2_tx);
3760 $tmpcal = calcul_price_total($obj->qty, $obj->up, $obj->remise_percent, $obj->vatrate, $obj->localtax1_tx, $obj->localtax2_tx, 0, 'HT', $obj->info_bits, $obj->product_type, $seller, $localtax_array, (isset($obj->situation_percent) ? $obj->situation_percent : 100), $multicurrency_tx);
3761
3762 $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.
3763 $diff_on_current_total = price2num($obj->total_ttc - $obj->total_ht - $obj->total_tva - $obj->total_localtax1 - $obj->total_localtax2, 'MT', 1);
3764 //var_dump($obj->total_ht.' '.$obj->total_tva.' '.$obj->total_localtax1.' '.$obj->total_localtax2.' => '.$obj->total_ttc);
3765 //var_dump($diff_when_using_price_ht.' '.$diff_on_current_total);
3766
3767 if ($diff_on_current_total) {
3768 // This should not happen, we should always have in table: total_ttc = total_ht + total_vat + total_localtax1 + total_localtax2
3769 $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldtva." = ".price2num((float) $tmpcal[1]).", total_ttc = ".price2num((float) $tmpcal[2])." WHERE rowid = ".((int) $obj->rowid);
3770 dol_syslog('We found unconsistent 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);
3771 $resqlfix = $this->db->query($sqlfix);
3772 if (!$resqlfix) {
3773 dol_print_error($this->db, 'Failed to update line');
3774 }
3775 $obj->total_tva = $tmpcal[1];
3776 $obj->total_ttc = $tmpcal[2];
3777 } elseif ($diff_when_using_price_ht && $roundingadjust == '0') {
3778 // After calculation from HT, total is consistent but we have found a difference between VAT part in calculation and into database and
3779 // we ask to force the use of rounding on line (like done on calculation) so we force update of line
3780 $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldtva." = ".price2num((float) $tmpcal[1]).", total_ttc = ".price2num((float) $tmpcal[2])." WHERE rowid = ".((int) $obj->rowid);
3781 dol_syslog('We found a line with different rounding data into detailed line (diff_when_using_price_ht = '.$diff_when_using_price_ht.' and diff_on_current_total = '.$diff_on_current_total.') for line rowid = '.$obj->rowid." (total vat of line calculated=".$tmpcal[1].", database=".$obj->total_tva."). We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix);
3782 $resqlfix = $this->db->query($sqlfix);
3783 if (!$resqlfix) {
3784 dol_print_error($this->db, 'Failed to update line');
3785 }
3786 $obj->total_tva = $tmpcal[1];
3787 $obj->total_ttc = $tmpcal[2];
3788 }
3789 }
3790
3791 $this->total_ht += $obj->total_ht; // The field visible at end of line detail
3792 $this->total_tva += $obj->total_tva;
3793 $this->total_localtax1 += $obj->total_localtax1;
3794 $this->total_localtax2 += $obj->total_localtax2;
3795 $this->total_ttc += $obj->total_ttc;
3796 $this->multicurrency_total_ht += $obj->multicurrency_total_ht; // The field visible at end of line detail
3797 $this->multicurrency_total_tva += $obj->multicurrency_total_tva;
3798 $this->multicurrency_total_ttc += $obj->multicurrency_total_ttc;
3799
3800 if (!isset($total_ht_by_vats[$obj->vatrate])) {
3801 $total_ht_by_vats[$obj->vatrate] = 0;
3802 }
3803 if (!isset($total_tva_by_vats[$obj->vatrate])) {
3804 $total_tva_by_vats[$obj->vatrate] = 0;
3805 }
3806 if (!isset($total_ttc_by_vats[$obj->vatrate])) {
3807 $total_ttc_by_vats[$obj->vatrate] = 0;
3808 }
3809 $total_ht_by_vats[$obj->vatrate] += $obj->total_ht;
3810 $total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
3811 $total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
3812
3813 if ($forcedroundingmode == '1') { // Check if we need adjustement onto line for vat. TODO This works on the company currency but not on foreign currency
3814 $tmpvat = price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
3815 $diff = price2num($total_tva_by_vats[$obj->vatrate] - $tmpvat, 'MT', 1);
3816 //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";
3817 if ($diff) {
3818 if (abs($diff) > (10 * pow(10, -1 * getDolGlobalInt('MAIN_MAX_DECIMALS_TOT', 0)))) {
3819 // If error is more than 10 times the accurancy of rounding. This should not happen.
3820 $errmsg = 'A rounding difference was detected into TOTAL but is too high to be corrected. Some data in your lines may be corrupted. Try to edit each line manually to fix this before restarting.';
3821 dol_syslog($errmsg, LOG_WARNING);
3822 $this->error = $errmsg;
3823 $error++;
3824 break;
3825 }
3826 $sqlfix = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldtva." = ".price2num($obj->total_tva - $diff).", total_ttc = ".price2num($obj->total_ttc - $diff)." WHERE rowid = ".((int) $obj->rowid);
3827 dol_syslog('We found a difference of '.$diff.' for line rowid = '.$obj->rowid.". We fix the total_vat and total_ttc of line by running sqlfix = ".$sqlfix);
3828
3829 $resqlfix = $this->db->query($sqlfix);
3830
3831 if (!$resqlfix) {
3832 dol_print_error($this->db, 'Failed to update line');
3833 }
3834
3835 $this->total_tva = (float) price2num($this->total_tva - $diff, '', 1);
3836 $this->total_ttc = (float) price2num($this->total_ttc - $diff, '', 1);
3837 $total_tva_by_vats[$obj->vatrate] = (float) price2num($total_tva_by_vats[$obj->vatrate] - $diff, '', 1);
3838 $total_ttc_by_vats[$obj->vatrate] = (float) price2num($total_ttc_by_vats[$obj->vatrate] - $diff, '', 1);
3839 }
3840 }
3841
3842 $i++;
3843 }
3844
3845 // Add revenue stamp to total
3846 $this->total_ttc += isset($this->revenuestamp) ? $this->revenuestamp : 0;
3847 $this->multicurrency_total_ttc += isset($this->revenuestamp) ? ($this->revenuestamp * $multicurrency_tx) : 0;
3848
3849 // Situations totals
3850 if (!empty($this->situation_cycle_ref) && !empty($this->situation_counter) && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits')) {
3851 include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
3852 if ($this->type != Facture::TYPE_CREDIT_NOTE) { // @phpstan-ignore-line
3853 $prev_sits = $this->get_prev_sits();
3854
3855 foreach ($prev_sits as $sit) { // $sit is an object Facture loaded with a fetch.
3856 $this->total_ht -= $sit->total_ht;
3857 $this->total_tva -= $sit->total_tva;
3858 $this->total_localtax1 -= $sit->total_localtax1;
3859 $this->total_localtax2 -= $sit->total_localtax2;
3860 $this->total_ttc -= $sit->total_ttc;
3861 $this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
3862 $this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
3863 $this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
3864 }
3865 }
3866 }
3867
3868 // Clean total
3869 $this->total_ht = (float) price2num($this->total_ht);
3870 $this->total_tva = (float) price2num($this->total_tva);
3871 $this->total_localtax1 = (float) price2num($this->total_localtax1);
3872 $this->total_localtax2 = (float) price2num($this->total_localtax2);
3873 $this->total_ttc = (float) price2num($this->total_ttc);
3874
3875 $this->db->free($resql);
3876
3877 // Now update global fields total_ht, total_ttc, total_tva, total_localtax1, total_localtax2, multicurrency_total_* of main object
3878 $fieldht = 'total_ht';
3879 $fieldtva = 'tva';
3880 $fieldlocaltax1 = 'localtax1';
3881 $fieldlocaltax2 = 'localtax2';
3882 $fieldttc = 'total_ttc';
3883 // Specific code for backward compatibility with old field names
3884 if (in_array($this->element, array('propal', 'commande', 'facture', 'facturerec', 'supplier_proposal', 'order_supplier', 'facture_fourn', 'invoice_supplier', 'invoice_supplier_rec', 'expensereport'))) {
3885 $fieldtva = 'total_tva';
3886 }
3887
3888 if (!$error && empty($nodatabaseupdate)) {
3889 $sql = "UPDATE ".$this->db->prefix().$this->table_element.' SET';
3890 $sql .= " ".$fieldht." = ".((float) price2num($this->total_ht, 'MT', 1)).",";
3891 $sql .= " ".$fieldtva." = ".((float) price2num($this->total_tva, 'MT', 1)).",";
3892 $sql .= " ".$fieldlocaltax1." = ".((float) price2num($this->total_localtax1, 'MT', 1)).",";
3893 $sql .= " ".$fieldlocaltax2." = ".((float) price2num($this->total_localtax2, 'MT', 1)).",";
3894 $sql .= " ".$fieldttc." = ".((float) price2num($this->total_ttc, 'MT', 1));
3895 $sql .= ", multicurrency_total_ht = ".((float) price2num($this->multicurrency_total_ht, 'MT', 1));
3896 $sql .= ", multicurrency_total_tva = ".((float) price2num($this->multicurrency_total_tva, 'MT', 1));
3897 $sql .= ", multicurrency_total_ttc = ".((float) price2num($this->multicurrency_total_ttc, 'MT', 1));
3898 $sql .= " WHERE rowid = ".((int) $this->id);
3899
3900 dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3901 $resql = $this->db->query($sql);
3902
3903 if (!$resql) {
3904 $error++;
3905 $this->error = $this->db->lasterror();
3906 $this->errors[] = $this->db->lasterror();
3907 }
3908 }
3909
3910 if (!$error) {
3911 $this->db->commit();
3912 return 1;
3913 } else {
3914 $this->db->rollback();
3915 return -1;
3916 }
3917 } else {
3918 dol_print_error($this->db, 'Bad request in update_price');
3919 return -1;
3920 }
3921 }
3922
3923 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3934 public function add_object_linked($origin = null, $origin_id = null, $f_user = null, $notrigger = 0)
3935 {
3936 // phpcs:enable
3937 global $user, $hookmanager, $action;
3938 $origin = (!empty($origin) ? $origin : $this->origin);
3939 $origin_id = (!empty($origin_id) ? $origin_id : $this->origin_id);
3940 $f_user = isset($f_user) ? $f_user : $user;
3941
3942 // Special case
3943 if ($origin == 'order') {
3944 $origin = 'commande';
3945 }
3946 if ($origin == 'invoice') {
3947 $origin = 'facture';
3948 }
3949 if ($origin == 'invoice_template') {
3950 $origin = 'facturerec';
3951 }
3952 if ($origin == 'supplierorder') {
3953 $origin = 'order_supplier';
3954 }
3955
3956 // Elements of the core modules which have `$module` property but may to which we don't want to prefix module part to the element name for finding the linked object in llx_element_element.
3957 // It's because an entry for this element may be exist in llx_element_element before this modification (version <=14.2) and ave named only with their element name in fk_source or fk_target.
3958 $coremodule = array('knowledgemanagement', 'partnership', 'workstation', 'ticket', 'recruitment', 'eventorganization', 'asset');
3959 // Add module part to target type if object has $module property and isn't in core modules.
3960 $targettype = ((!empty($this->module) && ! in_array($this->module, $coremodule)) ? $this->module.'_' : '').$this->element;
3961
3962 $parameters = array('targettype'=>$targettype);
3963 // Hook for explicitly set the targettype if it must be differtent than $this->element
3964 $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3965 if ($reshook > 0) {
3966 if (!empty($hookmanager->resArray['targettype'])) {
3967 $targettype = $hookmanager->resArray['targettype'];
3968 }
3969 }
3970
3971 $this->db->begin();
3972 $error = 0;
3973
3974 $sql = "INSERT INTO " . $this->db->prefix() . "element_element (";
3975 $sql .= "fk_source";
3976 $sql .= ", sourcetype";
3977 $sql .= ", fk_target";
3978 $sql .= ", targettype";
3979 $sql .= ") VALUES (";
3980 $sql .= ((int) $origin_id);
3981 $sql .= ", '" . $this->db->escape($origin) . "'";
3982 $sql .= ", " . ((int) $this->id);
3983 $sql .= ", '" . $this->db->escape($targettype) . "'";
3984 $sql .= ")";
3985
3986 dol_syslog(get_class($this) . "::add_object_linked", LOG_DEBUG);
3987 if ($this->db->query($sql)) {
3988 if (!$notrigger) {
3989 // Call trigger
3990 $this->context['link_origin'] = $origin;
3991 $this->context['link_origin_id'] = $origin_id;
3992 $result = $this->call_trigger('OBJECT_LINK_INSERT', $f_user);
3993 if ($result < 0) {
3994 $error++;
3995 }
3996 // End call triggers
3997 }
3998 } else {
3999 $this->error = $this->db->lasterror();
4000 $error++;
4001 }
4002
4003 if (!$error) {
4004 $this->db->commit();
4005 return 1;
4006 } else {
4007 $this->db->rollback();
4008 return 0;
4009 }
4010 }
4011
4034 public function fetchObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1)
4035 {
4036 global $conf, $hookmanager, $action;
4037
4038 // Important for pdf generation time reduction
4039 // This boolean is true if $this->linkedObjects has already been loaded with all objects linked without filter
4040 // If you need to force the reload, you can call clearObjectLinkedCache() before calling fetchObjectLinked()
4041 if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
4042 return 1;
4043 }
4044
4045 $this->linkedObjectsIds = array();
4046 $this->linkedObjects = array();
4047
4048 $justsource = false;
4049 $justtarget = false;
4050 $withtargettype = false;
4051 $withsourcetype = false;
4052
4053 $parameters = array('sourcetype'=>$sourcetype, 'sourceid'=>$sourceid, 'targettype'=>$targettype, 'targetid'=>$targetid);
4054 // Hook for explicitly set the targettype if it must be differtent than $this->element
4055 $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4056 if ($reshook > 0) {
4057 if (!empty($hookmanager->resArray['sourcetype'])) {
4058 $sourcetype = $hookmanager->resArray['sourcetype'];
4059 }
4060 if (!empty($hookmanager->resArray['sourceid'])) {
4061 $sourceid = $hookmanager->resArray['sourceid'];
4062 }
4063 if (!empty($hookmanager->resArray['targettype'])) {
4064 $targettype = $hookmanager->resArray['targettype'];
4065 }
4066 if (!empty($hookmanager->resArray['targetid'])) {
4067 $targetid = $hookmanager->resArray['targetid'];
4068 }
4069 }
4070
4071 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid)) {
4072 $justsource = true; // the source (id and type) is a search criteria
4073 if (!empty($targettype)) {
4074 $withtargettype = true;
4075 }
4076 }
4077 if (!empty($targetid) && !empty($targettype) && empty($sourceid)) {
4078 $justtarget = true; // the target (id and type) is a search criteria
4079 if (!empty($sourcetype)) {
4080 $withsourcetype = true;
4081 }
4082 }
4083
4084 $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
4085 $targetid = (!empty($targetid) ? $targetid : $this->id);
4086 $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
4087 $targettype = (!empty($targettype) ? $targettype : $this->element);
4088
4089 /*if (empty($sourceid) && empty($targetid))
4090 {
4091 dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
4092 return -1;
4093 }*/
4094
4095 // Links between objects are stored in table element_element
4096 $sql = "SELECT rowid, fk_source, sourcetype, fk_target, targettype";
4097 $sql .= " FROM ".$this->db->prefix()."element_element";
4098 $sql .= " WHERE ";
4099 if ($justsource || $justtarget) {
4100 if ($justsource) {
4101 $sql .= "fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."'";
4102 if ($withtargettype) {
4103 $sql .= " AND targettype = '".$this->db->escape($targettype)."'";
4104 }
4105 } elseif ($justtarget) {
4106 $sql .= "fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."'";
4107 if ($withsourcetype) {
4108 $sql .= " AND sourcetype = '".$this->db->escape($sourcetype)."'";
4109 }
4110 }
4111 } else {
4112 $sql .= "(fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."')";
4113 $sql .= " ".$clause." (fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."')";
4114 if ($loadalsoobjects && $this->id > 0 && $sourceid == $this->id && $sourcetype == $this->element && $targetid == $this->id && $targettype == $this->element && $clause == 'OR') {
4115 $this->linkedObjectsFullLoaded[$this->id] = true;
4116 }
4117 }
4118 $sql .= " ORDER BY ".$orderby;
4119
4120 dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
4121 $resql = $this->db->query($sql);
4122 if ($resql) {
4123 $num = $this->db->num_rows($resql);
4124 $i = 0;
4125 while ($i < $num) {
4126 $obj = $this->db->fetch_object($resql);
4127 if ($justsource || $justtarget) {
4128 if ($justsource) {
4129 $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
4130 } elseif ($justtarget) {
4131 $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
4132 }
4133 } else {
4134 if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype) {
4135 $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
4136 }
4137 if ($obj->fk_target == $targetid && $obj->targettype == $targettype) {
4138 $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
4139 }
4140 }
4141 $i++;
4142 }
4143
4144 if (!empty($this->linkedObjectsIds)) {
4145 $tmparray = $this->linkedObjectsIds;
4146 foreach ($tmparray as $objecttype => $objectids) { // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
4147 // Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
4148 $module = $element = $subelement = $objecttype;
4149 $regs = array();
4150 if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
4151 && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
4152 $module = $element = $regs[1];
4153 $subelement = $regs[2];
4154 }
4155
4156 $classpath = $element.'/class';
4157 // To work with non standard classpath or module name
4158 if ($objecttype == 'facture') {
4159 $classpath = 'compta/facture/class';
4160 } elseif ($objecttype == 'facturerec') {
4161 $classpath = 'compta/facture/class';
4162 $module = 'facture';
4163 } elseif ($objecttype == 'propal') {
4164 $classpath = 'comm/propal/class';
4165 } elseif ($objecttype == 'supplier_proposal') {
4166 $classpath = 'supplier_proposal/class';
4167 } elseif ($objecttype == 'shipping') {
4168 $classpath = 'expedition/class';
4169 $subelement = 'expedition';
4170 $module = 'expedition';
4171 } elseif ($objecttype == 'delivery') {
4172 $classpath = 'delivery/class';
4173 $subelement = 'delivery';
4174 $module = 'delivery_note';
4175 } elseif ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier') {
4176 $classpath = 'fourn/class';
4177 $module = 'fournisseur';
4178 } elseif ($objecttype == 'fichinter') {
4179 $classpath = 'fichinter/class';
4180 $subelement = 'fichinter';
4181 $module = 'ficheinter';
4182 } elseif ($objecttype == 'subscription') {
4183 $classpath = 'adherents/class';
4184 $module = 'adherent';
4185 } elseif ($objecttype == 'contact') {
4186 $module = 'societe';
4187 }
4188 // Set classfile
4189 $classfile = strtolower($subelement);
4190 $classname = ucfirst($subelement);
4191
4192 if ($objecttype == 'order') {
4193 $classfile = 'commande';
4194 $classname = 'Commande';
4195 } elseif ($objecttype == 'invoice_supplier') {
4196 $classfile = 'fournisseur.facture';
4197 $classname = 'FactureFournisseur';
4198 } elseif ($objecttype == 'order_supplier') {
4199 $classfile = 'fournisseur.commande';
4200 $classname = 'CommandeFournisseur';
4201 } elseif ($objecttype == 'supplier_proposal') {
4202 $classfile = 'supplier_proposal';
4203 $classname = 'SupplierProposal';
4204 } elseif ($objecttype == 'facturerec') {
4205 $classfile = 'facture-rec';
4206 $classname = 'FactureRec';
4207 } elseif ($objecttype == 'subscription') {
4208 $classfile = 'subscription';
4209 $classname = 'Subscription';
4210 } elseif ($objecttype == 'project' || $objecttype == 'projet') {
4211 $classpath = 'projet/class';
4212 $classfile = 'project';
4213 $classname = 'Project';
4214 } elseif ($objecttype == 'conferenceorboothattendee') {
4215 $classpath = 'eventorganization/class';
4216 $classfile = 'conferenceorboothattendee';
4217 $classname = 'ConferenceOrBoothAttendee';
4218 $module = 'eventorganization';
4219 } elseif ($objecttype == 'conferenceorbooth') {
4220 $classpath = 'eventorganization/class';
4221 $classfile = 'conferenceorbooth';
4222 $classname = 'ConferenceOrBooth';
4223 $module = 'eventorganization';
4224 } elseif ($objecttype == 'mo') {
4225 $classpath = 'mrp/class';
4226 $classfile = 'mo';
4227 $classname = 'Mo';
4228 $module = 'mrp';
4229 }
4230
4231 // Here $module, $classfile and $classname are set, we can use them.
4232 if (isModEnabled($module) && (($element != $this->element) || $alsosametype)) {
4233 if ($loadalsoobjects && (is_numeric($loadalsoobjects) || ($loadalsoobjects === $objecttype))) {
4234 dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
4235 //print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
4236 if (class_exists($classname)) {
4237 foreach ($objectids as $i => $objectid) { // $i is rowid into llx_element_element
4238 $object = new $classname($this->db);
4239 $ret = $object->fetch($objectid);
4240 if ($ret >= 0) {
4241 $this->linkedObjects[$objecttype][$i] = $object;
4242 }
4243 }
4244 }
4245 }
4246 } else {
4247 unset($this->linkedObjectsIds[$objecttype]);
4248 }
4249 }
4250 }
4251 return 1;
4252 } else {
4253 dol_print_error($this->db);
4254 return -1;
4255 }
4256 }
4257
4264 public function clearObjectLinkedCache()
4265 {
4266 if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
4267 unset($this->linkedObjectsFullLoaded[$this->id]);
4268 }
4269
4270 return 1;
4271 }
4272
4285 public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $f_user = null, $notrigger = 0)
4286 {
4287 global $user;
4288 $updatesource = false;
4289 $updatetarget = false;
4290 $f_user = isset($f_user) ? $f_user : $user;
4291
4292 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4293 $updatesource = true;
4294 } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4295 $updatetarget = true;
4296 }
4297
4298 $this->db->begin();
4299 $error = 0;
4300
4301 $sql = "UPDATE " . $this->db->prefix() . "element_element SET ";
4302 if ($updatesource) {
4303 $sql .= "fk_source = " . ((int) $sourceid);
4304 $sql .= ", sourcetype = '" . $this->db->escape($sourcetype) . "'";
4305 $sql .= " WHERE fk_target = " . ((int) $this->id);
4306 $sql .= " AND targettype = '" . $this->db->escape($this->element) . "'";
4307 } elseif ($updatetarget) {
4308 $sql .= "fk_target = " . ((int) $targetid);
4309 $sql .= ", targettype = '" . $this->db->escape($targettype) . "'";
4310 $sql .= " WHERE fk_source = " . ((int) $this->id);
4311 $sql .= " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4312 }
4313
4314 dol_syslog(get_class($this) . "::updateObjectLinked", LOG_DEBUG);
4315 if ($this->db->query($sql)) {
4316 if (!$notrigger) {
4317 // Call trigger
4318 $this->context['link_source_id'] = $sourceid;
4319 $this->context['link_source_type'] = $sourcetype;
4320 $this->context['link_target_id'] = $targetid;
4321 $this->context['link_target_type'] = $targettype;
4322 $result = $this->call_trigger('OBJECT_LINK_MODIFY', $f_user);
4323 if ($result < 0) {
4324 $error++;
4325 }
4326 // End call triggers
4327 }
4328 } else {
4329 $this->error = $this->db->lasterror();
4330 $error++;
4331 }
4332
4333 if (!$error) {
4334 $this->db->commit();
4335 return 1;
4336 } else {
4337 $this->db->rollback();
4338 return -1;
4339 }
4340 }
4341
4355 public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = 0, $f_user = null, $notrigger = 0)
4356 {
4357 global $user;
4358 $deletesource = false;
4359 $deletetarget = false;
4360 $f_user = isset($f_user) ? $f_user : $user;
4361
4362 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4363 $deletesource = true;
4364 } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4365 $deletetarget = true;
4366 }
4367
4368 $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
4369 $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
4370 $targetid = (!empty($targetid) ? $targetid : $this->id);
4371 $targettype = (!empty($targettype) ? $targettype : $this->element);
4372 $this->db->begin();
4373 $error = 0;
4374
4375 if (!$notrigger) {
4376 // Call trigger
4377 $this->context['link_id'] = $rowid;
4378 $this->context['link_source_id'] = $sourceid;
4379 $this->context['link_source_type'] = $sourcetype;
4380 $this->context['link_target_id'] = $targetid;
4381 $this->context['link_target_type'] = $targettype;
4382 $result = $this->call_trigger('OBJECT_LINK_DELETE', $f_user);
4383 if ($result < 0) {
4384 $error++;
4385 }
4386 // End call triggers
4387 }
4388
4389 if (!$error) {
4390 $sql = "DELETE FROM " . $this->db->prefix() . "element_element";
4391 $sql .= " WHERE";
4392 if ($rowid > 0) {
4393 $sql .= " rowid = " . ((int) $rowid);
4394 } else {
4395 if ($deletesource) {
4396 $sql .= " fk_source = " . ((int) $sourceid) . " AND sourcetype = '" . $this->db->escape($sourcetype) . "'";
4397 $sql .= " AND fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "'";
4398 } elseif ($deletetarget) {
4399 $sql .= " fk_target = " . ((int) $targetid) . " AND targettype = '" . $this->db->escape($targettype) . "'";
4400 $sql .= " AND fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4401 } else {
4402 $sql .= " (fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "')";
4403 $sql .= " OR";
4404 $sql .= " (fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "')";
4405 }
4406 }
4407
4408 dol_syslog(get_class($this) . "::deleteObjectLinked", LOG_DEBUG);
4409 if (!$this->db->query($sql)) {
4410 $this->error = $this->db->lasterror();
4411 $this->errors[] = $this->error;
4412 $error++;
4413 }
4414 }
4415
4416 if (!$error) {
4417 $this->db->commit();
4418 return 1;
4419 } else {
4420 $this->db->rollback();
4421 return 0;
4422 }
4423 }
4424
4434 public static function getAllItemsLinkedByObjectID($fk_object_where, $field_select, $field_where, $table_element)
4435 {
4436 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4437 return -1;
4438 }
4439 if (!preg_match('/^[_a-zA-Z0-9]+$/', $field_select)) {
4440 dol_syslog('Invalid value $field_select for parameter '.$field_select.' in call to getAllItemsLinkedByObjectID(). Must be a single field name.', LOG_ERR);
4441 }
4442
4443 global $db;
4444
4445 $sql = "SELECT ".$field_select." FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4446 $resql = $db->query($sql);
4447
4448 $TRes = array();
4449 if (!empty($resql)) {
4450 while ($res = $db->fetch_object($resql)) {
4451 $TRes[] = $res->{$field_select};
4452 }
4453 }
4454
4455 return $TRes;
4456 }
4457
4466 public static function getCountOfItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
4467 {
4468 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4469 return -1;
4470 }
4471
4472 global $db;
4473
4474 $sql = "SELECT COUNT(*) as nb FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4475 $resql = $db->query($sql);
4476 $n = 0;
4477 if ($resql) {
4478 $res = $db->fetch_object($resql);
4479 if ($res) {
4480 $n = $res->nb;
4481 }
4482 }
4483
4484 return $n;
4485 }
4486
4495 public static function deleteAllItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
4496 {
4497 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4498 return -1;
4499 }
4500
4501 global $db;
4502
4503 $sql = "DELETE FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4504 $resql = $db->query($sql);
4505
4506 if (empty($resql)) {
4507 return 0;
4508 }
4509
4510 return 1;
4511 }
4512
4523 public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '', $fieldstatus = 'fk_statut')
4524 {
4525 global $user, $langs, $conf;
4526
4527 $savElementId = $elementId; // To be used later to know if we were using the method using the id of this or not.
4528
4529 $elementId = (!empty($elementId) ? $elementId : $this->id);
4530 $elementTable = (!empty($elementType) ? $elementType : $this->table_element);
4531
4532 $this->db->begin();
4533
4534 if ($elementTable == 'facture_rec') {
4535 $fieldstatus = "suspended";
4536 }
4537 if ($elementTable == 'mailing') {
4538 $fieldstatus = "statut";
4539 }
4540 if ($elementTable == 'cronjob') {
4541 $fieldstatus = "status";
4542 }
4543 if ($elementTable == 'user') {
4544 $fieldstatus = "statut";
4545 }
4546 if ($elementTable == 'expensereport') {
4547 $fieldstatus = "fk_statut";
4548 }
4549 if ($elementTable == 'commande_fournisseur_dispatch') {
4550 $fieldstatus = "status";
4551 }
4552 if ($elementTable == 'prelevement_bons') {
4553 $fieldstatus = "statut";
4554 }
4555 if (isset($this->fields) && is_array($this->fields) && array_key_exists('status', $this->fields)) {
4556 $fieldstatus = 'status';
4557 }
4558
4559 $sql = "UPDATE ".$this->db->prefix().$elementTable;
4560 $sql .= " SET ".$fieldstatus." = ".((int) $status);
4561 // If status = 1 = validated, update also fk_user_valid
4562 // TODO Replace the test on $elementTable by doing a test on existence of the field in $this->fields
4563 if ($status == 1 && in_array($elementTable, array('expensereport', 'inventory'))) {
4564 $sql .= ", fk_user_valid = ".((int) $user->id);
4565 }
4566 if ($status == 1 && in_array($elementTable, array('expensereport'))) {
4567 $sql .= ", date_valid = '".$this->db->idate(dol_now())."'";
4568 }
4569 if ($status == 1 && in_array($elementTable, array('inventory'))) {
4570 $sql .= ", date_validation = '".$this->db->idate(dol_now())."'";
4571 }
4572 $sql .= " WHERE rowid = ".((int) $elementId);
4573 $sql .= " AND ".$fieldstatus." <> ".((int) $status); // We avoid update if status already correct
4574
4575 dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
4576 $resql = $this->db->query($sql);
4577 if ($resql) {
4578 $error = 0;
4579
4580 $nb_rows_affected = $this->db->affected_rows($resql); // should be 1 or 0 if status was already correct
4581
4582 if ($nb_rows_affected > 0) {
4583 if (empty($trigkey)) {
4584 // Try to guess trigkey (for backward compatibility, now we should have trigkey defined into the call of setStatus)
4585 if ($this->element == 'supplier_proposal' && $status == 2) {
4586 $trigkey = 'SUPPLIER_PROPOSAL_SIGN'; // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
4587 }
4588 if ($this->element == 'supplier_proposal' && $status == 3) {
4589 $trigkey = 'SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
4590 }
4591 if ($this->element == 'supplier_proposal' && $status == 4) {
4592 $trigkey = 'SUPPLIER_PROPOSAL_CLOSE'; // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
4593 }
4594 if ($this->element == 'fichinter' && $status == 3) {
4595 $trigkey = 'FICHINTER_CLASSIFY_DONE';
4596 }
4597 if ($this->element == 'fichinter' && $status == 2) {
4598 $trigkey = 'FICHINTER_CLASSIFY_BILLED';
4599 }
4600 if ($this->element == 'fichinter' && $status == 1) {
4601 $trigkey = 'FICHINTER_CLASSIFY_UNBILLED';
4602 }
4603 }
4604
4605 if ($trigkey) {
4606 // Call trigger
4607 $result = $this->call_trigger($trigkey, $user);
4608 if ($result < 0) {
4609 $error++;
4610 }
4611 // End call triggers
4612 }
4613 } else {
4614 // The status was probably already good. We do nothing more, no triggers.
4615 }
4616
4617 if (!$error) {
4618 $this->db->commit();
4619
4620 if (empty($savElementId)) {
4621 // If the element we update is $this (so $elementId was provided as null)
4622 if ($fieldstatus == 'tosell') {
4623 $this->status = $status;
4624 } elseif ($fieldstatus == 'tobuy') {
4625 $this->status_buy = $status; // @phpstan-ignore-line
4626 } else {
4627 $this->statut = $status;
4628 $this->status = $status;
4629 }
4630 }
4631
4632 return 1;
4633 } else {
4634 $this->db->rollback();
4635 dol_syslog(get_class($this)."::setStatut ".$this->error, LOG_ERR);
4636 return -1;
4637 }
4638 } else {
4639 $this->error = $this->db->lasterror();
4640 $this->db->rollback();
4641 return -1;
4642 }
4643 }
4644
4645
4653 public function getCanvas($id = 0, $ref = '')
4654 {
4655 global $conf;
4656
4657 if (empty($id) && empty($ref)) {
4658 return 0;
4659 }
4660 if (getDolGlobalString('MAIN_DISABLE_CANVAS')) {
4661 return 0; // To increase speed. Not enabled by default.
4662 }
4663
4664 // Clean parameters
4665 $ref = trim($ref);
4666
4667 $sql = "SELECT rowid, canvas";
4668 $sql .= " FROM ".$this->db->prefix().$this->table_element;
4669 $sql .= " WHERE entity IN (".getEntity($this->element).")";
4670 if (!empty($id)) {
4671 $sql .= " AND rowid = ".((int) $id);
4672 }
4673 if (!empty($ref)) {
4674 $sql .= " AND ref = '".$this->db->escape($ref)."'";
4675 }
4676
4677 $resql = $this->db->query($sql);
4678 if ($resql) {
4679 $obj = $this->db->fetch_object($resql);
4680 if ($obj) {
4681 $this->canvas = $obj->canvas;
4682 return 1;
4683 } else {
4684 return 0;
4685 }
4686 } else {
4687 dol_print_error($this->db);
4688 return -1;
4689 }
4690 }
4691
4692
4699 public function getSpecialCode($lineid)
4700 {
4701 $sql = "SELECT special_code FROM ".$this->db->prefix().$this->table_element_line;
4702 $sql .= " WHERE rowid = ".((int) $lineid);
4703 $resql = $this->db->query($sql);
4704 if ($resql) {
4705 $row = $this->db->fetch_row($resql);
4706 return (!empty($row[0]) ? $row[0] : 0);
4707 }
4708
4709 return 0;
4710 }
4711
4720 public function isObjectUsed($id = 0, $entity = 0)
4721 {
4722 global $langs;
4723
4724 if (empty($id)) {
4725 $id = $this->id;
4726 }
4727
4728 // Check parameters
4729 if (!isset($this->childtables) || !is_array($this->childtables) || count($this->childtables) == 0) {
4730 dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
4731 return -1;
4732 }
4733
4734 $arraytoscan = $this->childtables; // array('tablename'=>array('fk_element'=>'parentfield'), ...) or array('tablename'=>array('parent'=>table_parent, 'parentkey'=>'nameoffieldforparentfkkey'), ...)
4735 // For backward compatibility, we check if array is old format array('tablename1', 'tablename2', ...)
4736 $tmparray = array_keys($this->childtables);
4737 if (is_numeric($tmparray[0])) {
4738 $arraytoscan = array_flip($this->childtables);
4739 }
4740
4741 // Test if child exists
4742 $haschild = 0;
4743 foreach ($arraytoscan as $table => $element) {
4744 //print $id.'-'.$table.'-'.$elementname.'<br>';
4745 // Check if element can be deleted
4746 $sql = "SELECT COUNT(*) as nb";
4747 $sql.= " FROM ".$this->db->prefix().$table." as c";
4748 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4749 $sql.= ", ".$this->db->prefix().$element['parent']." as p";
4750 }
4751 if (!empty($element['fk_element'])) {
4752 $sql.= " WHERE c.".$element['fk_element']." = ".((int) $id);
4753 } else {
4754 $sql.= " WHERE c.".$this->fk_element." = ".((int) $id);
4755 }
4756 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4757 $sql.= " AND c.".$element['parentkey']." = p.rowid";
4758 }
4759 if (!empty($element['parent']) && !empty($element['parenttypefield']) && !empty($element['parenttypevalue'])) {
4760 $sql.= " AND c.".$element['parenttypefield']." = '".$this->db->escape($element['parenttypevalue'])."'";
4761 }
4762 if (!empty($entity)) {
4763 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4764 $sql.= " AND p.entity = ".((int) $entity);
4765 } else {
4766 $sql.= " AND c.entity = ".((int) $entity);
4767 }
4768 }
4769
4770 $resql = $this->db->query($sql);
4771 if ($resql) {
4772 $obj = $this->db->fetch_object($resql);
4773 if ($obj->nb > 0) {
4774 $langs->load("errors");
4775 //print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
4776 $haschild += $obj->nb;
4777 if (is_numeric($element)) { // very old usage array('table1', 'table2', ...)
4778 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $table);
4779 } elseif (is_string($element)) { // old usage array('table1' => 'TranslateKey1', 'table2' => 'TranslateKey2', ...)
4780 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element));
4781 } else { // new usage: $element['name']=Translation key
4782 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element['name']));
4783 }
4784 break; // We found at least one, we stop here
4785 }
4786 } else {
4787 $this->errors[] = $this->db->lasterror();
4788 return -1;
4789 }
4790 }
4791 if ($haschild > 0) {
4792 $this->errors[] = "ErrorRecordHasChildren";
4793 return $haschild;
4794 } else {
4795 return 0;
4796 }
4797 }
4798
4805 public function hasProductsOrServices($predefined = -1)
4806 {
4807 $nb = 0;
4808
4809 foreach ($this->lines as $key => $val) {
4810 $qualified = 0;
4811 if ($predefined == -1) {
4812 $qualified = 1;
4813 }
4814 if ($predefined == 1 && $val->fk_product > 0) {
4815 $qualified = 1;
4816 }
4817 if ($predefined == 0 && $val->fk_product <= 0) {
4818 $qualified = 1;
4819 }
4820 if ($predefined == 2 && $val->fk_product > 0 && $val->product_type == 0) {
4821 $qualified = 1;
4822 }
4823 if ($predefined == 3 && $val->fk_product > 0 && $val->product_type == 1) {
4824 $qualified = 1;
4825 }
4826 if ($qualified) {
4827 $nb++;
4828 }
4829 }
4830 dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
4831 return $nb;
4832 }
4833
4839 public function getTotalDiscount()
4840 {
4841 if (!empty($this->table_element_line)) {
4842 $total_discount = 0.00;
4843
4844 $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
4845 $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
4846 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
4847
4848 dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
4849 $resql = $this->db->query($sql);
4850 if ($resql) {
4851 $num = $this->db->num_rows($resql);
4852 $i = 0;
4853 while ($i < $num) {
4854 $obj = $this->db->fetch_object($resql);
4855
4856 $pu_ht = $obj->pu_ht;
4857 $qty = $obj->qty;
4858 $total_ht = $obj->total_ht;
4859
4860 $total_discount_line = (float) price2num(($pu_ht * $qty) - $total_ht, 'MT');
4861 $total_discount += $total_discount_line;
4862
4863 $i++;
4864 }
4865 }
4866
4867 //print $total_discount; exit;
4868 return price2num($total_discount);
4869 }
4870
4871 return null;
4872 }
4873
4874
4881 public function getTotalWeightVolume()
4882 {
4883 $totalWeight = 0;
4884 $totalVolume = 0;
4885 // defined for shipment only
4886 $totalOrdered = '';
4887 // defined for shipment only
4888 $totalToShip = '';
4889
4890 foreach ($this->lines as $line) {
4891 if (isset($line->qty_asked)) {
4892 if (empty($totalOrdered)) {
4893 $totalOrdered = 0; // Avoid warning because $totalOrdered is ''
4894 }
4895 $totalOrdered += $line->qty_asked; // defined for shipment only
4896 }
4897 if (isset($line->qty_shipped)) {
4898 if (empty($totalToShip)) {
4899 $totalToShip = 0; // Avoid warning because $totalToShip is ''
4900 }
4901 $totalToShip += $line->qty_shipped; // defined for shipment only
4902 } elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty)) {
4903 if (empty($totalToShip)) {
4904 $totalToShip = 0;
4905 }
4906 $totalToShip += $line->qty; // defined for reception only
4907 }
4908
4909 // Define qty, weight, volume, weight_units, volume_units
4910 if ($this->element == 'shipping') {
4911 // for shipments
4912 $qty = $line->qty_shipped ? $line->qty_shipped : 0;
4913 } else {
4914 $qty = $line->qty ? $line->qty : 0;
4915 }
4916
4917 $weight = !empty($line->weight) ? $line->weight : 0;
4918 ($weight == 0 && !empty($line->product->weight)) ? $weight = $line->product->weight : 0;
4919 $volume = !empty($line->volume) ? $line->volume : 0;
4920 ($volume == 0 && !empty($line->product->volume)) ? $volume = $line->product->volume : 0;
4921
4922 $weight_units = !empty($line->weight_units) ? $line->weight_units : 0;
4923 ($weight_units == 0 && !empty($line->product->weight_units)) ? $weight_units = $line->product->weight_units : 0;
4924 $volume_units = !empty($line->volume_units) ? $line->volume_units : 0;
4925 ($volume_units == 0 && !empty($line->product->volume_units)) ? $volume_units = $line->product->volume_units : 0;
4926
4927 $weightUnit = 0;
4928 $volumeUnit = 0;
4929 if (!empty($weight_units)) {
4930 $weightUnit = $weight_units;
4931 }
4932 if (!empty($volume_units)) {
4933 $volumeUnit = $volume_units;
4934 }
4935
4936 if (empty($totalWeight)) {
4937 $totalWeight = 0; // Avoid warning because $totalWeight is ''
4938 }
4939 if (empty($totalVolume)) {
4940 $totalVolume = 0; // Avoid warning because $totalVolume is ''
4941 }
4942
4943 //var_dump($line->volume_units);
4944 if ($weight_units < 50) { // < 50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
4945 $trueWeightUnit = pow(10, $weightUnit);
4946 $totalWeight += $weight * $qty * $trueWeightUnit;
4947 } else {
4948 if ($weight_units == 99) {
4949 // conversion 1 Pound = 0.45359237 KG
4950 $trueWeightUnit = 0.45359237;
4951 $totalWeight += $weight * $qty * $trueWeightUnit;
4952 } elseif ($weight_units == 98) {
4953 // conversion 1 Ounce = 0.0283495 KG
4954 $trueWeightUnit = 0.0283495;
4955 $totalWeight += $weight * $qty * $trueWeightUnit;
4956 } else {
4957 $totalWeight += $weight * $qty; // This may be wrong if we mix different units
4958 }
4959 }
4960 if ($volume_units < 50) { // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
4961 //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
4962 $trueVolumeUnit = pow(10, $volumeUnit);
4963 //print $line->volume;
4964 $totalVolume += $volume * $qty * $trueVolumeUnit;
4965 } else {
4966 $totalVolume += $volume * $qty; // This may be wrong if we mix different units
4967 }
4968 }
4969
4970 return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
4971 }
4972
4973
4979 public function setExtraParameters()
4980 {
4981 $this->db->begin();
4982
4983 $extraparams = (!empty($this->extraparams) ? json_encode($this->extraparams) : null);
4984
4985 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
4986 $sql .= " SET extraparams = ".(!empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
4987 $sql .= " WHERE rowid = ".((int) $this->id);
4988
4989 dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
4990 $resql = $this->db->query($sql);
4991 if (!$resql) {
4992 $this->error = $this->db->lasterror();
4993 $this->db->rollback();
4994 return -1;
4995 } else {
4996 $this->db->commit();
4997 return 1;
4998 }
4999 }
5000
5001
5002 // --------------------
5003 // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
5004 // --------------------
5005
5006 /* This is to show add lines */
5007
5017 public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl')
5018 {
5019 global $conf, $user, $langs, $object, $hookmanager, $extrafields, $form;
5020
5021 // Line extrafield
5022 if (!is_object($extrafields)) {
5023 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5024 $extrafields = new ExtraFields($this->db);
5025 }
5026 $extrafields->fetch_name_optionals_label($this->table_element_line);
5027
5028 // Output template part (modules that overwrite templates must declare this into descriptor)
5029 // Use global variables + $dateSelector + $seller and $buyer
5030 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'.
5031 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5032 foreach ($dirtpls as $module => $reldir) {
5033 if (!empty($module)) {
5034 $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
5035 } else {
5036 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php';
5037 }
5038
5039 if (empty($conf->file->strict_mode)) {
5040 $res = @include $tpl;
5041 } else {
5042 $res = include $tpl; // for debug
5043 }
5044 if ($res) {
5045 break;
5046 }
5047 }
5048 }
5049
5050
5051
5052 /* This is to show array of line of details */
5053
5054
5069 public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl')
5070 {
5071 global $conf, $hookmanager, $langs, $user, $form, $extrafields, $object;
5072 // TODO We should not use global var for this
5073 global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
5074
5075 // Define usemargins
5076 $usemargins = 0;
5077 if (isModEnabled('margin') && !empty($this->element) && in_array($this->element, array('facture', 'facturerec', 'propal', 'commande'))) {
5078 $usemargins = 1;
5079 }
5080
5081 $num = count($this->lines);
5082
5083 // Line extrafield
5084 if (!is_object($extrafields)) {
5085 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5086 $extrafields = new ExtraFields($this->db);
5087 }
5088 $extrafields->fetch_name_optionals_label($this->table_element_line);
5089
5090 $parameters = array('num'=>$num, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$this->table_element_line);
5091 $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5092 if (empty($reshook)) {
5093 // Output template part (modules that overwrite templates must declare this into descriptor)
5094 // Use global variables + $dateSelector + $seller and $buyer
5095 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook.
5096 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5097 foreach ($dirtpls as $module => $reldir) {
5098 $res = 0;
5099 if (!empty($module)) {
5100 $tpl = dol_buildpath($reldir.'/objectline_title.tpl.php');
5101 } else {
5102 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php';
5103 }
5104 if (file_exists($tpl)) {
5105 if (empty($conf->file->strict_mode)) {
5106 $res = @include $tpl;
5107 } else {
5108 $res = include $tpl; // for debug
5109 }
5110 }
5111 if ($res) {
5112 break;
5113 }
5114 }
5115 }
5116
5117 $i = 0;
5118
5119 print "<!-- begin printObjectLines() --><tbody>\n";
5120 foreach ($this->lines as $line) {
5121 //Line extrafield
5122 $line->fetch_optionals();
5123
5124 //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line)))
5125 if (is_object($hookmanager)) { // Old code is commented on preceding line.
5126 if (empty($line->fk_parent_line)) {
5127 $parameters = array('line'=>$line, 'num'=>$num, 'i'=>$i, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$line->table_element, 'defaulttpldir'=>$defaulttpldir);
5128 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5129 } else {
5130 $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);
5131 $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5132 }
5133 }
5134 if (empty($reshook)) {
5135 $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafields, $defaulttpldir);
5136 }
5137
5138 $i++;
5139 }
5140 print "</tbody><!-- end printObjectLines() -->\n";
5141 }
5142
5160 public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/core/tpl')
5161 {
5162 global $conf, $langs, $user, $object, $hookmanager;
5163 global $form;
5164 global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
5165
5166 $object_rights = $this->getRights();
5167
5168 // var used into tpl
5169 $text = '';
5170 $description = '';
5171
5172 // Line in view mode
5173 if ($action != 'editline' || $selected != $line->id) {
5174 // Product
5175 if (!empty($line->fk_product) && $line->fk_product > 0) {
5176 $product_static = new Product($this->db);
5177 $product_static->fetch($line->fk_product);
5178
5179 $product_static->ref = $line->ref; //can change ref in hook
5180 $product_static->label = !empty($line->label) ? $line->label : ""; //can change label in hook
5181
5182 $text = $product_static->getNomUrl(1);
5183
5184 // Define output language and label
5185 if (getDolGlobalInt('MAIN_MULTILANGS')) {
5186 if (property_exists($this, 'socid') && !is_object($this->thirdparty)) {
5187 dol_print_error('', 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
5188 return;
5189 }
5190
5191 $prod = new Product($this->db);
5192 $prod->fetch($line->fk_product);
5193
5194 $outputlangs = $langs;
5195 $newlang = '';
5196 if (empty($newlang) && GETPOST('lang_id', 'aZ09')) {
5197 $newlang = GETPOST('lang_id', 'aZ09');
5198 }
5199 if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE') && empty($newlang) && is_object($this->thirdparty)) {
5200 $newlang = $this->thirdparty->default_lang; // To use language of customer
5201 }
5202 if (!empty($newlang)) {
5203 $outputlangs = new Translate("", $conf);
5204 $outputlangs->setDefaultLang($newlang);
5205 }
5206
5207 $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
5208 } else {
5209 $label = $line->product_label;
5210 }
5211
5212 $text .= ' - '.(!empty($line->label) ? $line->label : $label);
5213 $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.
5214 }
5215
5216 $line->pu_ttc = price2num((!empty($line->subprice) ? $line->subprice : 0) * (1 + ((!empty($line->tva_tx) ? $line->tva_tx : 0) / 100)), 'MU');
5217
5218 // Output template part (modules that overwrite templates must declare this into descriptor)
5219 // Use global variables + $dateSelector + $seller and $buyer
5220 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5221 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5222 foreach ($dirtpls as $module => $reldir) {
5223 $res = 0;
5224 if (!empty($module)) {
5225 $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
5226 } else {
5227 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php';
5228 }
5229 //var_dump($tpl);
5230 if (file_exists($tpl)) {
5231 if (empty($conf->file->strict_mode)) {
5232 $res = @include $tpl;
5233 } else {
5234 $res = include $tpl; // for debug
5235 }
5236 }
5237 if ($res) {
5238 break;
5239 }
5240 }
5241 }
5242
5243 // Line in update mode
5244 if ($this->statut == 0 && $action == 'editline' && $selected == $line->id) {
5245 $label = (!empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
5246
5247 $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU');
5248
5249 // Output template part (modules that overwrite templates must declare this into descriptor)
5250 // Use global variables + $dateSelector + $seller and $buyer
5251 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5252 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5253 foreach ($dirtpls as $module => $reldir) {
5254 if (!empty($module)) {
5255 $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
5256 } else {
5257 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php';
5258 }
5259
5260 if (empty($conf->file->strict_mode)) {
5261 $res = @include $tpl;
5262 } else {
5263 $res = include $tpl; // for debug
5264 }
5265 if ($res) {
5266 break;
5267 }
5268 }
5269 }
5270 }
5271
5272
5273 /* This is to show array of line of details of source object */
5274
5275
5286 public function printOriginLinesList($restrictlist = '', $selectedLines = array())
5287 {
5288 global $langs, $hookmanager, $conf, $form, $action;
5289
5290 print '<tr class="liste_titre">';
5291 print '<td class="linecolref">'.$langs->trans('Ref').'</td>';
5292 print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
5293 print '<td class="linecolvat right">'.$langs->trans('VATRate').'</td>';
5294 print '<td class="linecoluht right">'.$langs->trans('PriceUHT').'</td>';
5295 if (isModEnabled("multicurrency")) {
5296 print '<td class="linecoluht_currency right">'.$langs->trans('PriceUHTCurrency').'</td>';
5297 }
5298 print '<td class="linecolqty right">'.$langs->trans('Qty').'</td>';
5299 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5300 print '<td class="linecoluseunit left">'.$langs->trans('Unit').'</td>';
5301 }
5302 print '<td class="linecoldiscount right">'.$langs->trans('ReductionShort').'</td>';
5303 print '<td class="linecolht right">'.$langs->trans('TotalHT').'</td>';
5304 print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
5305 print '</tr>';
5306 $i = 0;
5307
5308 if (!empty($this->lines)) {
5309 foreach ($this->lines as $line) {
5310 $reshook = 0;
5311 //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line))) {
5312 if (is_object($hookmanager)) { // Old code is commented on preceding line.
5313 $parameters = array('line'=>$line, 'i'=>$i, 'restrictlist'=>$restrictlist, 'selectedLines'=> $selectedLines);
5314 if (!empty($line->fk_parent_line)) {
5315 $parameters['fk_parent_line'] = $line->fk_parent_line;
5316 }
5317 $reshook = $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5318 }
5319 if (empty($reshook)) {
5320 $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines);
5321 }
5322
5323 $i++;
5324 }
5325 }
5326 }
5327
5341 public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
5342 {
5343 global $langs, $conf;
5344
5345 //var_dump($line);
5346 if (!empty($line->date_start)) {
5347 $date_start = $line->date_start;
5348 } else {
5349 $date_start = $line->date_debut_prevue;
5350 if ($line->date_debut_reel) {
5351 $date_start = $line->date_debut_reel;
5352 }
5353 }
5354 if (!empty($line->date_end)) {
5355 $date_end = $line->date_end;
5356 } else {
5357 $date_end = $line->date_fin_prevue;
5358 if ($line->date_fin_reel) {
5359 $date_end = $line->date_fin_reel;
5360 }
5361 }
5362
5363 $this->tpl['id'] = $line->id;
5364
5365 $this->tpl['label'] = '';
5366 if (!empty($line->fk_parent_line)) {
5367 $this->tpl['label'] .= img_picto('', 'rightarrow');
5368 }
5369
5370 if (($line->info_bits & 2) == 2) { // TODO Not sure this is used for source object
5371 $discount = new DiscountAbsolute($this->db);
5372 if (property_exists($this, 'socid')) {
5373 $discount->fk_soc = $this->socid;
5374 }
5375 $this->tpl['label'] .= $discount->getNomUrl(0, 'discount');
5376 } elseif (!empty($line->fk_product)) {
5377 $productstatic = new Product($this->db);
5378 $productstatic->id = $line->fk_product;
5379 $productstatic->ref = $line->ref;
5380 $productstatic->type = $line->fk_product_type;
5381 if (empty($productstatic->ref)) {
5382 $line->fetch_product();
5383 $productstatic = $line->product;
5384 }
5385
5386 $this->tpl['label'] .= $productstatic->getNomUrl(1);
5387 $this->tpl['label'] .= ' - '.(!empty($line->label) ? $line->label : $line->product_label);
5388 // Dates
5389 if ($line->product_type == 1 && ($date_start || $date_end)) {
5390 $this->tpl['label'] .= get_date_range($date_start, $date_end);
5391 }
5392 } else {
5393 $this->tpl['label'] .= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
5394 if (!empty($line->desc)) {
5395 $this->tpl['label'] .= $line->desc;
5396 } else {
5397 $this->tpl['label'] .= ($line->label ? '&nbsp;'.$line->label : '');
5398 }
5399
5400 // Dates
5401 if ($line->product_type == 1 && ($date_start || $date_end)) {
5402 $this->tpl['label'] .= get_date_range($date_start, $date_end);
5403 }
5404 }
5405
5406 if (!empty($line->desc)) {
5407 if ($line->desc == '(CREDIT_NOTE)') { // TODO Not sure this is used for source object
5408 $discount = new DiscountAbsolute($this->db);
5409 $discount->fetch($line->fk_remise_except);
5410 $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
5411 } elseif ($line->desc == '(DEPOSIT)') { // TODO Not sure this is used for source object
5412 $discount = new DiscountAbsolute($this->db);
5413 $discount->fetch($line->fk_remise_except);
5414 $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
5415 } elseif ($line->desc == '(EXCESS RECEIVED)') {
5416 $discount = new DiscountAbsolute($this->db);
5417 $discount->fetch($line->fk_remise_except);
5418 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
5419 } elseif ($line->desc == '(EXCESS PAID)') {
5420 $discount = new DiscountAbsolute($this->db);
5421 $discount->fetch($line->fk_remise_except);
5422 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
5423 } else {
5424 $this->tpl['description'] = dol_trunc($line->desc, 60);
5425 }
5426 } else {
5427 $this->tpl['description'] = '&nbsp;';
5428 }
5429
5430 // VAT Rate
5431 $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
5432 $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
5433 if (!empty($line->vat_src_code) && !preg_match('/\‍(/', $this->tpl['vat_rate'])) {
5434 $this->tpl['vat_rate'] .= ' ('.$line->vat_src_code.')';
5435 }
5436
5437 $this->tpl['price'] = price($line->subprice);
5438 $this->tpl['total_ht'] = price($line->total_ht);
5439 $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
5440 $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
5441 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5442 $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
5443 }
5444 $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
5445
5446 // Is the line strike or not
5447 $this->tpl['strike'] = 0;
5448 if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) {
5449 $this->tpl['strike'] = 1;
5450 }
5451
5452 // Output template part (modules that overwrite templates must declare this into descriptor)
5453 // Use global variables + $dateSelector + $seller and $buyer
5454 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5455 foreach ($dirtpls as $module => $reldir) {
5456 if (!empty($module)) {
5457 $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
5458 } else {
5459 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php';
5460 }
5461
5462 if (empty($conf->file->strict_mode)) {
5463 $res = @include $tpl;
5464 } else {
5465 $res = include $tpl; // for debug
5466 }
5467 if ($res) {
5468 break;
5469 }
5470 }
5471 }
5472
5473
5474 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5485 public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
5486 {
5487 // phpcs:enable
5488 $this->db->begin();
5489
5490 $sql = "INSERT INTO ".$this->db->prefix()."element_resources (";
5491 $sql .= "resource_id";
5492 $sql .= ", resource_type";
5493 $sql .= ", element_id";
5494 $sql .= ", element_type";
5495 $sql .= ", busy";
5496 $sql .= ", mandatory";
5497 $sql .= ") VALUES (";
5498 $sql .= ((int) $resource_id);
5499 $sql .= ", '".$this->db->escape($resource_type)."'";
5500 $sql .= ", '".$this->db->escape($this->id)."'";
5501 $sql .= ", '".$this->db->escape($this->element)."'";
5502 $sql .= ", '".$this->db->escape($busy)."'";
5503 $sql .= ", '".$this->db->escape($mandatory)."'";
5504 $sql .= ")";
5505
5506 dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
5507 if ($this->db->query($sql)) {
5508 $this->db->commit();
5509 return 1;
5510 } else {
5511 $this->error = $this->db->lasterror();
5512 $this->db->rollback();
5513 return 0;
5514 }
5515 }
5516
5517 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5526 public function delete_resource($rowid, $element, $notrigger = 0)
5527 {
5528 // phpcs:enable
5529 global $user;
5530
5531 $this->db->begin();
5532
5533 $sql = "DELETE FROM ".$this->db->prefix()."element_resources";
5534 $sql .= " WHERE rowid = ".((int) $rowid);
5535
5536 dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
5537
5538 $resql = $this->db->query($sql);
5539 if (!$resql) {
5540 $this->error = $this->db->lasterror();
5541 $this->db->rollback();
5542 return -1;
5543 } else {
5544 if (!$notrigger) {
5545 $result = $this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
5546 if ($result < 0) {
5547 $this->db->rollback();
5548 return -1;
5549 }
5550 }
5551 $this->db->commit();
5552 return 1;
5553 }
5554 }
5555
5556
5562 public function __clone()
5563 {
5564 // Force a copy of this->lines, otherwise it will point to same object.
5565 if (isset($this->lines) && is_array($this->lines)) {
5566 $nboflines = count($this->lines);
5567 for ($i = 0; $i < $nboflines; $i++) {
5568 if (is_object($this->lines[$i])) {
5569 $this->lines[$i] = clone $this->lines[$i];
5570 }
5571 }
5572 }
5573 }
5574
5588 protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
5589 {
5590 global $conf, $langs, $user, $hookmanager, $action;
5591
5592 $srctemplatepath = '';
5593
5594 $parameters = array('modelspath'=>$modelspath, 'modele'=>$modele, 'outputlangs'=>$outputlangs, 'hidedetails'=>$hidedetails, 'hidedesc'=>$hidedesc, 'hideref'=>$hideref, 'moreparams'=>$moreparams);
5595 $reshook = $hookmanager->executeHooks('commonGenerateDocument', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5596
5597 if (!empty($reshook)) {
5598 return $reshook;
5599 }
5600
5601 dol_syslog("commonGenerateDocument modele=".$modele." outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
5602
5603 if (empty($modele)) {
5604 $this->error = 'BadValueForParameterModele';
5605 return -1;
5606 }
5607
5608 // Increase limit for PDF build
5609 $err = error_reporting();
5610 error_reporting(0);
5611 @set_time_limit(120);
5612 error_reporting($err);
5613
5614 // If selected model is a filename template (then $modele="modelname" or "modelname:filename")
5615 $tmp = explode(':', $modele, 2);
5616 $saved_model = $modele;
5617 if (!empty($tmp[1])) {
5618 $modele = $tmp[0];
5619 $srctemplatepath = $tmp[1];
5620 }
5621
5622 // Search template files
5623 $file = '';
5624 $classname = '';
5625 $filefound = '';
5626 $dirmodels = array('/');
5627 if (is_array($conf->modules_parts['models'])) {
5628 $dirmodels = array_merge($dirmodels, $conf->modules_parts['models']);
5629 }
5630 foreach ($dirmodels as $reldir) {
5631 foreach (array('doc', 'pdf') as $prefix) {
5632 if (in_array(get_class($this), array('Adherent'))) {
5633 // Member module use prefix_modele.class.php
5634 $file = $prefix."_".$modele.".class.php";
5635 } else {
5636 // Other module use prefix_modele.modules.php
5637 $file = $prefix."_".$modele.".modules.php";
5638 }
5639
5640 $file = dol_sanitizeFileName($file);
5641
5642 // We chack if file exists
5643 $file = dol_buildpath($reldir.$modelspath.$file, 0);
5644 if (file_exists($file)) {
5645 $filefound = $file;
5646 $classname = $prefix.'_'.$modele;
5647 break;
5648 }
5649 }
5650 if ($filefound) {
5651 break;
5652 }
5653 }
5654
5655 if (!$filefound) {
5656 $this->error = $langs->trans("Error").' Failed to load doc generator with modelpaths='.$modelspath.' - modele='.$modele;
5657 $this->errors[] = $this->error;
5658 dol_syslog($this->error, LOG_ERR);
5659 return -1;
5660 }
5661
5662 // Sanitize $filefound
5663 $filefound = dol_sanitizePathName($filefound);
5664
5665 // If generator was found
5666 global $db; // Required to solve a conception default making an include of some code that uses $db instead of $this->db just after.
5667
5668 require_once $filefound;
5669
5670 $obj = new $classname($this->db);
5671
5672 // If generator is ODT, we must have srctemplatepath defined, if not we set it.
5673 if ($obj->type == 'odt' && empty($srctemplatepath)) {
5674 $varfortemplatedir = $obj->scandir;
5675 if ($varfortemplatedir && getDolGlobalString($varfortemplatedir)) {
5676 $dirtoscan = getDolGlobalString($varfortemplatedir);
5677
5678 $listoffiles = array();
5679
5680 // Now we add first model found in directories scanned
5681 $listofdir = explode(',', $dirtoscan);
5682 foreach ($listofdir as $key => $tmpdir) {
5683 $tmpdir = trim($tmpdir);
5684 $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
5685 if (!$tmpdir) {
5686 unset($listofdir[$key]);
5687 continue;
5688 }
5689 if (is_dir($tmpdir)) {
5690 $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
5691 if (count($tmpfiles)) {
5692 $listoffiles = array_merge($listoffiles, $tmpfiles);
5693 }
5694 }
5695 }
5696
5697 if (count($listoffiles)) {
5698 foreach ($listoffiles as $record) {
5699 $srctemplatepath = $record['fullname'];
5700 break;
5701 }
5702 }
5703 }
5704
5705 if (empty($srctemplatepath)) {
5706 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
5707 return -1;
5708 }
5709 }
5710
5711 if ($obj->type == 'odt' && !empty($srctemplatepath)) {
5712 if (!dol_is_file($srctemplatepath)) {
5713 dol_syslog("Failed to locate template file ".$srctemplatepath, LOG_WARNING);
5714 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
5715 return -1;
5716 }
5717 }
5718
5719 // We save charset_output to restore it because write_file can change it if needed for
5720 // output format that does not support UTF8.
5721 $sav_charset_output = empty($outputlangs->charset_output) ? '' : $outputlangs->charset_output;
5722
5723 // update model_pdf in object
5724 $this->model_pdf = $saved_model;
5725
5726 if (in_array(get_class($this), array('Adherent'))) {
5727 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, 'tmp_cards', $moreparams);
5728 } else {
5729 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
5730 }
5731 // After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
5732
5733 if ($resultwritefile > 0) {
5734 $outputlangs->charset_output = $sav_charset_output;
5735
5736 // We delete old preview
5737 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5738 dol_delete_preview($this);
5739
5740 // Index file in database
5741 if (!empty($obj->result['fullpath'])) {
5742 $destfull = $obj->result['fullpath'];
5743
5744 // Update the last_main_doc field into main object (if document generator has property ->update_main_doc_field set)
5745 $update_main_doc_field = 0;
5746 if (!empty($obj->update_main_doc_field)) {
5747 $update_main_doc_field = 1;
5748 }
5749
5750 // Check that the file exists, before indexing it.
5751 // Hint: It does not exist, if we create a PDF and auto delete the ODT File
5752 if (dol_is_file($destfull)) {
5753 $this->indexFile($destfull, $update_main_doc_field);
5754 }
5755 } else {
5756 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);
5757 }
5758
5759 // Success in building document. We build meta file.
5760 dol_meta_create($this);
5761
5762 return 1;
5763 } else {
5764 $outputlangs->charset_output = $sav_charset_output;
5765 $this->error = $obj->error;
5766 $this->errors = $obj->errors;
5767 dol_syslog("Error generating document for ".__CLASS__.". Error: ".$obj->error, LOG_ERR);
5768 return -1;
5769 }
5770 }
5771
5781 public function indexFile($destfull, $update_main_doc_field)
5782 {
5783 global $conf, $user;
5784
5785 $upload_dir = dirname($destfull);
5786 $destfile = basename($destfull);
5787 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
5788
5789 if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a tmp dir
5790 $filename = basename($destfile);
5791 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
5792 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
5793
5794 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
5795 $ecmfile = new EcmFiles($this->db);
5796 $result = $ecmfile->fetch(0, '', ($rel_dir ? $rel_dir.'/' : '').$filename);
5797
5798 // Set the public "share" key
5799 $setsharekey = false;
5800 if ($this->element == 'propal' || $this->element == 'proposal') {
5801 if (getDolGlobalInt("PROPOSAL_ALLOW_ONLINESIGN")) {
5802 $setsharekey = true; // feature to make online signature is not set or set to on (default)
5803 }
5804 if (getDolGlobalInt("PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD")) {
5805 $setsharekey = true;
5806 }
5807 }
5808 if ($this->element == 'commande' && getDolGlobalInt("ORDER_ALLOW_EXTERNAL_DOWNLOAD")) {
5809 $setsharekey = true;
5810 }
5811 if ($this->element == 'facture' && getDolGlobalInt("INVOICE_ALLOW_EXTERNAL_DOWNLOAD")) {
5812 $setsharekey = true;
5813 }
5814 if ($this->element == 'bank_account' && getDolGlobalInt("BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD")) {
5815 $setsharekey = true;
5816 }
5817 if ($this->element == 'product' && getDolGlobalInt("PRODUCT_ALLOW_EXTERNAL_DOWNLOAD")) {
5818 $setsharekey = true;
5819 }
5820 if ($this->element == 'contrat' && getDolGlobalInt("CONTRACT_ALLOW_EXTERNAL_DOWNLOAD")) {
5821 $setsharekey = true;
5822 }
5823 if ($this->element == 'fichinter' && getDolGlobalInt("FICHINTER_ALLOW_EXTERNAL_DOWNLOAD")) {
5824 $setsharekey = true;
5825 }
5826 if ($this->element == 'supplier_proposal' && getDolGlobalInt("SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD")) {
5827 $setsharekey = true;
5828 }
5829 if ($this->element == 'societe_rib' && getDolGlobalInt("SOCIETE_RIB_ALLOW_ONLINESIGN")) {
5830 $setsharekey = true;
5831 }
5832
5833 if ($setsharekey) {
5834 if (empty($ecmfile->share)) { // Because object not found or share not set yet
5835 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
5836 $ecmfile->share = getRandomPassword(true);
5837 }
5838 }
5839
5840 if ($result > 0) {
5841 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5842 $ecmfile->fullpath_orig = '';
5843 $ecmfile->gen_or_uploaded = 'generated';
5844 $ecmfile->description = ''; // indexed content
5845 $ecmfile->keywords = ''; // keyword content
5846 $result = $ecmfile->update($user);
5847 if ($result < 0) {
5848 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5849 return -1;
5850 }
5851 } else {
5852 $ecmfile->entity = $conf->entity;
5853 $ecmfile->filepath = $rel_dir;
5854 $ecmfile->filename = $filename;
5855 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5856 $ecmfile->fullpath_orig = '';
5857 $ecmfile->gen_or_uploaded = 'generated';
5858 $ecmfile->description = ''; // indexed content
5859 $ecmfile->keywords = ''; // keyword content
5860 $ecmfile->src_object_type = $this->table_element; // $this->table_name is 'myobject' or 'mymodule_myobject'.
5861 $ecmfile->src_object_id = $this->id;
5862
5863 $result = $ecmfile->create($user);
5864 if ($result < 0) {
5865 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5866 return -1;
5867 }
5868 }
5869
5870 /*$this->result['fullname']=$destfull;
5871 $this->result['filepath']=$ecmfile->filepath;
5872 $this->result['filename']=$ecmfile->filename;*/
5873 //var_dump($obj->update_main_doc_field);exit;
5874
5875 if ($update_main_doc_field && !empty($this->table_element)) {
5876 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath."/".$ecmfile->filename)."'";
5877 $sql .= " WHERE rowid = ".((int) $this->id);
5878
5879 $resql = $this->db->query($sql);
5880 if (!$resql) {
5881 dol_print_error($this->db);
5882 return -1;
5883 } else {
5884 $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
5885 }
5886 }
5887 }
5888
5889 return 1;
5890 }
5891
5899 public function addThumbs($file)
5900 {
5901 $file_osencoded = dol_osencode($file);
5902
5903 if (file_exists($file_osencoded)) {
5904 require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
5905
5906 $tmparraysize = getDefaultImageSizes();
5907 $maxwidthsmall = $tmparraysize['maxwidthsmall'];
5908 $maxheightsmall = $tmparraysize['maxheightsmall'];
5909 $maxwidthmini = $tmparraysize['maxwidthmini'];
5910 $maxheightmini = $tmparraysize['maxheightmini'];
5911 //$quality = $tmparraysize['quality'];
5912 $quality = 50; // For thumbs, we force quality to 50
5913
5914 // Create small thumbs for company (Ratio is near 16/9)
5915 // Used on logon for example
5916 vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
5917
5918 // Create mini thumbs for company (Ratio is near 16/9)
5919 // Used on menu or for setup page for example
5920 vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
5921 }
5922 }
5923
5931 public function delThumbs($file)
5932 {
5933 $imgThumbName = getImageFileNameForSize($file, '_small'); // Full path of thumb file
5934 dol_delete_file($imgThumbName);
5935 $imgThumbName = getImageFileNameForSize($file, '_mini'); // Full path of thumb file
5936 dol_delete_file($imgThumbName);
5937 }
5938
5939
5940 /* Functions common to commonobject and commonobjectline */
5941
5942 /* For default values */
5943
5957 public function getDefaultCreateValueFor($fieldname, $alternatevalue = null, $type = 'alphanohtml')
5958 {
5959 global $conf, $_POST;
5960
5961 // If param here has been posted, we use this value first.
5962 if (GETPOSTISSET($fieldname)) {
5963 return GETPOST($fieldname, $type, 3);
5964 }
5965
5966 if (isset($alternatevalue)) {
5967 return $alternatevalue;
5968 }
5969
5970 $newelement = $this->element;
5971 if ($newelement == 'facture') {
5972 $newelement = 'invoice';
5973 }
5974 if ($newelement == 'commande') {
5975 $newelement = 'order';
5976 }
5977 if (empty($newelement)) {
5978 dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
5979 return '';
5980 }
5981
5982 $keyforfieldname = strtoupper($newelement.'_DEFAULT_'.$fieldname);
5983 //var_dump($keyforfieldname);
5984 if (getDolGlobalString($keyforfieldname)) {
5985 return getDolGlobalString($keyforfieldname);
5986 }
5987
5988 // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
5989 // store content into $conf->cache['overwrite_default']
5990
5991 return '';
5992 }
5993
5994
5995 /* For triggers */
5996
5997
5998 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6009 public function call_trigger($triggerName, $user)
6010 {
6011 // phpcs:enable
6012 global $langs, $conf;
6013
6014 if (!empty(self::TRIGGER_PREFIX) && strpos($triggerName, self::TRIGGER_PREFIX . '_') !== 0) {
6015 dol_print_error('', 'The trigger "' . $triggerName . '" does not start with "' . self::TRIGGER_PREFIX . '_" as required.');
6016 exit;
6017 }
6018 if (!is_object($langs)) { // If lang was not defined, we set it. It is required by run_triggers().
6019 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
6020 $langs = new Translate('', $conf);
6021 }
6022
6023 include_once DOL_DOCUMENT_ROOT.'/core/class/interfaces.class.php';
6024 $interface = new Interfaces($this->db);
6025 $result = $interface->run_triggers($triggerName, $this, $user, $langs, $conf);
6026
6027 if ($result < 0) {
6028 if (!empty($this->errors)) {
6029 $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.
6030 } else {
6031 $this->errors = $interface->errors;
6032 }
6033 }
6034 return $result;
6035 }
6036
6037
6038 /* Functions for data in other language */
6039
6040
6049 {
6050 // To avoid SQL errors. Probably not the better solution though
6051 if (!$this->element) {
6052 return 0;
6053 }
6054 if (!($this->id > 0)) {
6055 return 0;
6056 }
6057 if (is_array($this->array_languages)) {
6058 return 1;
6059 }
6060
6061 $this->array_languages = array();
6062
6063 $element = $this->element;
6064 if ($element == 'categorie') {
6065 $element = 'categories'; // For compatibility
6066 }
6067
6068 // Request to get translation values for object
6069 $sql = "SELECT rowid, property, lang , value";
6070 $sql .= " FROM ".$this->db->prefix()."object_lang";
6071 $sql .= " WHERE type_object = '".$this->db->escape($element)."'";
6072 $sql .= " AND fk_object = ".((int) $this->id);
6073
6074 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6075 $resql = $this->db->query($sql);
6076 if ($resql) {
6077 $numrows = $this->db->num_rows($resql);
6078 if ($numrows) {
6079 $i = 0;
6080 while ($i < $numrows) {
6081 $obj = $this->db->fetch_object($resql);
6082 $key = $obj->property;
6083 $value = $obj->value;
6084 $codelang = $obj->lang;
6085 $type = $this->fields[$key]['type'];
6086
6087 // we can add this attribute to object
6088 if (preg_match('/date/', $type)) {
6089 $this->array_languages[$key][$codelang] = $this->db->jdate($value);
6090 } else {
6091 $this->array_languages[$key][$codelang] = $value;
6092 }
6093
6094 $i++;
6095 }
6096 }
6097
6098 $this->db->free($resql);
6099
6100 if ($numrows) {
6101 return $numrows;
6102 } else {
6103 return 0;
6104 }
6105 } else {
6106 dol_print_error($this->db);
6107 return -1;
6108 }
6109 }
6110
6117 public function setValuesForExtraLanguages($onlykey = '')
6118 {
6119 global $_POST, $langs;
6120
6121 // Get extra fields
6122 foreach ($_POST as $postfieldkey => $postfieldvalue) {
6123 $tmparray = explode('-', $postfieldkey);
6124 if ($tmparray[0] != 'field') {
6125 continue;
6126 }
6127
6128 $element = $tmparray[1];
6129 $key = $tmparray[2];
6130 $codelang = $tmparray[3];
6131 //var_dump("postfieldkey=".$postfieldkey." element=".$element." key=".$key." codelang=".$codelang);
6132
6133 if (!empty($onlykey) && $key != $onlykey) {
6134 continue;
6135 }
6136 if ($element != $this->element) {
6137 continue;
6138 }
6139
6140 $key_type = $this->fields[$key]['type'];
6141
6142 $enabled = 1;
6143 if (isset($this->fields[$key]['enabled'])) {
6144 $enabled = dol_eval($this->fields[$key]['enabled'], 1, 1, '1');
6145 }
6146 /*$perms = 1;
6147 if (isset($this->fields[$key]['perms']))
6148 {
6149 $perms = dol_eval($this->fields[$key]['perms'], 1, 1, '1');
6150 }*/
6151 if (empty($enabled)) {
6152 continue;
6153 }
6154 //if (empty($perms)) continue;
6155
6156 if (in_array($key_type, array('date'))) {
6157 // Clean parameters
6158 // TODO GMT date in memory must be GMT so we should add gm=true in parameters
6159 $value_key = dol_mktime(0, 0, 0, GETPOST($postfieldkey."month", 'int'), GETPOST($postfieldkey."day", 'int'), GETPOST($postfieldkey."year", 'int'));
6160 } elseif (in_array($key_type, array('datetime'))) {
6161 // Clean parameters
6162 // TODO GMT date in memory must be GMT so we should add gm=true in parameters
6163 $value_key = dol_mktime(GETPOST($postfieldkey."hour", 'int'), GETPOST($postfieldkey."min", 'int'), 0, GETPOST($postfieldkey."month", 'int'), GETPOST($postfieldkey."day", 'int'), GETPOST($postfieldkey."year", 'int'));
6164 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
6165 $value_arr = GETPOST($postfieldkey, 'array'); // check if an array
6166 if (!empty($value_arr)) {
6167 $value_key = implode(',', $value_arr);
6168 } else {
6169 $value_key = '';
6170 }
6171 } elseif (in_array($key_type, array('price', 'double'))) {
6172 $value_arr = GETPOST($postfieldkey, 'alpha');
6173 $value_key = price2num($value_arr);
6174 } else {
6175 $value_key = GETPOST($postfieldkey);
6176 if (in_array($key_type, array('link')) && $value_key == '-1') {
6177 $value_key = '';
6178 }
6179 }
6180
6181 $this->array_languages[$key][$codelang] = $value_key;
6182
6183 /*if ($nofillrequired) {
6184 $langs->load('errors');
6185 setEventMessages($langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required), null, 'errors');
6186 return -1;
6187 }*/
6188 }
6189
6190 return 1;
6191 }
6192
6193
6194 /* Functions for extrafields */
6195
6202 public function fetchNoCompute($id)
6203 {
6204 global $conf;
6205
6206 $savDisableCompute = $conf->disable_compute;
6207 $conf->disable_compute = 1;
6208
6209 $ret = $this->fetch($id); /* @phpstan-ignore-line */
6210
6211 $conf->disable_compute = $savDisableCompute;
6212
6213 return $ret;
6214 }
6215
6216 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6226 public function fetch_optionals($rowid = null, $optionsArray = null)
6227 {
6228 // phpcs:enable
6229 global $conf, $extrafields;
6230
6231 if (empty($rowid)) {
6232 $rowid = $this->id;
6233 }
6234 if (empty($rowid) && isset($this->rowid)) {
6235 $rowid = $this->rowid; // deprecated
6236 }
6237
6238 // To avoid SQL errors. Probably not the better solution though
6239 if (!$this->table_element) {
6240 return 0;
6241 }
6242
6243 $this->array_options = array();
6244
6245 if (!is_array($optionsArray)) {
6246 // If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
6247 if (!isset($extrafields) || !is_object($extrafields)) {
6248 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6249 $extrafields = new ExtraFields($this->db);
6250 }
6251
6252 // Load array of extrafields for elementype = $this->table_element
6253 if (empty($extrafields->attributes[$this->table_element]['loaded'])) {
6254 $extrafields->fetch_name_optionals_label($this->table_element);
6255 }
6256 $optionsArray = (!empty($extrafields->attributes[$this->table_element]['label']) ? $extrafields->attributes[$this->table_element]['label'] : null);
6257 } else {
6258 global $extrafields;
6259 dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
6260 }
6261
6262 $table_element = $this->table_element;
6263 if ($table_element == 'categorie') {
6264 $table_element = 'categories'; // For compatibility
6265 }
6266
6267 // Request to get complementary values
6268 if (is_array($optionsArray) && count($optionsArray) > 0) {
6269 $sql = "SELECT rowid";
6270 foreach ($optionsArray as $name => $label) {
6271 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate') {
6272 $sql .= ", ".$name;
6273 }
6274 }
6275 $sql .= " FROM ".$this->db->prefix().$table_element."_extrafields";
6276 $sql .= " WHERE fk_object = ".((int) $rowid);
6277
6278 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6279 $resql = $this->db->query($sql);
6280 if ($resql) {
6281 $numrows = $this->db->num_rows($resql);
6282 if ($numrows) {
6283 $tab = $this->db->fetch_array($resql);
6284
6285 foreach ($tab as $key => $value) {
6286 // 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)
6287 if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && !is_int($key)) {
6288 // we can add this attribute to object
6289 if (!empty($extrafields->attributes[$this->table_element]) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date', 'datetime'))) {
6290 //var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
6291 $this->array_options["options_".$key] = $this->db->jdate($value);
6292 } else {
6293 $this->array_options["options_".$key] = $value;
6294 }
6295
6296 //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
6297 }
6298 if (!empty($extrafields->attributes[$this->table_element]['type'][$key]) && $extrafields->attributes[$this->table_element]['type'][$key] == 'password') {
6299 if (!empty($value) && preg_match('/^dolcrypt:/', $value)) {
6300 $this->array_options["options_".$key] = dolDecrypt($value);
6301 }
6302 }
6303 }
6304 }
6305
6306 // If field is a computed field, value must become result of compute (regardless of whether a row exists
6307 // in the element's extrafields table)
6308 if (is_array($extrafields->attributes[$this->table_element]['label'])) {
6309 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
6310 if (!empty($extrafields->attributes[$this->table_element]) && !empty($extrafields->attributes[$this->table_element]['computed'][$key])) {
6311 //var_dump($conf->disable_compute);
6312 if (empty($conf->disable_compute)) {
6313 global $objectoffield; // We set a global variable to $objectoffield so
6314 $objectoffield = $this; // we can use it inside computed formula
6315 $this->array_options['options_' . $key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0, '2');
6316 }
6317 }
6318 }
6319 }
6320
6321 $this->db->free($resql);
6322
6323 if ($numrows) {
6324 return $numrows;
6325 } else {
6326 return 0;
6327 }
6328 } else {
6329 $this->errors[]=$this->db->lasterror;
6330 return -1;
6331 }
6332 }
6333 return 0;
6334 }
6335
6342 public function deleteExtraFields()
6343 {
6344 global $conf;
6345
6346 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6347 return 0;
6348 }
6349
6350 $this->db->begin();
6351
6352 $table_element = $this->table_element;
6353 if ($table_element == 'categorie') {
6354 $table_element = 'categories'; // For compatibility
6355 }
6356
6357 dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
6358
6359 $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6360
6361 $resql = $this->db->query($sql_del);
6362 if (!$resql) {
6363 $this->error = $this->db->lasterror();
6364 $this->db->rollback();
6365 return -1;
6366 } else {
6367 $this->db->commit();
6368 return 1;
6369 }
6370 }
6371
6382 public function insertExtraFields($trigger = '', $userused = null)
6383 {
6384 global $conf, $langs, $user;
6385
6386 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6387 return 0;
6388 }
6389
6390 if (empty($userused)) {
6391 $userused = $user;
6392 }
6393
6394 $error = 0;
6395
6396 if (!empty($this->array_options)) {
6397 // Check parameters
6398 $langs->load('admin');
6399 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6400 $extrafields = new ExtraFields($this->db);
6401 $target_extrafields = $extrafields->fetch_name_optionals_label($this->table_element);
6402
6403 // Eliminate copied source object extra fields that do not exist in target object
6404 $new_array_options = array();
6405 foreach ($this->array_options as $key => $value) {
6406 if (in_array(substr($key, 8), array_keys($target_extrafields))) { // We remove the 'options_' from $key for test
6407 $new_array_options[$key] = $value;
6408 } elseif (in_array($key, array_keys($target_extrafields))) { // We test on $key that does not contain the 'options_' prefix
6409 $new_array_options['options_'.$key] = $value;
6410 }
6411 }
6412
6413 foreach ($new_array_options as $key => $value) {
6414 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6415 $attributeType = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
6416 $attributeLabel = $langs->transnoentities($extrafields->attributes[$this->table_element]['label'][$attributeKey]);
6417 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
6418 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
6419 $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
6420 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$attributeKey];
6421
6422 // If we clone, we have to clean unique extrafields to prevent duplicates.
6423 // This behaviour can be prevented by external code by changing $this->context['createfromclone'] value in createFrom hook
6424 if (!empty($this->context['createfromclone']) && $this->context['createfromclone'] == 'createfromclone' && !empty($attributeUnique)) {
6425 $new_array_options[$key] = null;
6426 }
6427
6428 // Similar code than into insertExtraFields
6429 if ($attributeRequired) {
6430 $v = $this->array_options[$key];
6431 if (ExtraFields::isEmptyValue($v, $attributeType)) {
6432 $langs->load("errors");
6433 dol_syslog("Mandatory field '".$key."' is empty during create and set to required into definition of extrafields");
6434 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6435 return -1;
6436 }
6437 }
6438
6439 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6440 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6441
6442 if (!empty($attrfieldcomputed)) {
6443 if (getDolGlobalString('MAIN_STORE_COMPUTED_EXTRAFIELDS')) {
6444 $value = dol_eval($attrfieldcomputed, 1, 0, '2');
6445 dol_syslog($langs->trans("Extrafieldcomputed")." on ".$attributeLabel."(".$value.")", LOG_DEBUG);
6446 $new_array_options[$key] = $value;
6447 } else {
6448 $new_array_options[$key] = null;
6449 }
6450 }
6451
6452 switch ($attributeType) {
6453 case 'int':
6454 if (!is_numeric($value) && $value != '') {
6455 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6456 return -1;
6457 } elseif ($value == '') {
6458 $new_array_options[$key] = null;
6459 }
6460 break;
6461 case 'price':
6462 case 'double':
6463 $value = price2num($value);
6464 if (!is_numeric($value) && $value != '') {
6465 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." for ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6466 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6467 return -1;
6468 } elseif ($value == '') {
6469 $value = null;
6470 }
6471 //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6472 $new_array_options[$key] = $value;
6473 break;
6474 /*case 'select': // Not required, we chosed value='0' for undefined values
6475 if ($value=='-1')
6476 {
6477 $this->array_options[$key] = null;
6478 }
6479 break;*/
6480 case 'password':
6481 $algo = '';
6482 if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
6483 // If there is an encryption choice, we use it to crypt data before insert
6484 $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
6485 $algo = reset($tmparrays);
6486 if ($algo != '') {
6487 //global $action; // $action may be 'create', 'update', 'update_extras'...
6488 //var_dump($action);
6489 //var_dump($this->oldcopy);exit;
6490 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
6491 //var_dump('iii'.$algo.' '.$this->oldcopy->array_options[$key].' -> '.$this->array_options[$key]);
6492 if (isset($this->oldcopy->array_options[$key]) && $this->array_options[$key] == $this->oldcopy->array_options[$key]) {
6493 // If old value crypted in database is same than submited new value, it means we don't change it, so we don't update.
6494 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
6495 if (!preg_match('/^dolcrypt:/', $this->array_options[$key])) {
6496 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6497 } else {
6498 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6499 }
6500 } else {
6501 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6502 }
6503 } else {
6504 // If value has changed
6505 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
6506 if (!preg_match('/^dolcrypt:/', $this->array_options[$key])) {
6507 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6508 } else {
6509 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6510 }
6511 } else {
6512 $new_array_options[$key] = dol_hash($this->array_options[$key], $algo);
6513 }
6514 }
6515 } else {
6516 //var_dump('jjj'.$algo.' '.$this->oldcopy->array_options[$key].' -> '.$this->array_options[$key]);
6517 // If this->oldcopy is not defined, we can't know if we change attribute or not, so we must keep value
6518 if ($algo == 'dolcrypt' && !preg_match('/^dolcrypt:/', $this->array_options[$key])) { // dolibarr reversible encryption
6519 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6520 } else {
6521 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6522 }
6523 }
6524 } else {
6525 // No encryption
6526 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6527 }
6528 } else { // Common usage
6529 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6530 }
6531 break;
6532 case 'date':
6533 case 'datetime':
6534 // If data is a string instead of a timestamp, we convert it
6535 if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6536 $this->array_options[$key] = strtotime($this->array_options[$key]);
6537 }
6538 $new_array_options[$key] = $this->db->idate($this->array_options[$key]);
6539 break;
6540 case 'datetimegmt':
6541 // If data is a string instead of a timestamp, we convert it
6542 if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6543 $this->array_options[$key] = strtotime($this->array_options[$key]);
6544 }
6545 $new_array_options[$key] = $this->db->idate($this->array_options[$key], 'gmt');
6546 break;
6547 case 'link':
6548 $param_list = array_keys($attributeParam['options']);
6549 // 0 : ObjectName
6550 // 1 : classPath
6551 $InfoFieldList = explode(":", $param_list[0]);
6552 dol_include_once($InfoFieldList[1]);
6553 if ($InfoFieldList[0] && class_exists($InfoFieldList[0])) {
6554 if ($value == '-1') { // -1 is key for no defined in combo list of objects
6555 $new_array_options[$key] = '';
6556 } elseif ($value) {
6557 $object = new $InfoFieldList[0]($this->db);
6558 if (is_numeric($value)) {
6559 $res = $object->fetch($value); // Common case
6560 } else {
6561 $res = $object->fetch('', $value); // For compatibility
6562 }
6563
6564 if ($res > 0) {
6565 $new_array_options[$key] = $object->id;
6566 } else {
6567 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
6568 return -1;
6569 }
6570 }
6571 } else {
6572 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6573 }
6574 break;
6575 case 'checkbox':
6576 case 'chkbxlst':
6577 if (is_array($this->array_options[$key])) {
6578 $new_array_options[$key] = implode(',', $this->array_options[$key]);
6579 } else {
6580 $new_array_options[$key] = $this->array_options[$key];
6581 }
6582 break;
6583 }
6584 }
6585
6586 $this->db->begin();
6587
6588 $table_element = $this->table_element;
6589 if ($table_element == 'categorie') {
6590 $table_element = 'categories'; // For compatibility
6591 }
6592
6593 dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG);
6594
6595 $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6596 $this->db->query($sql_del);
6597
6598 $sql = "INSERT INTO ".$this->db->prefix().$table_element."_extrafields (fk_object";
6599 foreach ($new_array_options as $key => $value) {
6600 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6601 // Add field of attribut
6602 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator
6603 $sql .= ",".$attributeKey;
6604 }
6605 }
6606 // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6607 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6608 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6609 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6610 $sql .= ",".$tmpkey;
6611 }
6612 }
6613 }
6614 $sql .= ") VALUES (".$this->id;
6615
6616 foreach ($new_array_options as $key => $value) {
6617 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6618 // Add field of attribute
6619 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator)
6620 if ($new_array_options[$key] != '' || $new_array_options[$key] == '0') {
6621 $sql .= ",'".$this->db->escape($new_array_options[$key])."'";
6622 } else {
6623 $sql .= ",null";
6624 }
6625 }
6626 }
6627 // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6628 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6629 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6630 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6631 if (in_array($tmpval, array('int', 'double', 'price'))) {
6632 $sql .= ", 0";
6633 } else {
6634 $sql .= ", ''";
6635 }
6636 }
6637 }
6638 }
6639
6640 $sql .= ")";
6641
6642 $resql = $this->db->query($sql);
6643 if (!$resql) {
6644 $this->error = $this->db->lasterror();
6645 $error++;
6646 }
6647
6648 if (!$error && $trigger) {
6649 // Call trigger
6650 $this->context = array('extrafieldaddupdate'=>1);
6651 $result = $this->call_trigger($trigger, $userused);
6652 if ($result < 0) {
6653 $error++;
6654 }
6655 // End call trigger
6656 }
6657
6658 if ($error) {
6659 $this->db->rollback();
6660 return -1;
6661 } else {
6662 $this->db->commit();
6663 return 1;
6664 }
6665 } else {
6666 return 0;
6667 }
6668 }
6669
6680 public function insertExtraLanguages($trigger = '', $userused = null)
6681 {
6682 global $conf, $langs, $user;
6683
6684 if (empty($userused)) {
6685 $userused = $user;
6686 }
6687
6688 $error = 0;
6689
6690 if (getDolGlobalString('MAIN_EXTRALANGUAGES_DISABLED')) {
6691 return 0; // For avoid conflicts if trigger used
6692 }
6693
6694 if (is_array($this->array_languages)) {
6695 $new_array_languages = $this->array_languages;
6696
6697 foreach ($new_array_languages as $key => $value) {
6698 $attributeKey = $key;
6699 $attributeType = $this->fields[$attributeKey]['type'];
6700 $attributeLabel = $this->fields[$attributeKey]['label'];
6701
6702 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6703 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6704
6705 switch ($attributeType) {
6706 case 'int':
6707 if (!is_numeric($value) && $value != '') {
6708 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6709 return -1;
6710 } elseif ($value == '') {
6711 $new_array_languages[$key] = null;
6712 }
6713 break;
6714 case 'double':
6715 $value = price2num($value);
6716 if (!is_numeric($value) && $value != '') {
6717 dol_syslog($langs->trans("ExtraLanguageHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6718 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6719 return -1;
6720 } elseif ($value == '') {
6721 $new_array_languages[$key] = null;
6722 } else {
6723 $new_array_languages[$key] = $value;
6724 }
6725 break;
6726 /*case 'select': // Not required, we chosed value='0' for undefined values
6727 if ($value=='-1')
6728 {
6729 $this->array_options[$key] = null;
6730 }
6731 break;*/
6732 }
6733 }
6734
6735 $this->db->begin();
6736
6737 $table_element = $this->table_element;
6738 if ($table_element == 'categorie') { // TODO Rename table llx_categories_extrafields into llx_categorie_extrafields so we can remove this.
6739 $table_element = 'categories'; // For compatibility
6740 }
6741
6742 dol_syslog(get_class($this)."::insertExtraLanguages delete then insert", LOG_DEBUG);
6743
6744 foreach ($new_array_languages as $key => $langcodearray) { // $key = 'name', 'town', ...
6745 foreach ($langcodearray as $langcode => $value) {
6746 $sql_del = "DELETE FROM ".$this->db->prefix()."object_lang";
6747 $sql_del .= " WHERE fk_object = ".((int) $this->id)." AND property = '".$this->db->escape($key)."' AND type_object = '".$this->db->escape($table_element)."'";
6748 $sql_del .= " AND lang = '".$this->db->escape($langcode)."'";
6749 $this->db->query($sql_del);
6750
6751 if ($value !== '') {
6752 $sql = "INSERT INTO ".$this->db->prefix()."object_lang (fk_object, property, type_object, lang, value";
6753 $sql .= ") VALUES (".$this->id.", '".$this->db->escape($key)."', '".$this->db->escape($table_element)."', '".$this->db->escape($langcode)."', '".$this->db->escape($value)."'";
6754 $sql .= ")";
6755
6756 $resql = $this->db->query($sql);
6757 if (!$resql) {
6758 $this->error = $this->db->lasterror();
6759 $error++;
6760 break;
6761 }
6762 }
6763 }
6764 }
6765
6766 if (!$error && $trigger) {
6767 // Call trigger
6768 $this->context = array('extralanguagesaddupdate'=>1);
6769 $result = $this->call_trigger($trigger, $userused);
6770 if ($result < 0) {
6771 $error++;
6772 }
6773 // End call trigger
6774 }
6775
6776 if ($error) {
6777 $this->db->rollback();
6778 return -1;
6779 } else {
6780 $this->db->commit();
6781 return 1;
6782 }
6783 } else {
6784 return 0;
6785 }
6786 }
6787
6798 public function updateExtraField($key, $trigger = null, $userused = null)
6799 {
6800 global $conf, $langs, $user, $hookmanager;
6801
6802 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6803 return 0;
6804 }
6805
6806 if (empty($userused)) {
6807 $userused = $user;
6808 }
6809
6810 $error = 0;
6811
6812 if (!empty($this->array_options) && isset($this->array_options["options_".$key])) {
6813 // Check parameters
6814 $langs->load('admin');
6815 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6816 $extrafields = new ExtraFields($this->db);
6817 $extrafields->fetch_name_optionals_label($this->table_element);
6818
6819 $value = $this->array_options["options_".$key];
6820
6821 $attributeKey = $key;
6822 $attributeType = $extrafields->attributes[$this->table_element]['type'][$key];
6823 $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key];
6824 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key];
6825 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
6826 $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
6827 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$key];
6828
6829 // Similar code than into insertExtraFields
6830 if ($attributeRequired) {
6831 $mandatorypb = false;
6832 if ($attributeType == 'link' && $this->array_options["options_".$key] == '-1') {
6833 $mandatorypb = true;
6834 }
6835 if ($this->array_options["options_".$key] === '') {
6836 $mandatorypb = true;
6837 }
6838 if ($mandatorypb) {
6839 $langs->load("errors");
6840 dol_syslog("Mandatory field 'options_".$key."' is empty during update and set to required into definition of extrafields");
6841 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6842 return -1;
6843 }
6844 }
6845
6846 // $new_array_options will be used for direct update, so must contains formated data for the UPDATE.
6847 $new_array_options = $this->array_options;
6848
6849 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6850 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6851 if (!empty($attrfieldcomputed)) {
6852 if (getDolGlobalString('MAIN_STORE_COMPUTED_EXTRAFIELDS')) {
6853 $value = dol_eval($attrfieldcomputed, 1, 0, '2');
6854 dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
6855
6856 $new_array_options["options_".$key] = $value;
6857
6858 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6859 } else {
6860 $new_array_options["options_".$key] = null;
6861
6862 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6863 }
6864 }
6865
6866 switch ($attributeType) {
6867 case 'int':
6868 if (!is_numeric($value) && $value != '') {
6869 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6870 return -1;
6871 } elseif ($value === '') {
6872 $new_array_options["options_".$key] = null;
6873
6874 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6875 }
6876 break;
6877 case 'price':
6878 case 'double':
6879 $value = price2num($value);
6880 if (!is_numeric($value) && $value != '') {
6881 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6882 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6883 return -1;
6884 } elseif ($value === '') {
6885 $value = null;
6886 }
6887 //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6888 $new_array_options["options_".$key] = $value;
6889
6890 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6891 break;
6892 /*case 'select': // Not required, we chosed value='0' for undefined values
6893 if ($value=='-1')
6894 {
6895 $new_array_options["options_".$key] = $value;
6896
6897 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6898 }
6899 break;*/
6900 case 'password':
6901 $algo = '';
6902 if ($this->array_options["options_".$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
6903 // If there is an encryption choice, we use it to crypt data before insert
6904 $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
6905 $algo = reset($tmparrays);
6906 if ($algo != '') {
6907 //global $action; // $action may be 'create', 'update', 'update_extras'...
6908 //var_dump($action);
6909 //var_dump($this->oldcopy);exit;
6910 //var_dump($key.' '.$this->array_options["options_".$key].' '.$algo);
6911 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
6912 //var_dump($this->oldcopy->array_options["options_".$key]); var_dump($this->array_options["options_".$key]);
6913 if (isset($this->oldcopy->array_options["options_".$key]) && $this->array_options["options_".$key] == $this->oldcopy->array_options["options_".$key]) { // If old value crypted in database is same than submited new value, it means we don't change it, so we don't update.
6914 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
6915 if (!preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) {
6916 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]); // warning, must be called when on the master
6917 } else {
6918 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
6919 }
6920 } else {
6921 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
6922 }
6923 } else {
6924 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
6925 if (!preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) {
6926 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]);
6927 } else {
6928 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
6929 }
6930 } else {
6931 $new_array_options["options_".$key] = dol_hash($this->array_options["options_".$key], $algo);
6932 }
6933 }
6934 } else {
6935 if ($algo == 'dolcrypt' && !preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) { // dolibarr reversible encryption
6936 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]); // warning, must be called when on the master
6937 } else {
6938 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
6939 }
6940 }
6941 } else {
6942 // No encryption
6943 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
6944 }
6945 } else { // Common usage
6946 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
6947 }
6948
6949 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6950 break;
6951 case 'date':
6952 case 'datetime':
6953 if (empty($this->array_options["options_".$key])) {
6954 $new_array_options["options_".$key] = null;
6955
6956 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6957 } else {
6958 $new_array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key]);
6959 }
6960 break;
6961 case 'datetimegmt':
6962 if (empty($this->array_options["options_".$key])) {
6963 $new_array_options["options_".$key] = null;
6964
6965 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6966 } else {
6967 $new_array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key], 'gmt');
6968 }
6969 break;
6970 case 'boolean':
6971 if (empty($this->array_options["options_".$key])) {
6972 $new_array_options["options_".$key] = null;
6973
6974 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6975 }
6976 break;
6977 case 'link':
6978 if ($this->array_options["options_".$key] === '') {
6979 $new_array_options["options_".$key] = null;
6980
6981 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6982 }
6983 break;
6984 /*
6985 case 'link':
6986 $param_list = array_keys($attributeParam['options']);
6987 // 0 : ObjectName
6988 // 1 : classPath
6989 $InfoFieldList = explode(":", $param_list[0]);
6990 dol_include_once($InfoFieldList[1]);
6991 if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
6992 {
6993 if ($value == '-1') // -1 is key for no defined in combo list of objects
6994 {
6995 $new_array_options[$key] = '';
6996 } elseif ($value) {
6997 $object = new $InfoFieldList[0]($this->db);
6998 if (is_numeric($value)) $res = $object->fetch($value); // Common case
6999 else $res = $object->fetch('', $value); // For compatibility
7000
7001 if ($res > 0) $new_array_options[$key] = $object->id;
7002 else {
7003 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
7004 $this->db->rollback();
7005 return -1;
7006 }
7007 }
7008 } else {
7009 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
7010 }
7011 break;
7012 */
7013 case 'checkbox':
7014 case 'chkbxlst':
7015 $new_array_options = array();
7016 if (is_array($this->array_options["options_".$key])) {
7017 $new_array_options["options_".$key] = implode(',', $this->array_options["options_".$key]);
7018 } else {
7019 $new_array_options["options_".$key] = $this->array_options["options_".$key];
7020 }
7021
7022 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7023 break;
7024 }
7025
7026 $this->db->begin();
7027
7028 $linealreadyfound = 0;
7029
7030 // 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)
7031 $table_element = $this->table_element;
7032 if ($table_element == 'categorie') { // TODO Rename table llx_categories_extrafields into llx_categorie_extrafields so we can remove this.
7033 $table_element = 'categories'; // For compatibility
7034 }
7035
7036 $sql = "SELECT COUNT(rowid) as nb FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
7037 $resql = $this->db->query($sql);
7038 if ($resql) {
7039 $tmpobj = $this->db->fetch_object($resql);
7040 if ($tmpobj) {
7041 $linealreadyfound = $tmpobj->nb;
7042 }
7043 }
7044
7045 //var_dump('linealreadyfound='.$linealreadyfound.' sql='.$sql); exit;
7046 if ($linealreadyfound) {
7047 if ($this->array_options["options_".$key] === null) {
7048 $sql = "UPDATE ".$this->db->prefix().$table_element."_extrafields SET ".$key." = null";
7049 } else {
7050 $sql = "UPDATE ".$this->db->prefix().$table_element."_extrafields SET ".$key." = '".$this->db->escape($new_array_options["options_".$key])."'";
7051 }
7052 $sql .= " WHERE fk_object = ".((int) $this->id);
7053
7054 $resql = $this->db->query($sql);
7055 if (!$resql) {
7056 $error++;
7057 $this->error = $this->db->lasterror();
7058 }
7059 } else {
7060 $result = $this->insertExtraFields('', $user);
7061 if ($result < 0) {
7062 $error++;
7063 }
7064 }
7065
7066 if (!$error) {
7067 $parameters = array('key'=>$key);
7068 $reshook = $hookmanager->executeHooks('updateExtraFieldBeforeCommit', $parameters, $this, $action);
7069 if ($reshook < 0) {
7070 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
7071 }
7072 }
7073
7074 if (!$error && $trigger) {
7075 // Call trigger
7076 $this->context = array('extrafieldupdate'=>1);
7077 $result = $this->call_trigger($trigger, $userused);
7078 if ($result < 0) {
7079 $error++;
7080 }
7081 // End call trigger
7082 }
7083
7084 if ($error) {
7085 dol_syslog(__METHOD__.$this->error, LOG_ERR);
7086 $this->db->rollback();
7087 return -1;
7088 } else {
7089 $this->db->commit();
7090 return 1;
7091 }
7092 } else {
7093 return 0;
7094 }
7095 }
7096
7107 public function updateExtraLanguages($key, $trigger = null, $userused = null)
7108 {
7109 global $conf, $langs, $user;
7110
7111 if (empty($userused)) {
7112 $userused = $user;
7113 }
7114
7115 $error = 0;
7116
7117 if (getDolGlobalString('MAIN_EXTRALANGUAGES_DISABLED')) {
7118 return 0; // For avoid conflicts if trigger used
7119 }
7120
7121 return 0;
7122 }
7123
7124
7139 public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0, $nonewbutton = 0)
7140 {
7141 global $conf, $langs, $form;
7142
7143 if (!is_object($form)) {
7144 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
7145 $form = new Form($this->db);
7146 }
7147
7148 if (!empty($this->fields)) {
7149 $val = $this->fields[$key];
7150 }
7151
7152 // Validation tests and output
7153 $fieldValidationErrorMsg = '';
7154 $validationClass = '';
7155 $fieldValidationErrorMsg = $this->getFieldError($key);
7156 if (!empty($fieldValidationErrorMsg)) {
7157 $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
7158 } else {
7159 $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
7160 }
7161
7162 $out = '';
7163 $type = '';
7164 $isDependList = 0;
7165 $param = array();
7166 $param['options'] = array();
7167 $reg = array();
7168 $size = !empty($this->fields[$key]['size']) ? $this->fields[$key]['size'] : 0;
7169 // Because we work on extrafields
7170 if (preg_match('/^(integer|link):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7171 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
7172 $type = 'link';
7173 } elseif (preg_match('/^(integer|link):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7174 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7175 $type = 'link';
7176 } elseif (preg_match('/^(integer|link):(.*):(.*)/i', $val['type'], $reg)) {
7177 $param['options'] = array($reg[2].':'.$reg[3] => 'N');
7178 $type = 'link';
7179 } elseif (preg_match('/^(sellist):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7180 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
7181 $type = 'sellist';
7182 } elseif (preg_match('/^(sellist):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7183 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7184 $type = 'sellist';
7185 } elseif (preg_match('/^(sellist):(.*):(.*)/i', $val['type'], $reg)) {
7186 $param['options'] = array($reg[2].':'.$reg[3] => 'N');
7187 $type = 'sellist';
7188 } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
7189 $param['options'] = array($reg[1] => 'N');
7190 $type = 'chkbxlst';
7191 } elseif (preg_match('/varchar\‍((\d+)\‍)/', $val['type'], $reg)) {
7192 $param['options'] = array();
7193 $type = 'varchar';
7194 $size = $reg[1];
7195 } elseif (preg_match('/varchar/', $val['type'])) {
7196 $param['options'] = array();
7197 $type = 'varchar';
7198 } else {
7199 $param['options'] = array();
7200 $type = $this->fields[$key]['type'];
7201 }
7202 //var_dump($type); var_dump($param['options']);
7203
7204 // Special case that force options and type ($type can be integer, varchar, ...)
7205 if (!empty($this->fields[$key]['arrayofkeyval']) && is_array($this->fields[$key]['arrayofkeyval'])) {
7206 $param['options'] = $this->fields[$key]['arrayofkeyval'];
7207 $type = (($this->fields[$key]['type']=='checkbox') ? $this->fields[$key]['type'] : 'select');
7208 }
7209
7210 $label = $this->fields[$key]['label'];
7211 //$elementtype=$this->fields[$key]['elementtype']; // Seems not used
7212 $default = (!empty($this->fields[$key]['default']) ? $this->fields[$key]['default'] : '');
7213 $computed = (!empty($this->fields[$key]['computed']) ? $this->fields[$key]['computed'] : '');
7214 $unique = (!empty($this->fields[$key]['unique']) ? $this->fields[$key]['unique'] : 0);
7215 $required = (!empty($this->fields[$key]['required']) ? $this->fields[$key]['required'] : 0);
7216 $autofocusoncreate = (!empty($this->fields[$key]['autofocusoncreate']) ? $this->fields[$key]['autofocusoncreate'] : 0);
7217
7218 $langfile = (!empty($this->fields[$key]['langfile']) ? $this->fields[$key]['langfile'] : '');
7219 $list = (!empty($this->fields[$key]['list']) ? $this->fields[$key]['list'] : 0);
7220 $hidden = (in_array(abs($this->fields[$key]['visible']), array(0, 2)) ? 1 : 0);
7221
7222 $objectid = $this->id;
7223
7224 if ($computed) {
7225 if (!preg_match('/^search_/', $keyprefix)) {
7226 return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
7227 } else {
7228 return '';
7229 }
7230 }
7231
7232 // Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine
7233 if (empty($morecss) && !empty($val['css'])) {
7234 $morecss = $val['css'];
7235 } elseif (empty($morecss)) {
7236 if ($type == 'date') {
7237 $morecss = 'minwidth100imp';
7238 } elseif ($type == 'datetime' || $type == 'link') { // link means an foreign key to another primary id
7239 $morecss = 'minwidth200imp';
7240 } elseif (in_array($type, array('int', 'integer', 'price')) || preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', $type)) {
7241 $morecss = 'maxwidth75';
7242 } elseif ($type == 'url') {
7243 $morecss = 'minwidth400';
7244 } elseif ($type == 'boolean') {
7245 $morecss = '';
7246 } else {
7247 if (round($size) < 12) {
7248 $morecss = 'minwidth100';
7249 } elseif (round($size) <= 48) {
7250 $morecss = 'minwidth200';
7251 } else {
7252 $morecss = 'minwidth400';
7253 }
7254 }
7255 }
7256
7257 // Add validation state class
7258 if (!empty($validationClass)) {
7259 $morecss.= $validationClass;
7260 }
7261
7262 if (in_array($type, array('date'))) {
7263 $tmp = explode(',', $size);
7264 $newsize = $tmp[0];
7265 $showtime = 0;
7266
7267 // Do not show current date when field not required (see selectDate() method)
7268 if (!$required && $value == '') {
7269 $value = '-1';
7270 }
7271
7272 // TODO Must also support $moreparam
7273 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
7274 } elseif (in_array($type, array('datetime'))) {
7275 $tmp = explode(',', $size);
7276 $newsize = $tmp[0];
7277 $showtime = 1;
7278
7279 // Do not show current date when field not required (see selectDate() method)
7280 if (!$required && $value == '') {
7281 $value = '-1';
7282 }
7283
7284 // TODO Must also support $moreparam
7285 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
7286 } elseif (in_array($type, array('duration'))) {
7287 $out = $form->select_duration($keyprefix.$key.$keysuffix, $value, 0, 'text', 0, 1);
7288 } elseif (in_array($type, array('int', 'integer'))) {
7289 $tmp = explode(',', $size);
7290 $newsize = $tmp[0];
7291 $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' : '').'>';
7292 } elseif (in_array($type, array('real'))) {
7293 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7294 } elseif (preg_match('/varchar/', $type)) {
7295 $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' : '').'>';
7296 } elseif (in_array($type, array('email', 'mail', 'phone', 'url', 'ip'))) {
7297 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7298 } elseif (preg_match('/^text/', $type)) {
7299 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7300 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7301 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
7302 $out = $doleditor->Create(1);
7303 } else {
7304 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7305 }
7306 } elseif (preg_match('/^html/', $type)) {
7307 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7308 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7309 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_5, '90%');
7310 $out = $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
7311 } else {
7312 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7313 }
7314 } elseif ($type == 'boolean') {
7315 $checked = '';
7316 if (!empty($value)) {
7317 $checked = ' checked value="1" ';
7318 } else {
7319 $checked = ' value="1" ';
7320 }
7321 $out = '<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
7322 } elseif ($type == 'price') {
7323 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7324 $value = price($value);
7325 }
7326 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '.$langs->getCurrencySymbol($conf->currency);
7327 } elseif (preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', $type)) {
7328 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7329 $value = price($value);
7330 }
7331 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
7332 } elseif ($type == 'select') { // combo list
7333 $out = '';
7334 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
7335 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7336 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7337 }
7338
7339 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7340 if ((!isset($this->fields[$key]['default'])) || ($this->fields[$key]['notnull'] != 1)) {
7341 $out .= '<option value="0">&nbsp;</option>';
7342 }
7343 foreach ($param['options'] as $keyb => $valb) {
7344 if ((string) $keyb == '') {
7345 continue;
7346 }
7347 if (strpos($valb, "|") !== false) {
7348 list($valb, $parent) = explode('|', $valb);
7349 }
7350 $out .= '<option value="'.$keyb.'"';
7351 $out .= (((string) $value == (string) $keyb) ? ' selected' : '');
7352 if (!empty($parent)) {
7353 $isDependList = 1;
7354 }
7355 $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
7356 $out .= '>'.$langs->trans($valb).'</option>';
7357 }
7358 $out .= '</select>';
7359 } elseif ($type == 'sellist') {
7360 $out = '';
7361 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
7362 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7363 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7364 }
7365
7366 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7367 if (is_array($param['options'])) {
7368 $param_list = array_keys($param['options']);
7369 $InfoFieldList = explode(":", $param_list[0], 5);
7370 if (! empty($InfoFieldList[4])) {
7371 $pos = 0;
7372 $parenthesisopen = 0;
7373 while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
7374 if (substr($InfoFieldList[4], $pos, 1) == '(') {
7375 $parenthesisopen++;
7376 }
7377 if (substr($InfoFieldList[4], $pos, 1) == ')') {
7378 $parenthesisopen--;
7379 }
7380 $pos++;
7381 }
7382 $tmpbefore = substr($InfoFieldList[4], 0, $pos);
7383 $tmpafter = substr($InfoFieldList[4], $pos+1);
7384 //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
7385 $InfoFieldList[4] = $tmpbefore;
7386 if ($tmpafter !== '') {
7387 $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
7388 }
7389 //var_dump($InfoFieldList);
7390 }
7391 $parentName = '';
7392 $parentField = '';
7393
7394 // 0 : tableName
7395 // 1 : label field name
7396 // 2 : key fields name (if differ of rowid)
7397 // 3 : key field parent (for dependent lists)
7398 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
7399 // 5 : id category type
7400 // 6 : ids categories list separated by comma for category root
7401 // 7 : sort field
7402 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7403
7404 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7405 if (strpos($InfoFieldList[4], 'extra.') !== false) {
7406 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7407 } else {
7408 $keyList = $InfoFieldList[2].' as rowid';
7409 }
7410 }
7411 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7412 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7413 $keyList .= ', '.$parentField;
7414 }
7415
7416 $filter_categorie = false;
7417 if (count($InfoFieldList) > 5) {
7418 if ($InfoFieldList[0] == 'categorie') {
7419 $filter_categorie = true;
7420 }
7421 }
7422
7423 if ($filter_categorie === false) {
7424 $fields_label = explode('|', $InfoFieldList[1]);
7425 if (is_array($fields_label)) {
7426 $keyList .= ', ';
7427 $keyList .= implode(', ', $fields_label);
7428 }
7429
7430 $sqlwhere = '';
7431 $sql = "SELECT " . $keyList;
7432 $sql .= " FROM " . $this->db->prefix() . $InfoFieldList[0];
7433 if (!empty($InfoFieldList[4])) {
7434 // can use SELECT request
7435 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7436 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7437 }
7438
7439 // current object id can be use into filter
7440 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7441 $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
7442 } else {
7443 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7444 }
7445
7446 // We have to join on extrafield table
7447 $errstr = '';
7448 if (strpos($InfoFieldList[4], 'extra') !== false) {
7449 $sql .= " as main, " . $this->db->prefix() . $InfoFieldList[0] . "_extrafields as extra";
7450 $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2];
7451 $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
7452 } else {
7453 $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
7454 }
7455 } else {
7456 $sqlwhere .= ' WHERE 1=1';
7457 }
7458 // Some tables may have field, some other not. For the moment we disable it.
7459 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7460 $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7461 }
7462 $sql .= $sqlwhere;
7463 //print $sql;
7464
7465 // Note: $InfoFieldList can be 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter[:CategoryIdType[:CategoryIdList[:Sortfield]]]]]]'
7466 if (isset($InfoFieldList[7]) && preg_match('/^[a-z0-9_\-,]+$/i', $InfoFieldList[7])) {
7467 $sql .= " ORDER BY ".$this->db->escape($InfoFieldList[7]);
7468 } else {
7469 $sql .= " ORDER BY ".$this->db->sanitize(implode(', ', $fields_label));
7470 }
7471
7472 dol_syslog(get_class($this) . '::showInputField type=sellist', LOG_DEBUG);
7473 $resql = $this->db->query($sql);
7474 if ($resql) {
7475 $out .= '<option value="0">&nbsp;</option>';
7476 $num = $this->db->num_rows($resql);
7477 $i = 0;
7478 while ($i < $num) {
7479 $labeltoshow = '';
7480 $obj = $this->db->fetch_object($resql);
7481
7482 // Several field into label (eq table:code|libelle:rowid)
7483 $notrans = false;
7484 $fields_label = explode('|', $InfoFieldList[1]);
7485 if (count($fields_label) > 1) {
7486 $notrans = true;
7487 foreach ($fields_label as $field_toshow) {
7488 $labeltoshow .= $obj->$field_toshow . ' ';
7489 }
7490 } else {
7491 $labeltoshow = $obj->{$InfoFieldList[1]};
7492 }
7493 $labeltoshow = dol_trunc($labeltoshow, 45);
7494
7495 if ($value == $obj->rowid) {
7496 foreach ($fields_label as $field_toshow) {
7497 $translabel = $langs->trans($obj->$field_toshow);
7498 if ($translabel != $obj->$field_toshow) {
7499 $labeltoshow = dol_trunc($translabel) . ' ';
7500 } else {
7501 $labeltoshow = dol_trunc($obj->$field_toshow) . ' ';
7502 }
7503 }
7504 $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7505 } else {
7506 if (!$notrans) {
7507 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7508 if ($translabel != $obj->{$InfoFieldList[1]}) {
7509 $labeltoshow = dol_trunc($translabel, 18);
7510 } else {
7511 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]});
7512 }
7513 }
7514 if (empty($labeltoshow)) {
7515 $labeltoshow = '(not defined)';
7516 }
7517 if ($value == $obj->rowid) {
7518 $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7519 }
7520
7521 if (!empty($InfoFieldList[3]) && $parentField) {
7522 $parent = $parentName . ':' . $obj->{$parentField};
7523 $isDependList = 1;
7524 }
7525
7526 $out .= '<option value="' . $obj->rowid . '"';
7527 $out .= ($value == $obj->rowid ? ' selected' : '');
7528 $out .= (!empty($parent) ? ' parent="' . $parent . '"' : '');
7529 $out .= '>' . $labeltoshow . '</option>';
7530 }
7531
7532 $i++;
7533 }
7534 $this->db->free($resql);
7535 } else {
7536 print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
7537 }
7538 } else {
7539 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7540 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
7541 $out .= '<option value="0">&nbsp;</option>';
7542 foreach ($data as $data_key => $data_value) {
7543 $out .= '<option value="' . $data_key . '"';
7544 $out .= ($value == $data_key ? ' selected' : '');
7545 $out .= '>' . $data_value . '</option>';
7546 }
7547 }
7548 }
7549 $out .= '</select>';
7550 } elseif ($type == 'checkbox') {
7551 $value_arr = explode(',', $value);
7552 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ? null : $param['options']), $value_arr, '', 0, $morecss, 0, '100%');
7553 } elseif ($type == 'radio') {
7554 $out = '';
7555 foreach ($param['options'] as $keyopt => $valopt) {
7556 $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
7557 $out .= ' value="'.$keyopt.'"';
7558 $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
7559 $out .= ($value == $keyopt ? 'checked' : '');
7560 $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$valopt.'</label><br>';
7561 }
7562 } elseif ($type == 'chkbxlst') {
7563 if (is_array($value)) {
7564 $value_arr = $value;
7565 } else {
7566 $value_arr = explode(',', $value);
7567 }
7568
7569 if (is_array($param['options'])) {
7570 $param_list = array_keys($param['options']);
7571 $InfoFieldList = explode(":", $param_list[0]);
7572 $parentName = '';
7573 $parentField = '';
7574 // 0 : tableName
7575 // 1 : label field name
7576 // 2 : key fields name (if differ of rowid)
7577 // 3 : key field parent (for dependent lists)
7578 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
7579 // 5 : id category type
7580 // 6 : ids categories list separated by comma for category root
7581 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7582
7583 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7584 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7585 $keyList .= ', '.$parentField;
7586 }
7587 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7588 if (strpos($InfoFieldList[4], 'extra.') !== false) {
7589 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7590 } else {
7591 $keyList = $InfoFieldList[2].' as rowid';
7592 }
7593 }
7594
7595 $filter_categorie = false;
7596 if (count($InfoFieldList) > 5) {
7597 if ($InfoFieldList[0] == 'categorie') {
7598 $filter_categorie = true;
7599 }
7600 }
7601
7602 if ($filter_categorie === false) {
7603 $fields_label = explode('|', $InfoFieldList[1]);
7604 if (is_array($fields_label)) {
7605 $keyList .= ', ';
7606 $keyList .= implode(', ', $fields_label);
7607 }
7608
7609 $sqlwhere = '';
7610 $sql = "SELECT " . $keyList;
7611 $sql .= ' FROM ' . $this->db->prefix() . $InfoFieldList[0];
7612 if (!empty($InfoFieldList[4])) {
7613 // can use SELECT request
7614 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7615 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7616 }
7617
7618 // current object id can be use into filter
7619 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7620 $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
7621 } else {
7622 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7623 }
7624
7625 // We have to join on extrafield table
7626 if (strpos($InfoFieldList[4], 'extra') !== false) {
7627 $sql .= ' as main, ' . $this->db->prefix() . $InfoFieldList[0] . '_extrafields as extra';
7628 $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2] . " AND " . $InfoFieldList[4];
7629 } else {
7630 $sqlwhere .= " WHERE " . $InfoFieldList[4];
7631 }
7632 } else {
7633 $sqlwhere .= ' WHERE 1=1';
7634 }
7635 // Some tables may have field, some other not. For the moment we disable it.
7636 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7637 $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7638 }
7639 // $sql.=preg_replace('/^ AND /','',$sqlwhere);
7640 // print $sql;
7641
7642 $sql .= $sqlwhere;
7643 dol_syslog(get_class($this) . '::showInputField type=chkbxlst', LOG_DEBUG);
7644 $resql = $this->db->query($sql);
7645 if ($resql) {
7646 $num = $this->db->num_rows($resql);
7647 $i = 0;
7648
7649 $data = array();
7650
7651 while ($i < $num) {
7652 $labeltoshow = '';
7653 $obj = $this->db->fetch_object($resql);
7654
7655 $notrans = false;
7656 // Several field into label (eq table:code|libelle:rowid)
7657 $fields_label = explode('|', $InfoFieldList[1]);
7658 if (count($fields_label) > 1) {
7659 $notrans = true;
7660 foreach ($fields_label as $field_toshow) {
7661 $labeltoshow .= $obj->$field_toshow . ' ';
7662 }
7663 } else {
7664 $labeltoshow = $obj->{$InfoFieldList[1]};
7665 }
7666 $labeltoshow = dol_trunc($labeltoshow, 45);
7667
7668 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7669 foreach ($fields_label as $field_toshow) {
7670 $translabel = $langs->trans($obj->$field_toshow);
7671 if ($translabel != $obj->$field_toshow) {
7672 $labeltoshow = dol_trunc($translabel, 18) . ' ';
7673 } else {
7674 $labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
7675 }
7676 }
7677
7678 $data[$obj->rowid] = $labeltoshow;
7679 } else {
7680 if (!$notrans) {
7681 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7682 if ($translabel != $obj->{$InfoFieldList[1]}) {
7683 $labeltoshow = dol_trunc($translabel, 18);
7684 } else {
7685 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
7686 }
7687 }
7688 if (empty($labeltoshow)) {
7689 $labeltoshow = '(not defined)';
7690 }
7691
7692 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7693 $data[$obj->rowid] = $labeltoshow;
7694 }
7695
7696 if (!empty($InfoFieldList[3]) && $parentField) {
7697 $parent = $parentName . ':' . $obj->{$parentField};
7698 $isDependList = 1;
7699 }
7700
7701 $data[$obj->rowid] = $labeltoshow;
7702 }
7703
7704 $i++;
7705 }
7706 $this->db->free($resql);
7707
7708 $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, '', 0, $morecss, 0, '100%');
7709 } else {
7710 print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
7711 }
7712 } else {
7713 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7714 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
7715 $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, '', 0, $morecss, 0, '100%');
7716 }
7717 }
7718 } elseif ($type == 'link') {
7719 // $param_list='ObjectName:classPath[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
7720 // Filter can contains some ':' inside.
7721 $param_list = array_keys($param['options']);
7722 $param_list_array = explode(':', $param_list[0], 4);
7723
7724 $showempty = (($required && $default != '') ? 0 : 1);
7725
7726 if (!preg_match('/search_/', $keyprefix)) {
7727 if (!empty($param_list_array[2])) { // If the entry into $fields is set to add a create button
7728 if (!empty($this->fields[$key]['picto'])) {
7729 $morecss .= ' widthcentpercentminusxx';
7730 } else {
7731 $morecss .= ' widthcentpercentminusx';
7732 }
7733 } else {
7734 if (!empty($this->fields[$key]['picto'])) {
7735 $morecss .= ' widthcentpercentminusx';
7736 }
7737 }
7738 }
7739 $objectfield = $this->element.($this->module ? '@'.$this->module : '').':'.$key.$keysuffix;
7740 $out = $form->selectForForms($param_list_array[0], $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, $moreparam, 0, (empty($val['disabled']) ? 0 : 1), '', $objectfield);
7741
7742 if (!empty($param_list_array[2])) { // If the entry into $fields is set, we must add a create button
7743 if ((!GETPOSTISSET('backtopage') || strpos(GETPOST('backtopage'), $_SERVER['PHP_SELF']) === 0) // // To avoid to open several times the 'Plus' button (we accept only one level)
7744 && empty($val['disabled']) && empty($nonewbutton)) { // and to avoid to show the button if the field is protected by a "disabled".
7745 list($class, $classfile) = explode(':', $param_list[0]);
7746 if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) {
7747 $url_path = dol_buildpath(dirname(dirname($classfile)).'/card.php', 1);
7748 } else {
7749 $url_path = dol_buildpath(dirname(dirname($classfile)).'/'.strtolower($class).'_card.php', 1);
7750 }
7751 $paramforthenewlink = '';
7752 $paramforthenewlink .= (GETPOSTISSET('action') ? '&action='.GETPOST('action', 'aZ09') : '');
7753 $paramforthenewlink .= (GETPOSTISSET('id') ? '&id='.GETPOST('id', 'int') : '');
7754 $paramforthenewlink .= (GETPOSTISSET('origin') ? '&origin='.GETPOST('origin', 'aZ09') : '');
7755 $paramforthenewlink .= (GETPOSTISSET('originid') ? '&originid='.GETPOST('originid', 'int') : '');
7756 $paramforthenewlink .= '&fk_'.strtolower($class).'=--IDFORBACKTOPAGE--';
7757 // TODO Add Javascript code to add input fields already filled into $paramforthenewlink so we won't loose them when going back to main page
7758 $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>';
7759 }
7760 }
7761 } elseif ($type == 'password') {
7762 // If prefix is 'search_', field is used as a filter, we use a common text field.
7763 if ($keyprefix.$key.$keysuffix == 'pass_crypted') {
7764 $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="pass" id="pass" value="" '.($moreparam ? $moreparam : '').'>';
7765 $out .= '<input type="hidden" name="pass_crypted" id="pass_crypted" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
7766 } else {
7767 $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
7768 }
7769 } elseif ($type == 'array') {
7770 $newval = $val;
7771 $newval['type'] = 'varchar(256)';
7772
7773 $out = '';
7774 if (!empty($value)) {
7775 foreach ($value as $option) {
7776 $out .= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
7777 $out .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $morecss).'<br></span>';
7778 }
7779 }
7780 $out .= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
7781
7782 $newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
7783 $newInput .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $morecss).'<br></span>';
7784
7785 if (!empty($conf->use_javascript_ajax)) {
7786 $out .= '
7787 <script nonce="'.getNonce().'">
7788 $(document).ready(function() {
7789 $("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
7790 $("'.dol_escape_js($newInput).'").insertBefore(this);
7791 });
7792
7793 $(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
7794 $(this).parent().remove();
7795 });
7796 });
7797 </script>';
7798 }
7799 }
7800 if (!empty($hidden)) {
7801 $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
7802 }
7803
7804 if ($isDependList == 1) {
7805 $out .= $this->getJSListDependancies('_common');
7806 }
7807 /* Add comments
7808 if ($type == 'date') $out.=' (YYYY-MM-DD)';
7809 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
7810 */
7811
7812 // Display error message for field
7813 if (!empty($fieldValidationErrorMsg) && function_exists('getFieldErrorIcon')) {
7814 $out .= ' '.getFieldErrorIcon($fieldValidationErrorMsg);
7815 }
7816
7817 return $out;
7818 }
7819
7833 public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '')
7834 {
7835 global $conf, $langs, $form;
7836
7837 if (!is_object($form)) {
7838 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
7839 $form = new Form($this->db);
7840 }
7841
7842 $label = empty($val['label']) ? '' : $val['label'];
7843 $type = empty($val['type']) ? '' : $val['type'];
7844 $size = empty($val['css']) ? '' : $val['css'];
7845 $reg = array();
7846
7847 // Convert var to be able to share same code than showOutputField of extrafields
7848 if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
7849 $type = 'varchar'; // convert varchar(xx) int varchar
7850 $size = $reg[1];
7851 } elseif (preg_match('/varchar/', $type)) {
7852 $type = 'varchar'; // convert varchar(xx) int varchar
7853 }
7854 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
7855 $type = (($this->fields[$key]['type']=='checkbox') ? $this->fields[$key]['type'] : 'select');
7856 }
7857 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
7858 $type = 'link';
7859 }
7860
7861 $default = empty($val['default']) ? '' : $val['default'];
7862 $computed = empty($val['computed']) ? '' : $val['computed'];
7863 $unique = empty($val['unique']) ? '' : $val['unique'];
7864 $required = empty($val['required']) ? '' : $val['required'];
7865 $param = array();
7866 $param['options'] = array();
7867
7868 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
7869 $param['options'] = $val['arrayofkeyval'];
7870 }
7871 if (preg_match('/^integer:([^:]*):([^:]*)/i', $val['type'], $reg)) { // ex: integer:User:user/class/user.class.php
7872 $type = 'link';
7873 $stringforoptions = $reg[1].':'.$reg[2];
7874 // Special case: Force addition of getnomurlparam1 to -1 for users
7875 if ($reg[1] == 'User') {
7876 $stringforoptions .= ':#getnomurlparam1=-1';
7877 }
7878 $param['options'] = array($stringforoptions => $stringforoptions);
7879 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7880 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7881 $type = 'sellist';
7882 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
7883 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
7884 $type = 'sellist';
7885 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
7886 $param['options'] = array($reg[1].':'.$reg[2] => 'N');
7887 $type = 'sellist';
7888 } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
7889 $param['options'] = array($reg[1] => 'N');
7890 $type = 'chkbxlst';
7891 }
7892
7893 $langfile = empty($val['langfile']) ? '' : $val['langfile'];
7894 $list = (empty($val['list']) ? '' : $val['list']);
7895 $help = (empty($val['help']) ? '' : $val['help']);
7896 $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)
7897
7898 if ($hidden) {
7899 return '';
7900 }
7901
7902 // If field is a computed field, value must become result of compute
7903 if ($computed) {
7904 // Make the eval of compute string
7905 //var_dump($computed);
7906 $value = dol_eval($computed, 1, 0, '2');
7907 }
7908
7909 if (empty($morecss)) {
7910 if ($type == 'date') {
7911 $morecss = 'minwidth100imp';
7912 } elseif ($type == 'datetime' || $type == 'timestamp') {
7913 $morecss = 'minwidth200imp';
7914 } elseif (in_array($type, array('int', 'double', 'price'))) {
7915 $morecss = 'maxwidth75';
7916 } elseif ($type == 'url') {
7917 $morecss = 'minwidth400';
7918 } elseif ($type == 'boolean') {
7919 $morecss = '';
7920 } else {
7921 if (is_numeric($size) && round($size) < 12) {
7922 $morecss = 'minwidth100';
7923 } elseif (is_numeric($size) && round($size) <= 48) {
7924 $morecss = 'minwidth200';
7925 } else {
7926 $morecss = 'minwidth400';
7927 }
7928 }
7929 }
7930
7931 // Format output value differently according to properties of field
7932 if (in_array($key, array('rowid', 'ref')) && method_exists($this, 'getNomUrl')) {
7933 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.
7934 $value = $this->getNomUrl(1, '', 0, '', 1);
7935 }
7936 } elseif ($key == 'status' && method_exists($this, 'getLibStatut')) {
7937 $value = $this->getLibStatut(3);
7938 } elseif ($type == 'date') {
7939 if (!empty($value)) {
7940 $value = dol_print_date($value, 'day'); // We suppose dates without time are always gmt (storage of course + output)
7941 } else {
7942 $value = '';
7943 }
7944 } elseif ($type == 'datetime' || $type == 'timestamp') {
7945 if (!empty($value)) {
7946 $value = dol_print_date($value, 'dayhour', 'tzuserrel');
7947 } else {
7948 $value = '';
7949 }
7950 } elseif ($type == 'duration') {
7951 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
7952 if (!is_null($value) && $value !== '') {
7953 $value = convertSecondToTime($value, 'allhourmin');
7954 }
7955 } elseif ($type == 'double' || $type == 'real') {
7956 if (!is_null($value) && $value !== '') {
7957 $value = price($value);
7958 }
7959 } elseif ($type == 'boolean') {
7960 $checked = '';
7961 if (!empty($value)) {
7962 $checked = ' checked ';
7963 }
7964 $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
7965 } elseif ($type == 'mail' || $type == 'email') {
7966 $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
7967 } elseif ($type == 'url') {
7968 $value = dol_print_url($value, '_blank', 32, 1);
7969 } elseif ($type == 'phone') {
7970 $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
7971 } elseif ($type == 'ip') {
7972 $value = dol_print_ip($value, 0);
7973 } elseif ($type == 'price') {
7974 if (!is_null($value) && $value !== '') {
7975 $value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
7976 }
7977 } elseif ($type == 'select') {
7978 $value = isset($param['options'][$value]) ? $param['options'][$value] : '';
7979 if (strpos($value, "|") !== false) {
7980 $value = $langs->trans(explode('|', $value)[0]);
7981 }
7982 } elseif ($type == 'sellist') {
7983 $param_list = array_keys($param['options']);
7984 $InfoFieldList = explode(":", $param_list[0]);
7985
7986 $selectkey = "rowid";
7987 $keyList = 'rowid';
7988
7989 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7990 $selectkey = $InfoFieldList[2];
7991 $keyList = $InfoFieldList[2].' as rowid';
7992 }
7993
7994 $fields_label = explode('|', $InfoFieldList[1]);
7995 if (is_array($fields_label)) {
7996 $keyList .= ', ';
7997 $keyList .= implode(', ', $fields_label);
7998 }
7999
8000 $filter_categorie = false;
8001 if (count($InfoFieldList) > 5) {
8002 if ($InfoFieldList[0] == 'categorie') {
8003 $filter_categorie = true;
8004 }
8005 }
8006
8007 $sql = "SELECT ".$keyList;
8008 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
8009 if (strpos($InfoFieldList[4], 'extra') !== false) {
8010 $sql .= ' as main';
8011 }
8012 if ($selectkey == 'rowid' && empty($value)) {
8013 $sql .= " WHERE ".$selectkey." = 0";
8014 } elseif ($selectkey == 'rowid') {
8015 $sql .= " WHERE ".$selectkey." = ".((int) $value);
8016 } else {
8017 $sql .= " WHERE ".$selectkey." = '".$this->db->escape($value)."'";
8018 }
8019
8020 //$sql.= ' AND entity = '.$conf->entity;
8021
8022 dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
8023 $resql = $this->db->query($sql);
8024 if ($resql) {
8025 if ($filter_categorie === false) {
8026 $value = ''; // value was used, so now we reste it to use it to build final output
8027 $numrows = $this->db->num_rows($resql);
8028 if ($numrows) {
8029 $obj = $this->db->fetch_object($resql);
8030
8031 // Several field into label (eq table:code|libelle:rowid)
8032 $fields_label = explode('|', $InfoFieldList[1]);
8033
8034 if (is_array($fields_label) && count($fields_label) > 1) {
8035 foreach ($fields_label as $field_toshow) {
8036 $translabel = '';
8037 if (!empty($obj->$field_toshow)) {
8038 $translabel = $langs->trans($obj->$field_toshow);
8039 }
8040 if ($translabel != $field_toshow) {
8041 $value .= dol_trunc($translabel, 18) . ' ';
8042 } else {
8043 $value .= $obj->$field_toshow . ' ';
8044 }
8045 }
8046 } else {
8047 $translabel = '';
8048 if (!empty($obj->{$InfoFieldList[1]})) {
8049 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
8050 }
8051 if ($translabel != $obj->{$InfoFieldList[1]}) {
8052 $value = dol_trunc($translabel, 18);
8053 } else {
8054 $value = $obj->{$InfoFieldList[1]};
8055 }
8056 }
8057 }
8058 } else {
8059 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
8060
8061 $toprint = array();
8062 $obj = $this->db->fetch_object($resql);
8063 $c = new Categorie($this->db);
8064 $c->fetch($obj->rowid);
8065 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
8066 foreach ($ways as $way) {
8067 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
8068 }
8069 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
8070 }
8071 } else {
8072 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
8073 }
8074 } elseif ($type == 'radio') {
8075 $value = $param['options'][$value];
8076 } elseif ($type == 'checkbox') {
8077 $value_arr = explode(',', $value);
8078 $value = '';
8079 if (is_array($value_arr) && count($value_arr) > 0) {
8080 $toprint = array();
8081 foreach ($value_arr as $keyval => $valueval) {
8082 if (!empty($valueval)) {
8083 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $param['options'][$valueval] . '</li>';
8084 }
8085 }
8086 if (!empty($toprint)) {
8087 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
8088 }
8089 }
8090 } elseif ($type == 'chkbxlst') {
8091 $value_arr = explode(',', $value);
8092
8093 $param_list = array_keys($param['options']);
8094 $InfoFieldList = explode(":", $param_list[0]);
8095
8096 $selectkey = "rowid";
8097 $keyList = 'rowid';
8098
8099 if (count($InfoFieldList) >= 3) {
8100 $selectkey = $InfoFieldList[2];
8101 $keyList = $InfoFieldList[2].' as rowid';
8102 }
8103
8104 $fields_label = explode('|', $InfoFieldList[1]);
8105 if (is_array($fields_label)) {
8106 $keyList .= ', ';
8107 $keyList .= implode(', ', $fields_label);
8108 }
8109
8110 $filter_categorie = false;
8111 if (count($InfoFieldList) > 5) {
8112 if ($InfoFieldList[0] == 'categorie') {
8113 $filter_categorie = true;
8114 }
8115 }
8116
8117 $sql = "SELECT ".$keyList;
8118 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
8119 if (strpos($InfoFieldList[4], 'extra') !== false) {
8120 $sql .= ' as main';
8121 }
8122 // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
8123 // $sql.= ' AND entity = '.$conf->entity;
8124
8125 dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG);
8126 $resql = $this->db->query($sql);
8127 if ($resql) {
8128 if ($filter_categorie === false) {
8129 $value = ''; // value was used, so now we reste it to use it to build final output
8130 $toprint = array();
8131 while ($obj = $this->db->fetch_object($resql)) {
8132 // Several field into label (eq table:code|libelle:rowid)
8133 $fields_label = explode('|', $InfoFieldList[1]);
8134 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8135 if (is_array($fields_label) && count($fields_label) > 1) {
8136 foreach ($fields_label as $field_toshow) {
8137 $translabel = '';
8138 if (!empty($obj->$field_toshow)) {
8139 $translabel = $langs->trans($obj->$field_toshow);
8140 }
8141 if ($translabel != $field_toshow) {
8142 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
8143 } else {
8144 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->$field_toshow . '</li>';
8145 }
8146 }
8147 } else {
8148 $translabel = '';
8149 if (!empty($obj->{$InfoFieldList[1]})) {
8150 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
8151 }
8152 if ($translabel != $obj->{$InfoFieldList[1]}) {
8153 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
8154 } else {
8155 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->{$InfoFieldList[1]} . '</li>';
8156 }
8157 }
8158 }
8159 }
8160 } else {
8161 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
8162
8163 $toprint = array();
8164 while ($obj = $this->db->fetch_object($resql)) {
8165 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8166 $c = new Categorie($this->db);
8167 $c->fetch($obj->rowid);
8168 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
8169 foreach ($ways as $way) {
8170 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
8171 }
8172 }
8173 }
8174 }
8175 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
8176 } else {
8177 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
8178 }
8179 } elseif ($type == 'link') {
8180 $out = '';
8181
8182 // only if something to display (perf)
8183 if ($value) {
8184 $param_list = array_keys($param['options']);
8185 // Example: $param_list='ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
8186 // Example: $param_list='ObjectClass:PathToClass:#getnomurlparam1=-1#getnomurlparam2=customer'
8187
8188 $InfoFieldList = explode(":", $param_list[0]);
8189
8190 $classname = $InfoFieldList[0];
8191 $classpath = $InfoFieldList[1];
8192
8193 // Set $getnomurlparam1 et getnomurlparam2
8194 $getnomurlparam = 3;
8195 $getnomurlparam2 = '';
8196 $regtmp = array();
8197 if (preg_match('/#getnomurlparam1=([^#]*)/', $param_list[0], $regtmp)) {
8198 $getnomurlparam = $regtmp[1];
8199 }
8200 if (preg_match('/#getnomurlparam2=([^#]*)/', $param_list[0], $regtmp)) {
8201 $getnomurlparam2 = $regtmp[1];
8202 }
8203
8204 if (!empty($classpath)) {
8205 dol_include_once($InfoFieldList[1]);
8206
8207 if ($classname && !class_exists($classname)) {
8208 // from V19 of Dolibarr, In some cases link use element instead of class, example project_task
8209 // TODO use newObjectByElement() introduce in V20 by PR #30036 for better errors management
8210 $element_prop = getElementProperties($classname);
8211 if ($element_prop) {
8212 $classname = $element_prop['classname'];
8213 }
8214 }
8215
8216
8217 if ($classname && class_exists($classname)) {
8218 $object = new $classname($this->db);
8219 if ($object->element === 'product') { // Special case for product because default valut of fetch are wrong
8220 $result = $object->fetch($value, '', '', '', 0, 1, 1);
8221 } else {
8222 $result = $object->fetch($value);
8223 }
8224 if ($result > 0) {
8225 if ($object->element === 'product') {
8226 $get_name_url_param_arr = array($getnomurlparam, $getnomurlparam2, 0, -1, 0, '', 0);
8227 if (isset($val['get_name_url_params'])) {
8228 $get_name_url_params = explode(':', $val['get_name_url_params']);
8229 if (!empty($get_name_url_params)) {
8230 $param_num_max = count($get_name_url_param_arr) - 1;
8231 foreach ($get_name_url_params as $param_num => $param_value) {
8232 if ($param_num > $param_num_max) {
8233 break;
8234 }
8235 $get_name_url_param_arr[$param_num] = $param_value;
8236 }
8237 }
8238 }
8239
8243 $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]);
8244 } else {
8245 $value = $object->getNomUrl($getnomurlparam, $getnomurlparam2);
8246 }
8247 } else {
8248 $value = '';
8249 }
8250 }
8251 } else {
8252 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
8253 return 'Error bad setup of extrafield';
8254 }
8255 } else {
8256 $value = '';
8257 }
8258 } elseif ($type == 'password') {
8259 $value = preg_replace('/./i', '*', $value);
8260 } elseif ($type == 'array') {
8261 $value = implode('<br>', $value);
8262 } else { // text|html|varchar
8263 $value = dol_htmlentitiesbr($value);
8264 }
8265
8266 //print $type.'-'.$size.'-'.$value;
8267 $out = $value;
8268
8269 return $out;
8270 }
8271
8278 public function clearFieldError($fieldKey)
8279 {
8280 $this->error = '';
8281 unset($this->validateFieldsErrors[$fieldKey]);
8282 }
8283
8291 public function setFieldError($fieldKey, $msg = '')
8292 {
8293 global $langs;
8294 if (empty($msg)) {
8295 $msg = $langs->trans("UnknowError");
8296 }
8297
8298 $this->error = $this->validateFieldsErrors[$fieldKey] = $msg;
8299 }
8300
8307 public function getFieldError($fieldKey)
8308 {
8309 if (!empty($this->validateFieldsErrors[$fieldKey])) {
8310 return $this->validateFieldsErrors[$fieldKey];
8311 }
8312 return '';
8313 }
8314
8323 public function validateField($fields, $fieldKey, $fieldValue)
8324 {
8325 global $langs;
8326
8327 if (!class_exists('Validate')) {
8328 require_once DOL_DOCUMENT_ROOT . '/core/class/validate.class.php';
8329 }
8330
8331 $this->clearFieldError($fieldKey);
8332
8333 if (!isset($fields[$fieldKey])) {
8334 $this->setFieldError($fieldKey, $langs->trans('FieldNotFoundInObject'));
8335 return false;
8336 }
8337
8338 $val = $fields[$fieldKey];
8339
8340 $param = array();
8341 $param['options'] = array();
8342 $type = $val['type'];
8343
8344 $required = false;
8345 if (isset($val['notnull']) && $val['notnull'] === 1) {
8346 // 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
8347 $required = true;
8348 }
8349
8350 $maxSize = 0;
8351 $minSize = 0;
8352
8353 //
8354 // PREPARE Elements
8355 //
8356 $reg = array();
8357
8358 // Convert var to be able to share same code than showOutputField of extrafields
8359 if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
8360 $type = 'varchar'; // convert varchar(xx) int varchar
8361 $maxSize = $reg[1];
8362 } elseif (preg_match('/varchar/', $type)) {
8363 $type = 'varchar'; // convert varchar(xx) int varchar
8364 }
8365
8366 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8367 $type = 'select';
8368 }
8369
8370 if (!empty($val['type']) && preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8371 $type = 'link';
8372 }
8373
8374 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8375 $param['options'] = $val['arrayofkeyval'];
8376 }
8377
8378 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8379 $type = 'link';
8380 $param['options'] = array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
8381 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
8382 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
8383 $type = 'sellist';
8384 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
8385 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
8386 $type = 'sellist';
8387 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
8388 $param['options'] = array($reg[1].':'.$reg[2] => 'N');
8389 $type = 'sellist';
8390 }
8391
8392 //
8393 // TEST Value
8394 //
8395
8396 // Use Validate class to allow external Modules to use data validation part instead of concentrate all test here (factoring) or just for reuse
8397 $validate = new Validate($this->db, $langs);
8398
8399
8400 // little trick : to perform tests with good performances sort tests by quick to low
8401
8402 //
8403 // COMMON TESTS
8404 //
8405
8406 // Required test and empty value
8407 if ($required && !$validate->isNotEmptyString($fieldValue)) {
8408 $this->setFieldError($fieldKey, $validate->error);
8409 return false;
8410 } elseif (!$required && !$validate->isNotEmptyString($fieldValue)) {
8411 // if no value sent and the field is not mandatory, no need to perform tests
8412 return true;
8413 }
8414
8415 // MAX Size test
8416 if (!empty($maxSize) && !$validate->isMaxLength($fieldValue, $maxSize)) {
8417 $this->setFieldError($fieldKey, $validate->error);
8418 return false;
8419 }
8420
8421 // MIN Size test
8422 if (!empty($minSize) && !$validate->isMinLength($fieldValue, $minSize)) {
8423 $this->setFieldError($fieldKey, $validate->error);
8424 return false;
8425 }
8426
8427 //
8428 // TESTS for TYPE
8429 //
8430
8431 if (in_array($type, array('date', 'datetime', 'timestamp'))) {
8432 if (!$validate->isTimestamp($fieldValue)) {
8433 $this->setFieldError($fieldKey, $validate->error);
8434 return false;
8435 } else {
8436 return true;
8437 }
8438 } elseif ($type == 'duration') {
8439 if (!$validate->isDuration($fieldValue)) {
8440 $this->setFieldError($fieldKey, $validate->error);
8441 return false;
8442 } else {
8443 return true;
8444 }
8445 } elseif (in_array($type, array('double', 'real', 'price'))) {
8446 // is numeric
8447 if (!$validate->isNumeric($fieldValue)) {
8448 $this->setFieldError($fieldKey, $validate->error);
8449 return false;
8450 } else {
8451 return true;
8452 }
8453 } elseif ($type == 'boolean') {
8454 if (!$validate->isBool($fieldValue)) {
8455 $this->setFieldError($fieldKey, $validate->error);
8456 return false;
8457 } else {
8458 return true;
8459 }
8460 } elseif ($type == 'mail') {
8461 if (!$validate->isEmail($fieldValue)) {
8462 $this->setFieldError($fieldKey, $validate->error);
8463 return false;
8464 }
8465 } elseif ($type == 'url') {
8466 if (!$validate->isUrl($fieldValue)) {
8467 $this->setFieldError($fieldKey, $validate->error);
8468 return false;
8469 } else {
8470 return true;
8471 }
8472 } elseif ($type == 'phone') {
8473 if (!$validate->isPhone($fieldValue)) {
8474 $this->setFieldError($fieldKey, $validate->error);
8475 return false;
8476 } else {
8477 return true;
8478 }
8479 } elseif ($type == 'select' || $type == 'radio') {
8480 if (!isset($param['options'][$fieldValue])) {
8481 $this->error = $langs->trans('RequireValidValue');
8482 return false;
8483 } else {
8484 return true;
8485 }
8486 } elseif ($type == 'sellist' || $type == 'chkbxlst') {
8487 $param_list = array_keys($param['options']);
8488 $InfoFieldList = explode(":", $param_list[0]);
8489 $value_arr = explode(',', $fieldValue);
8490 $value_arr = array_map(array($this->db, 'escape'), $value_arr);
8491
8492 $selectkey = "rowid";
8493 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
8494 $selectkey = $InfoFieldList[2];
8495 }
8496
8497 if (!$validate->isInDb($value_arr, $InfoFieldList[0], $selectkey)) {
8498 $this->setFieldError($fieldKey, $validate->error);
8499 return false;
8500 } else {
8501 return true;
8502 }
8503 } elseif ($type == 'link') {
8504 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
8505 $InfoFieldList = explode(":", $param_list[0]);
8506 $classname = $InfoFieldList[0];
8507 $classpath = $InfoFieldList[1];
8508 if (!$validate->isFetchable($fieldValue, $classname, $classpath)) {
8509 $lastIsFetchableError = $validate->error;
8510
8511 // from V19 of Dolibarr, In some cases link use element instead of class, example project_task
8512 if ($validate->isFetchableElement($fieldValue, $classname)) {
8513 return true;
8514 }
8515
8516 $this->setFieldError($fieldKey, $lastIsFetchableError);
8517 return false;
8518 } else {
8519 return true;
8520 }
8521 }
8522
8523 // if no test failled all is ok
8524 return true;
8525 }
8526
8540 public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = '', $display_type = 'card')
8541 {
8542 global $db, $conf, $langs, $action, $form, $hookmanager;
8543
8544 if (!is_object($form)) {
8545 $form = new Form($db);
8546 }
8547 if (!is_object($extrafields)) {
8548 dol_syslog('Bad parameter extrafields for showOptionals', LOG_ERR);
8549 return 'Bad parameter extrafields for showOptionals';
8550 }
8551 if (!is_array($extrafields->attributes[$this->table_element])) {
8552 dol_syslog("extrafields->attributes was not loaded with extrafields->fetch_name_optionals_label(table_element);", LOG_WARNING);
8553 }
8554
8555 $out = '';
8556
8557 $parameters = array('mode'=>$mode, 'params'=>$params, 'keysuffix'=>$keysuffix, 'keyprefix'=>$keyprefix, 'display_type'=>$display_type);
8558 $reshook = $hookmanager->executeHooks('showOptionals', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
8559
8560 if (empty($reshook)) {
8561 if (is_array($extrafields->attributes[$this->table_element]) && key_exists('label', $extrafields->attributes[$this->table_element]) && is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label']) > 0) {
8562 $out .= "\n";
8563 $out .= '<!-- commonobject:showOptionals --> ';
8564 $out .= "\n";
8565
8566 $nbofextrafieldsshown = 0;
8567 $e = 0; // var to manage the modulo (odd/even)
8568
8569 $lastseparatorkeyfound = '';
8570 $extrafields_collapse_num = '';
8571 $extrafields_collapse_num_old = '';
8572 $i = 0;
8573
8574 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $label) {
8575 $i++;
8576
8577 // Show only the key field in params
8578 if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) {
8579 continue;
8580 }
8581
8582 // Test on 'enabled' ('enabled' is different than 'list' = 'visibility')
8583 $enabled = 1;
8584 if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
8585 $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
8586 }
8587 if (empty($enabled)) {
8588 continue;
8589 }
8590
8591 $visibility = 1;
8592 if ($visibility && isset($extrafields->attributes[$this->table_element]['list'][$key])) {
8593 $visibility = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
8594 }
8595
8596 $perms = 1;
8597 if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
8598 $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
8599 }
8600
8601 if (($mode == 'create') && abs($visibility) != 1 && abs($visibility) != 3) {
8602 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list
8603 } elseif (($mode == 'edit') && abs($visibility) != 1 && abs($visibility) != 3 && abs($visibility) != 4) {
8604 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list and <> 4 = not visible at the creation
8605 } elseif ($mode == 'view' && empty($visibility)) {
8606 continue;
8607 }
8608 if (empty($perms)) {
8609 continue;
8610 }
8611
8612 // Load language if required
8613 if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
8614 $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
8615 }
8616
8617 $colspan = 0;
8618 if (is_array($params) && count($params) > 0 && $display_type=='card') {
8619 if (array_key_exists('cols', $params)) {
8620 $colspan = $params['cols'];
8621 } elseif (array_key_exists('colspan', $params)) { // For backward compatibility. Use cols instead now.
8622 $reg = array();
8623 if (preg_match('/colspan="(\d+)"/', $params['colspan'], $reg)) {
8624 $colspan = $reg[1];
8625 } else {
8626 $colspan = $params['colspan'];
8627 }
8628 }
8629 }
8630 $colspan = intval($colspan);
8631
8632 switch ($mode) {
8633 case "view":
8634 $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 formated later
8635 break;
8636 case "create":
8637 case "edit":
8638 // We get the value of property found with GETPOST so it takes into account:
8639 // default values overwrite, restore back to list link, ... (but not 'default value in database' of field)
8640 $check = 'alphanohtml';
8641 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text'))) {
8642 $check = 'restricthtml';
8643 }
8644 $getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, $check, 3); // GETPOST can get value from GET, POST or setup of default values overwrite.
8645 // GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
8646 if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)) {
8647 if (is_array($getposttemp)) {
8648 // $getposttemp is an array but following code expects a comma separated string
8649 $value = implode(",", $getposttemp);
8650 } else {
8651 $value = $getposttemp;
8652 }
8653 } elseif (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('int'))) {
8654 $value =( !empty($this->array_options["options_".$key]) || (isset($this->array_options["options_".$key]) && $this->array_options["options_".$key] === '0')) ? $this->array_options["options_".$key] : '';
8655 } else {
8656 $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.
8657 }
8658 //var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
8659 break;
8660 }
8661
8662 $nbofextrafieldsshown++;
8663
8664 // Output value of the current field
8665 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
8666 $extrafields_collapse_num = $key;
8667 /*
8668 $extrafield_param = $extrafields->attributes[$this->table_element]['param'][$key];
8669 if (!empty($extrafield_param) && is_array($extrafield_param)) {
8670 $extrafield_param_list = array_keys($extrafield_param['options']);
8671
8672 if (count($extrafield_param_list) > 0) {
8673 $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
8674
8675 if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
8676 //$extrafields_collapse_num = $extrafields->attributes[$this->table_element]['pos'][$key];
8677 $extrafields_collapse_num = $key;
8678 }
8679 }
8680 }
8681 */
8682
8683 // if colspan=0 or 1, the second column is not extended, so the separator must be on 2 columns
8684 $out .= $extrafields->showSeparator($key, $this, ($colspan ? $colspan + 1 : 2), $display_type, $mode);
8685
8686 $lastseparatorkeyfound = $key;
8687 } else {
8688 $collapse_group = $extrafields_collapse_num.(!empty($this->id) ? '_'.$this->id : '');
8689
8690 $class = (!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
8691 $csstyle = '';
8692 if (is_array($params) && count($params) > 0) {
8693 if (array_key_exists('class', $params)) {
8694 $class .= $params['class'].' ';
8695 }
8696 if (array_key_exists('style', $params)) {
8697 $csstyle = $params['style'];
8698 }
8699 }
8700
8701 // add html5 elements
8702 $domData = ' data-element="extrafield"';
8703 $domData .= ' data-targetelement="'.$this->element.'"';
8704 $domData .= ' data-targetid="'.$this->id.'"';
8705
8706 $html_id = (empty($this->id) ? '' : 'extrarow-'.$this->element.'_'.$key.'_'.$this->id);
8707 if ($display_type=='card') {
8708 if (getDolGlobalString('MAIN_EXTRAFIELDS_USE_TWO_COLUMS') && ($e % 2) == 0) {
8709 $colspan = 0;
8710 }
8711
8712 if ($action == 'selectlines') {
8713 $colspan++;
8714 }
8715 }
8716
8717 // Convert date into timestamp format (value in memory must be a timestamp)
8718 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date'))) {
8719 $datenotinstring = null;
8720 if (array_key_exists('options_'.$key, $this->array_options)) {
8721 $datenotinstring = $this->array_options['options_'.$key];
8722 if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
8723 $datenotinstring = $this->db->jdate($datenotinstring);
8724 }
8725 }
8726 $datekey = $keyprefix.'options_'.$key.$keysuffix;
8727 $value = (GETPOSTISSET($datekey)) ? dol_mktime(12, 0, 0, GETPOST($datekey.'month', 'int', 3), GETPOST($datekey.'day', 'int', 3), GETPOST($datekey.'year', 'int', 3)) : $datenotinstring;
8728 }
8729 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('datetime'))) {
8730 $datenotinstring = null;
8731 if (array_key_exists('options_'.$key, $this->array_options)) {
8732 $datenotinstring = $this->array_options['options_'.$key];
8733 if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
8734 $datenotinstring = $this->db->jdate($datenotinstring);
8735 }
8736 }
8737 $timekey = $keyprefix.'options_'.$key.$keysuffix;
8738 $value = (GETPOSTISSET($timekey)) ? dol_mktime(GETPOST($timekey.'hour', 'int', 3), GETPOST($timekey.'min', 'int', 3), GETPOST($timekey.'sec', 'int', 3), GETPOST($timekey.'month', 'int', 3), GETPOST($timekey.'day', 'int', 3), GETPOST($timekey.'year', 'int', 3), 'tzuserrel') : $datenotinstring;
8739 }
8740 // Convert float submited string into real php numeric (value in memory must be a php numeric)
8741 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price', 'double'))) {
8742 if (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) {
8743 $value = price2num($value);
8744 } elseif (isset($this->array_options['options_'.$key])) {
8745 $value = $this->array_options['options_'.$key];
8746 }
8747 }
8748
8749 // HTML, text, select, integer and varchar: take into account default value in database if in create mode
8750 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text', 'varchar', 'select', 'radio', 'int', 'boolean'))) {
8751 if ($action == 'create' || $mode == 'create') {
8752 $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) ? $value : $extrafields->attributes[$this->table_element]['default'][$key];
8753 }
8754 }
8755
8756 $labeltoshow = $langs->trans($label);
8757 $helptoshow = $langs->trans($extrafields->attributes[$this->table_element]['help'][$key]);
8758
8759 if ($display_type == 'card') {
8760 $out .= '<tr '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="field_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
8761 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER') && ($action == 'view' || $action == 'valid' || $action == 'editline' || $action == 'confirm_valid' || $action == 'confirm_cancel')) {
8762 $out .= '<td></td>';
8763 }
8764 $out .= '<td class="'.(empty($params['tdclass']) ? 'titlefieldcreate' : $params['tdclass']).' wordbreak';
8765 } elseif ($display_type == 'line') {
8766 $out .= '<div '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="fieldline_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
8767 $out .= '<div style="display: inline-block; padding-right:4px" class="wordbreak';
8768 }
8769 //$out .= "titlefield";
8770 //if (GETPOST('action', 'restricthtml') == 'create') $out.='create';
8771 // BUG #11554 : For public page, use red dot for required fields, instead of bold label
8772 $tpl_context = isset($params["tpl_context"]) ? $params["tpl_context"] : "none";
8773 if ($tpl_context != "public") { // Public page : red dot instead of fieldrequired characters
8774 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
8775 $out .= ' fieldrequired';
8776 }
8777 }
8778 $out .= '">';
8779 if ($tpl_context == "public") { // Public page : red dot instead of fieldrequired characters
8780 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
8781 $out .= $form->textwithpicto($labeltoshow, $helptoshow);
8782 } else {
8783 $out .= $labeltoshow;
8784 }
8785 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
8786 $out .= '&nbsp;<span style="color: red">*</span>';
8787 }
8788 } else {
8789 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
8790 $out .= $form->textwithpicto($labeltoshow, $helptoshow);
8791 } else {
8792 $out .= $labeltoshow;
8793 }
8794 }
8795
8796 $out .= ($display_type == 'card' ? '</td>' : '</div>');
8797
8798 $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
8799 if ($display_type == 'card') {
8800 // 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
8801 $out .= '<td '.($html_id ? 'id="'.$html_id.'" ' : '').' class="valuefieldcreate '.$this->element.'_extras_'.$key.'" '.($colspan ? ' colspan="'.$colspan.'"' : '').'>';
8802 } elseif ($display_type == 'line') {
8803 $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].'">';
8804 }
8805
8806 switch ($mode) {
8807 case "view":
8808 $out .= $extrafields->showOutputField($key, $value, '', $this->table_element);
8809 break;
8810 case "create":
8811 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
8812 break;
8813 case "edit":
8814 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
8815 break;
8816 }
8817
8818 $out .= ($display_type=='card' ? '</td>' : '</div>');
8819
8820 if (getDolGlobalString('MAIN_EXTRAFIELDS_USE_TWO_COLUMS') && (($e % 2) == 1)) {
8821 $out .= ($display_type=='card' ? '</tr>' : '</div>');
8822 } else {
8823 $out .= ($display_type=='card' ? '</tr>' : '</div>');
8824 }
8825
8826 $e++;
8827 }
8828 }
8829 $out .= "\n";
8830 // Add code to manage list depending on others
8831 if (!empty($conf->use_javascript_ajax)) {
8832 $out .= $this->getJSListDependancies();
8833 }
8834
8835 $out .= '<!-- commonobject:showOptionals end --> '."\n";
8836
8837 if (empty($nbofextrafieldsshown)) {
8838 $out = '';
8839 }
8840 }
8841 }
8842
8843 $out .= $hookmanager->resPrint;
8844
8845 return $out;
8846 }
8847
8852 public function getJSListDependancies($type = '_extra')
8853 {
8854 $out = '
8855 <script nonce="'.getNonce().'">
8856 jQuery(document).ready(function() {
8857 function showOptions'.$type.'(child_list, parent_list, orig_select)
8858 {
8859 var val = $("select[name=\""+parent_list+"\"]").val();
8860 var parentVal = parent_list + ":" + val;
8861 if(typeof val == "string"){
8862 if(val != "") {
8863 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
8864 $("select[name=\""+child_list+"\"] option[parent]").remove();
8865 $("select[name=\""+child_list+"\"]").append(options);
8866 } else {
8867 var options = orig_select.find("option[parent]").clone();
8868 $("select[name=\""+child_list+"\"] option[parent]").remove();
8869 $("select[name=\""+child_list+"\"]").append(options);
8870 }
8871 } else if(val > 0) {
8872 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
8873 $("select[name=\""+child_list+"\"] option[parent]").remove();
8874 $("select[name=\""+child_list+"\"]").append(options);
8875 } else {
8876 var options = orig_select.find("option[parent]").clone();
8877 $("select[name=\""+child_list+"\"] option[parent]").remove();
8878 $("select[name=\""+child_list+"\"]").append(options);
8879 }
8880 }
8881 function setListDependencies'.$type.'() {
8882 jQuery("select option[parent]").parent().each(function() {
8883 var orig_select = {};
8884 var child_list = $(this).attr("name");
8885 orig_select[child_list] = $(this).clone();
8886 var parent = $(this).find("option[parent]:first").attr("parent");
8887 var infos = parent.split(":");
8888 var parent_list = infos[0];
8889
8890 //Hide daughters lists
8891 if ($("#"+child_list).val() == 0 && $("#"+parent_list).val() == 0){
8892 $("#"+child_list).hide();
8893 //Show mother lists
8894 } else if ($("#"+parent_list).val() != 0){
8895 $("#"+parent_list).show();
8896 }
8897 //Show the child list if the parent list value is selected
8898 $("select[name=\""+parent_list+"\"]").click(function() {
8899 if ($(this).val() != 0){
8900 $("#"+child_list).show()
8901 }
8902 });
8903
8904 //When we change parent list
8905 $("select[name=\""+parent_list+"\"]").change(function() {
8906 showOptions'.$type.'(child_list, parent_list, orig_select[child_list]);
8907 //Select the value 0 on child list after a change on the parent list
8908 $("#"+child_list).val(0).trigger("change");
8909 //Hide child lists if the parent value is set to 0
8910 if ($(this).val() == 0){
8911 $("#"+child_list).hide();
8912 }
8913 });
8914 });
8915 }
8916
8917 setListDependencies'.$type.'();
8918 });
8919 </script>'."\n";
8920 return $out;
8921 }
8922
8928 public function getRights()
8929 {
8930 global $user;
8931
8932 $module = empty($this->module) ? '' : $this->module;
8933 $element = $this->element;
8934
8935 if ($element == 'facturerec') {
8936 $element = 'facture';
8937 } elseif ($element == 'invoice_supplier_rec') {
8938 return !$user->hasRight('fournisseur', 'facture') ? null : $user->hasRight('fournisseur', 'facture');
8939 } elseif ($module && $user->hasRight($module, $element)) {
8940 // for modules built with ModuleBuilder
8941 return $user->hasRight($module, $element);
8942 }
8943
8944 return $user->rights->$element;
8945 }
8946
8959 public static function commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
8960 {
8961 foreach ($tables as $table) {
8962 $sql = 'UPDATE '.$dbs->prefix().$table.' SET fk_soc = '.((int) $dest_id).' WHERE fk_soc = '.((int) $origin_id);
8963
8964 if (!$dbs->query($sql)) {
8965 if ($ignoreerrors) {
8966 return true; // TODO Not enough. If there is A-B on kept thirdparty and B-C on old one, we must get A-B-C after merge. Not A-B.
8967 }
8968 //$this->errors = $db->lasterror();
8969 return false;
8970 }
8971 }
8972
8973 return true;
8974 }
8975
8988 public static function commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
8989 {
8990 foreach ($tables as $table) {
8991 $sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_product = '.((int) $dest_id).' WHERE fk_product = '.((int) $origin_id);
8992
8993 if (!$dbs->query($sql)) {
8994 if ($ignoreerrors) {
8995 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.
8996 }
8997 //$this->errors = $db->lasterror();
8998 return false;
8999 }
9000 }
9001
9002 return true;
9003 }
9004
9017 public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
9018 {
9019 global $conf;
9020
9021 $buyPrice = 0;
9022
9023 if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && getDolGlobalInt('ForceBuyingPriceIfNull') > 0)) {
9024 // When ForceBuyingPriceIfNull is set
9025 $buyPrice = $unitPrice * (1 - $discountPercent / 100);
9026 } else {
9027 // Get cost price for margin calculation
9028 if (!empty($fk_product) && $fk_product > 0) {
9029 if (getDolGlobalString('MARGIN_TYPE') == 'costprice') {
9030 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
9031 $product = new Product($this->db);
9032 $result = $product->fetch($fk_product);
9033 if ($result <= 0) {
9034 $this->errors[] = 'ErrorProductIdDoesNotExists';
9035 return -1;
9036 }
9037 if ($product->cost_price > 0) {
9038 $buyPrice = $product->cost_price;
9039 } elseif ($product->pmp > 0) {
9040 $buyPrice = $product->pmp;
9041 }
9042 } elseif (getDolGlobalString('MARGIN_TYPE') == 'pmp') {
9043 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
9044 $product = new Product($this->db);
9045 $result = $product->fetch($fk_product);
9046 if ($result <= 0) {
9047 $this->errors[] = 'ErrorProductIdDoesNotExists';
9048 return -1;
9049 }
9050 if ($product->pmp > 0) {
9051 $buyPrice = $product->pmp;
9052 }
9053 }
9054
9055 if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1', 'pmp', 'costprice'))) {
9056 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
9057 $productFournisseur = new ProductFournisseur($this->db);
9058 if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0) {
9059 $buyPrice = $productFournisseur->fourn_unitprice;
9060 } elseif ($result < 0) {
9061 $this->errors[] = $productFournisseur->error;
9062 return -2;
9063 }
9064 }
9065 }
9066 }
9067 return $buyPrice;
9068 }
9069
9070 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
9090 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')
9091 {
9092 // phpcs:enable
9093 global $conf, $user, $langs;
9094
9095 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
9096 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
9097
9098 $sortfield = 'position_name';
9099 $sortorder = 'asc';
9100
9101 $dir = $sdir.'/';
9102 $pdir = '/';
9103
9104 $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
9105 $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
9106
9107 // For backward compatibility
9108 if ($modulepart == 'product') {
9109 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
9110 $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9111 $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9112 }
9113 }
9114
9115 // Defined relative dir to DOL_DATA_ROOT
9116 $relativedir = '';
9117 if ($dir) {
9118 $relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
9119 $relativedir = preg_replace('/^[\\/]/', '', $relativedir);
9120 $relativedir = preg_replace('/[\\/]$/', '', $relativedir);
9121 }
9122
9123 $dirthumb = $dir.'thumbs/';
9124 $pdirthumb = $pdir.'thumbs/';
9125
9126 $return = '<!-- Photo -->'."\n";
9127 $nbphoto = 0;
9128
9129 $filearray = dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ? SORT_DESC : SORT_ASC), 1);
9130
9131 /*if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) // For backward compatiblity, we scan also old dirs
9132 {
9133 $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
9134 $filearray=array_merge($filearray, $filearrayold);
9135 }*/
9136
9137 completeFileArrayWithDatabaseInfo($filearray, $relativedir);
9138
9139 if (count($filearray)) {
9140 if ($sortfield && $sortorder) {
9141 $filearray = dol_sort_array($filearray, $sortfield, $sortorder);
9142 }
9143
9144 foreach ($filearray as $key => $val) {
9145 $photo = '';
9146 $file = $val['name'];
9147
9148 //if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
9149 if (image_format_supported($file) >= 0) {
9150 $nbphoto++;
9151 $photo = $file;
9152 $viewfilename = $file;
9153
9154 if ($size == 1 || $size == 'small') { // Format vignette
9155 // Find name of thumb file
9156 $photo_vignette = basename(getImageFileNameForSize($dir.$file, '_small'));
9157 if (!dol_is_file($dirthumb.$photo_vignette)) {
9158 // The thumb does not exists, so we will use the original file
9159 $dirthumb = $dir;
9160 $pdirthumb = $pdir;
9161 $photo_vignette = basename($file);
9162 }
9163
9164 // Get filesize of original file
9165 $imgarray = dol_getImageSize($dir.$photo);
9166
9167 if ($nbbyrow > 0) {
9168 if ($nbphoto == 1) {
9169 $return .= '<table class="valigntop center centpercent" style="border: 0; padding: 2px; border-spacing: 2px; border-collapse: separate;">';
9170 }
9171
9172 if ($nbphoto % $nbbyrow == 1) {
9173 $return .= '<tr class="center valignmiddle" style="border: 1px">';
9174 }
9175 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%" class="photo">'."\n";
9176 } elseif ($nbbyrow < 0) {
9177 $return .= '<div class="inline-block">'."\n";
9178 }
9179
9180 $relativefile = preg_replace('/^\//', '', $pdir.$photo);
9181 if (empty($nolink)) {
9182 $urladvanced = getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
9183 if ($urladvanced) {
9184 $return .= '<a href="'.$urladvanced.'">';
9185 } else {
9186 $return .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank" rel="noopener noreferrer">';
9187 }
9188 }
9189
9190 // Show image (width height=$maxHeight)
9191 // Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
9192 $alt = $langs->transnoentitiesnoconv('File').': '.$relativefile;
9193 $alt .= ' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
9194 if ($overwritetitle) {
9195 if (is_numeric($overwritetitle)) {
9196 $alt = '';
9197 } else {
9198 $alt = $overwritetitle;
9199 }
9200 }
9201
9202 if ($usesharelink) {
9203 if ($val['share']) {
9204 if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
9205 $return .= '<!-- Show original file (thumb not yet available with shared links) -->';
9206 $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).'">';
9207 } else {
9208 $return .= '<!-- Show original file -->';
9209 $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).'">';
9210 }
9211 } else {
9212 $return .= '<!-- Show nophoto file (because file is not shared) -->';
9213 $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
9214 }
9215 } else {
9216 if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
9217 $return .= '<!-- Show thumb -->';
9218 $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).'">';
9219 } else {
9220 $return .= '<!-- Show original file -->';
9221 $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).'">';
9222 }
9223 }
9224
9225 if (empty($nolink)) {
9226 $return .= '</a>';
9227 }
9228
9229 if ($showfilename) {
9230 $return .= '<br>'.$viewfilename;
9231 }
9232 if ($showaction) {
9233 $return .= '<br>';
9234 // If $photo_vignette set, we add link to generate thumbs if file is an image and ->imgWidth or->imgHeight higher than limits
9235 if ($photo_vignette && (image_format_supported($photo) > 0) && ((isset($this->imgWidth) && $this->imgWidth > $maxWidth) || (isset($this->imgHeight) && $this->imgHeight > $maxHeight))) {
9236 $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>';
9237 }
9238 // Special cas for product
9239 if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
9240 // Link to resize
9241 $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; ';
9242
9243 // Link to delete
9244 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
9245 $return .= img_delete().'</a>';
9246 }
9247 }
9248 $return .= "\n";
9249
9250 if ($nbbyrow > 0) {
9251 $return .= '</td>';
9252 if (($nbphoto % $nbbyrow) == 0) {
9253 $return .= '</tr>';
9254 }
9255 } elseif ($nbbyrow < 0) {
9256 $return .= '</div>'."\n";
9257 }
9258 }
9259
9260 if (empty($size)) { // Format origine
9261 $return .= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
9262
9263 if ($showfilename) {
9264 $return .= '<br>'.$viewfilename;
9265 }
9266 if ($showaction) {
9267 // Special case for product
9268 if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
9269 // Link to resize
9270 $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; ';
9271
9272 // Link to delete
9273 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
9274 $return .= img_delete().'</a>';
9275 }
9276 }
9277 }
9278
9279 // On continue ou on arrete de boucler ?
9280 if ($nbmax && $nbphoto >= $nbmax) {
9281 break;
9282 }
9283 }
9284 }
9285
9286 if ($size == 1 || $size == 'small') {
9287 if ($nbbyrow > 0) {
9288 // Ferme tableau
9289 while ($nbphoto % $nbbyrow) {
9290 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%">&nbsp;</td>';
9291 $nbphoto++;
9292 }
9293
9294 if ($nbphoto) {
9295 $return .= '</table>';
9296 }
9297 }
9298 }
9299 }
9300
9301 $this->nbphoto = $nbphoto;
9302
9303 return $return;
9304 }
9305
9306
9313 protected function isArray($info)
9314 {
9315 if (is_array($info)) {
9316 if (isset($info['type']) && $info['type'] == 'array') {
9317 return true;
9318 } else {
9319 return false;
9320 }
9321 }
9322 return false;
9323 }
9324
9331 public function isDate($info)
9332 {
9333 if (isset($info['type']) && ($info['type'] == 'date' || $info['type'] == 'datetime' || $info['type'] == 'timestamp')) {
9334 return true;
9335 }
9336 return false;
9337 }
9338
9345 public function isDuration($info)
9346 {
9347 if (is_array($info)) {
9348 if (isset($info['type']) && ($info['type'] == 'duration')) {
9349 return true;
9350 } else {
9351 return false;
9352 }
9353 } else {
9354 return false;
9355 }
9356 }
9357
9364 public function isInt($info)
9365 {
9366 if (is_array($info)) {
9367 if (isset($info['type']) && (preg_match('/(^int|int$)/i', $info['type']))) {
9368 return true;
9369 } else {
9370 return false;
9371 }
9372 } else {
9373 return false;
9374 }
9375 }
9376
9383 public function isFloat($info)
9384 {
9385 if (is_array($info)) {
9386 if (isset($info['type']) && (preg_match('/^(double|real|price)/i', $info['type']))) {
9387 return true;
9388 } else {
9389 return false;
9390 }
9391 }
9392 return false;
9393 }
9394
9401 public function isText($info)
9402 {
9403 if (is_array($info)) {
9404 if (isset($info['type']) && $info['type'] == 'text') {
9405 return true;
9406 } else {
9407 return false;
9408 }
9409 }
9410 return false;
9411 }
9412
9419 protected function canBeNull($info)
9420 {
9421 if (is_array($info)) {
9422 if (isset($info['notnull']) && $info['notnull'] != '1') {
9423 return true;
9424 } else {
9425 return false;
9426 }
9427 }
9428 return true;
9429 }
9430
9437 protected function isForcedToNullIfZero($info)
9438 {
9439 if (is_array($info)) {
9440 if (isset($info['notnull']) && $info['notnull'] == '-1') {
9441 return true;
9442 } else {
9443 return false;
9444 }
9445 }
9446 return false;
9447 }
9448
9455 protected function isIndex($info)
9456 {
9457 if (is_array($info)) {
9458 if (isset($info['index']) && $info['index'] == true) {
9459 return true;
9460 } else {
9461 return false;
9462 }
9463 }
9464 return false;
9465 }
9466
9467
9476 protected function setSaveQuery()
9477 {
9478 global $conf;
9479
9480 $queryarray = array();
9481 foreach ($this->fields as $field => $info) { // Loop on definition of fields
9482 // Depending on field type ('datetime', ...)
9483 if ($this->isDate($info)) {
9484 if (empty($this->{$field})) {
9485 $queryarray[$field] = null;
9486 } else {
9487 $queryarray[$field] = $this->db->idate($this->{$field});
9488 }
9489 } elseif ($this->isDuration($info)) {
9490 // $this->{$field} may be null, '', 0, '0', 123, '123'
9491 if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
9492 if (!isset($this->{$field})) {
9493 if (!empty($info['default'])) {
9494 $queryarray[$field] = $info['default'];
9495 } else {
9496 $queryarray[$field] = 0;
9497 }
9498 } else {
9499 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9500 }
9501 } else {
9502 $queryarray[$field] = null;
9503 }
9504 } elseif ($this->isInt($info) || $this->isFloat($info)) {
9505 if ($field == 'entity' && is_null($this->{$field})) {
9506 $queryarray[$field] = ((int) $conf->entity);
9507 } else {
9508 // $this->{$field} may be null, '', 0, '0', 123, '123'
9509 if ((isset($this->{$field}) && ((string) $this->{$field}) != '') || !empty($info['notnull'])) {
9510 if (!isset($this->{$field})) {
9511 $queryarray[$field] = 0;
9512 } elseif ($this->isInt($info)) {
9513 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9514 } elseif ($this->isFloat($info)) {
9515 $queryarray[$field] = (float) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9516 }
9517 } else {
9518 $queryarray[$field] = null;
9519 }
9520 }
9521 } else {
9522 // Note: If $this->{$field} is not defined, it means there is a bug into definition of ->fields or a missing declaration of property
9523 // We should keep the warning generated by this because it is a bug somewhere else in code, not here.
9524 $queryarray[$field] = $this->{$field};
9525 }
9526
9527 if ($info['type'] == 'timestamp' && empty($queryarray[$field])) {
9528 unset($queryarray[$field]);
9529 }
9530 if (!empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) {
9531 $queryarray[$field] = null; // May force 0 to null
9532 }
9533 }
9534
9535 return $queryarray;
9536 }
9537
9544 public function setVarsFromFetchObj(&$obj)
9545 {
9546 global $db;
9547
9548 foreach ($this->fields as $field => $info) {
9549 if ($this->isDate($info)) {
9550 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') {
9551 $this->$field = '';
9552 } else {
9553 $this->$field = $db->jdate($obj->$field);
9554 }
9555 } elseif ($this->isInt($info)) {
9556 if ($field == 'rowid') {
9557 $this->id = (int) $obj->$field;
9558 } else {
9559 if ($this->isForcedToNullIfZero($info)) {
9560 if (empty($obj->$field)) {
9561 $this->$field = null;
9562 } else {
9563 $this->$field = (float) $obj->$field;
9564 }
9565 } else {
9566 if (isset($obj->$field) && (!is_null($obj->$field) || (isset($info['notnull']) && $info['notnull'] == 1))) {
9567 $this->$field = (int) $obj->$field;
9568 } else {
9569 $this->$field = null;
9570 }
9571 }
9572 }
9573 } elseif ($this->isFloat($info)) {
9574 if ($this->isForcedToNullIfZero($info)) {
9575 if (empty($obj->$field)) {
9576 $this->$field = null;
9577 } else {
9578 $this->$field = (float) $obj->$field;
9579 }
9580 } else {
9581 if (isset($obj->$field) && (!is_null($obj->$field) || (isset($info['notnull']) && $info['notnull'] == 1))) {
9582 $this->$field = (float) $obj->$field;
9583 } else {
9584 $this->$field = null;
9585 }
9586 }
9587 } else {
9588 $this->$field = isset($obj->$field) ? $obj->$field : null;
9589 }
9590 }
9591
9592 // If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
9593 if (!isset($this->fields['ref']) && isset($this->id)) {
9594 $this->ref = $this->id;
9595 }
9596 }
9597
9602 public function emtpyObjectVars()
9603 {
9604 foreach ($this->fields as $field => $arr) {
9605 $this->$field = null;
9606 }
9607 }
9608
9616 public function getFieldList($alias = '', $excludefields = array())
9617 {
9618 $keys = array_keys($this->fields);
9619 if (!empty($alias)) {
9620 $keys_with_alias = array();
9621 foreach ($keys as $fieldname) {
9622 if (!empty($excludefields)) {
9623 if (in_array($fieldname, $excludefields)) { // The field is excluded and must not be in output
9624 continue;
9625 }
9626 }
9627 $keys_with_alias[] = $alias . '.' . $fieldname;
9628 }
9629 return implode(',', $keys_with_alias);
9630 } else {
9631 return implode(',', $keys);
9632 }
9633 }
9634
9642 protected function quote($value, $fieldsentry)
9643 {
9644 if (is_null($value)) {
9645 return 'NULL';
9646 } elseif (preg_match('/^(int|double|real|price)/i', $fieldsentry['type'])) {
9647 return price2num("$value");
9648 } elseif (preg_match('/int$/i', $fieldsentry['type'])) {
9649 return (int) $value;
9650 } elseif ($fieldsentry['type'] == 'boolean') {
9651 if ($value) {
9652 return 'true';
9653 } else {
9654 return 'false';
9655 }
9656 } else {
9657 return "'".$this->db->escape($value)."'";
9658 }
9659 }
9660
9661
9669 public function createCommon(User $user, $notrigger = false)
9670 {
9671 global $langs;
9672
9673 dol_syslog(get_class($this)."::createCommon create", LOG_DEBUG);
9674
9675 $error = 0;
9676
9677 $now = dol_now();
9678
9679 $fieldvalues = $this->setSaveQuery();
9680
9681 if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) {
9682 $fieldvalues['date_creation'] = $this->db->idate($now);
9683 }
9684 if (array_key_exists('fk_user_creat', $fieldvalues) && !($fieldvalues['fk_user_creat'] > 0)) {
9685 $fieldvalues['fk_user_creat'] = $user->id;
9686 }
9687 if (array_key_exists('pass_crypted', $fieldvalues) && property_exists($this, 'pass')) {
9688 $fieldvalues['pass_crypted'] = dol_hash($this->pass);
9689 }
9690 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
9691 if (array_key_exists('ref', $fieldvalues)) {
9692 $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
9693 }
9694
9695 $keys = array();
9696 $values = array(); // Array to store string forged for SQL syntax
9697 foreach ($fieldvalues as $k => $v) {
9698 $keys[$k] = $k;
9699 $value = $this->fields[$k];
9700 $values[$k] = $this->quote($v, $value); // May return string 'NULL' if $value is null
9701 }
9702
9703 // Clean and check mandatory
9704 foreach ($keys as $key) {
9705 // If field is an implicit foreign key field (so type = 'integer:...')
9706 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
9707 $values[$key] = '';
9708 }
9709 if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
9710 $values[$key] = '';
9711 }
9712
9713 if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && is_null($this->fields[$key]['default'])) {
9714 $error++;
9715 $langs->load("errors");
9716 dol_syslog("Mandatory field '".$key."' is empty and required into ->fields definition of class");
9717 $this->errors[] = $langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
9718 }
9719
9720 // If value is null and there is a default value for field
9721 if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && !is_null($this->fields[$key]['default'])) {
9722 $values[$key] = $this->quote($this->fields[$key]['default'], $this->fields[$key]);
9723 }
9724
9725 // If field is an implicit foreign key field (so type = 'integer:...')
9726 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) {
9727 if (isset($this->fields[$key]['default'])) {
9728 $values[$key] = ((int) $this->fields[$key]['default']);
9729 } else {
9730 $values[$key] = 'null';
9731 }
9732 }
9733 if (!empty($this->fields[$key]['foreignkey']) && empty($values[$key])) {
9734 $values[$key] = 'null';
9735 }
9736 }
9737
9738 if ($error) {
9739 return -1;
9740 }
9741
9742 $this->db->begin();
9743
9744 if (!$error) {
9745 $sql = "INSERT INTO ".$this->db->prefix().$this->table_element;
9746 $sql .= " (".implode(", ", $keys).')';
9747 $sql .= " VALUES (".implode(", ", $values).")"; // $values can contains 'abc' or 123
9748
9749 $res = $this->db->query($sql);
9750 if (!$res) {
9751 $error++;
9752 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
9753 $this->errors[] = "ErrorRefAlreadyExists";
9754 } else {
9755 $this->errors[] = $this->db->lasterror();
9756 }
9757 }
9758 }
9759
9760 if (!$error) {
9761 $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
9762 }
9763
9764 // If we have a field ref with a default value of (PROV)
9765 if (!$error) {
9766 if (key_exists('ref', $this->fields) && key_exists('notnull', $this->fields['ref']) && $this->fields['ref']['notnull'] > 0 && key_exists('default', $this->fields['ref']) && $this->fields['ref']['default'] == '(PROV)') {
9767 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ref = '(PROV".((int) $this->id).")' WHERE (ref = '(PROV)' OR ref = '') AND rowid = ".((int) $this->id);
9768 $resqlupdate = $this->db->query($sql);
9769
9770 if ($resqlupdate === false) {
9771 $error++;
9772 $this->errors[] = $this->db->lasterror();
9773 } else {
9774 $this->ref = '(PROV'.$this->id.')';
9775 }
9776 }
9777 }
9778
9779 // Create extrafields
9780 if (!$error) {
9781 $result = $this->insertExtraFields();
9782 if ($result < 0) {
9783 $error++;
9784 }
9785 }
9786
9787 // Create lines
9788 if (!empty($this->table_element_line) && !empty($this->fk_element)) {
9789 foreach ($this->lines as $line) {
9790 $keyforparent = $this->fk_element;
9791 $line->$keyforparent = $this->id;
9792
9793 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
9794 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
9795 if (!is_object($line)) {
9796 $line = (object) $line;
9797 }
9798
9799 $result = 0;
9800 if (method_exists($line, 'insert')) {
9801 $result = $line->insert($user, 1);
9802 } elseif (method_exists($line, 'create')) {
9803 $result = $line->create($user, 1);
9804 }
9805 if ($result < 0) {
9806 $this->error = $line->error;
9807 $this->db->rollback();
9808 return -1;
9809 }
9810 }
9811 }
9812
9813 // Triggers
9814 if (!$error && !$notrigger) {
9815 // Call triggers
9816 $result = $this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user);
9817 if ($result < 0) {
9818 $error++;
9819 }
9820 // End call triggers
9821 }
9822
9823 // Commit or rollback
9824 if ($error) {
9825 $this->db->rollback();
9826 return -1;
9827 } else {
9828 $this->db->commit();
9829 return $this->id;
9830 }
9831 }
9832
9833
9843 public function fetchCommon($id, $ref = null, $morewhere = '', $noextrafields = 0)
9844 {
9845 if (empty($id) && empty($ref) && empty($morewhere)) {
9846 return -1;
9847 }
9848
9849 $fieldlist = $this->getFieldList('t');
9850 if (empty($fieldlist)) {
9851 return 0;
9852 }
9853
9854 $sql = "SELECT ".$fieldlist;
9855 $sql .= " FROM ".$this->db->prefix().$this->table_element.' as t';
9856
9857 if (!empty($id)) {
9858 $sql .= ' WHERE t.rowid = '.((int) $id);
9859 } elseif (!empty($ref)) {
9860 $sql .= " WHERE t.ref = '".$this->db->escape($ref)."'";
9861 } else {
9862 $sql .= ' WHERE 1 = 1'; // usage with empty id and empty ref is very rare
9863 }
9864 if (empty($id) && isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
9865 $sql .= ' AND t.entity IN ('.getEntity($this->element).')';
9866 }
9867 if ($morewhere) {
9868 $sql .= $morewhere;
9869 }
9870 $sql .= ' LIMIT 1'; // This is a fetch, to be sure to get only one record
9871
9872 $res = $this->db->query($sql);
9873 if ($res) {
9874 $obj = $this->db->fetch_object($res);
9875 if ($obj) {
9876 $this->setVarsFromFetchObj($obj);
9877
9878 // Retrieve all extrafield
9879 // fetch optionals attributes and labels
9880 if (empty($noextrafields)) {
9881 $result = $this->fetch_optionals();
9882 if ($result < 0) {
9883 $this->error = $this->db->lasterror();
9884 $this->errors[] = $this->error;
9885 return -4;
9886 }
9887 }
9888
9889 return $this->id;
9890 } else {
9891 return 0;
9892 }
9893 } else {
9894 $this->error = $this->db->lasterror();
9895 $this->errors[] = $this->error;
9896 return -1;
9897 }
9898 }
9899
9907 public function fetchLinesCommon($morewhere = '', $noextrafields = 0)
9908 {
9909 $objectlineclassname = get_class($this).'Line';
9910 if (!class_exists($objectlineclassname)) {
9911 $this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
9912 return -1;
9913 }
9914
9915 $objectline = new $objectlineclassname($this->db);
9916
9917 $sql = "SELECT ".$objectline->getFieldList('l');
9918 $sql .= " FROM ".$this->db->prefix().$objectline->table_element." as l";
9919 $sql .= " WHERE l.fk_".$this->db->escape($this->element)." = ".((int) $this->id);
9920 if ($morewhere) {
9921 $sql .= $morewhere;
9922 }
9923 if (isset($objectline->fields['position'])) {
9924 $sql .= $this->db->order('position', 'ASC');
9925 }
9926
9927 $resql = $this->db->query($sql);
9928 if ($resql) {
9929 $num_rows = $this->db->num_rows($resql);
9930 $i = 0;
9931 $this->lines = array();
9932 while ($i < $num_rows) {
9933 $obj = $this->db->fetch_object($resql);
9934 if ($obj) {
9935 $newline = new $objectlineclassname($this->db);
9936 $newline->setVarsFromFetchObj($obj);
9937
9938 // Note: extrafields load of line not yet supported
9939 /*
9940 if (empty($noextrafields)) {
9941 // Load extrafields of line
9942 }*/
9943
9944 $this->lines[] = $newline;
9945 }
9946 $i++;
9947 }
9948
9949 return 1;
9950 } else {
9951 $this->error = $this->db->lasterror();
9952 $this->errors[] = $this->error;
9953 return -1;
9954 }
9955 }
9956
9964 public function updateCommon(User $user, $notrigger = false)
9965 {
9966 dol_syslog(get_class($this)."::updateCommon update", LOG_DEBUG);
9967
9968 $error = 0;
9969
9970 $now = dol_now();
9971
9972 // $this->oldcopy should have been set by the caller of update
9973 //if (empty($this->oldcopy)) {
9974 // $this->oldcopy = dol_clone($this);
9975 //}
9976
9977 $fieldvalues = $this->setSaveQuery();
9978
9979 if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) {
9980 $fieldvalues['date_modification'] = $this->db->idate($now);
9981 }
9982 if (array_key_exists('fk_user_modif', $fieldvalues) && !($fieldvalues['fk_user_modif'] > 0)) {
9983 $fieldvalues['fk_user_modif'] = $user->id;
9984 }
9985 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
9986 if (array_key_exists('ref', $fieldvalues)) {
9987 $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
9988 }
9989
9990 // Add quotes and escape on fields with type string
9991 $keys = array();
9992 $values = array();
9993 $tmp = array();
9994 foreach ($fieldvalues as $k => $v) {
9995 $keys[$k] = $k;
9996 $value = $this->fields[$k];
9997 $values[$k] = $this->quote($v, $value);
9998 $tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
9999 }
10000
10001 // Clean and check mandatory fields
10002 foreach ($keys as $key) {
10003 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
10004 $values[$key] = ''; // This is an implicit foreign key field
10005 }
10006 if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
10007 $values[$key] = ''; // This is an explicit foreign key field
10008 }
10009
10010 //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
10011 /*
10012 if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
10013 {
10014 $error++;
10015 $this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
10016 }*/
10017 }
10018
10019 $sql = 'UPDATE '.$this->db->prefix().$this->table_element.' SET '.implode(', ', $tmp).' WHERE rowid='.((int) $this->id);
10020
10021 $this->db->begin();
10022
10023 if (!$error) {
10024 $res = $this->db->query($sql);
10025 if (!$res) {
10026 $error++;
10027 $this->errors[] = $this->db->lasterror();
10028 }
10029 }
10030
10031 // Update extrafield
10032 if (!$error) {
10033 $result = $this->insertExtraFields(); // This delete and reinsert extrafields
10034 if ($result < 0) {
10035 $error++;
10036 }
10037 }
10038
10039 // Triggers
10040 if (!$error && !$notrigger) {
10041 // Call triggers
10042 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
10043 if ($result < 0) {
10044 $error++;
10045 } //Do also here what you must do to rollback action if trigger fail
10046 // End call triggers
10047 }
10048
10049 // Commit or rollback
10050 if ($error) {
10051 $this->db->rollback();
10052 return -1;
10053 } else {
10054 $this->db->commit();
10055 return $this->id;
10056 }
10057 }
10058
10067 public function deleteCommon(User $user, $notrigger = false, $forcechilddeletion = 0)
10068 {
10069 dol_syslog(get_class($this)."::deleteCommon delete", LOG_DEBUG);
10070
10071 $error = 0;
10072
10073 $this->db->begin();
10074
10075 if ($forcechilddeletion) { // Force also delete of childtables that should lock deletion in standard case when option force is off
10076 foreach ($this->childtables as $table) {
10077 $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
10078 $resql = $this->db->query($sql);
10079 if (!$resql) {
10080 $this->error = $this->db->lasterror();
10081 $this->errors[] = $this->error;
10082 $this->db->rollback();
10083 return -1;
10084 }
10085 }
10086 } elseif (!empty($this->childtables)) { // If object has childs linked with a foreign key field, we check all child tables.
10087 $objectisused = $this->isObjectUsed($this->id);
10088 if (!empty($objectisused)) {
10089 dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
10090 $this->error = 'ErrorRecordHasChildren';
10091 $this->errors[] = $this->error;
10092 $this->db->rollback();
10093 return 0;
10094 }
10095 }
10096
10097 // Delete cascade first
10098 if (is_array($this->childtablesoncascade) && !empty($this->childtablesoncascade)) {
10099 foreach ($this->childtablesoncascade as $table) {
10100 $deleteFromObject = explode(':', $table);
10101 if (count($deleteFromObject) >= 2) {
10102 $className = str_replace('@', '', $deleteFromObject[0]);
10103 $filePath = $deleteFromObject[1];
10104 $columnName = $deleteFromObject[2];
10105 $TMoreSQL = array();
10106 if (!empty($deleteFromObject[3])) {
10107 $TMoreSQL['customsql'] = $deleteFromObject[3];
10108 }
10109 if (dol_include_once($filePath)) {
10110 $childObject = new $className($this->db);
10111 if (method_exists($childObject, 'deleteByParentField')) {
10112 $result = $childObject->deleteByParentField($this->id, $columnName, $TMoreSQL);
10113 if ($result < 0) {
10114 $error++;
10115 $this->errors[] = $childObject->error;
10116 break;
10117 }
10118 } else {
10119 $error++;
10120 $this->errors[] = "You defined a cascade delete on an object $childObject but there is no method deleteByParentField for it";
10121 break;
10122 }
10123 } else {
10124 $error++;
10125 $this->errors[] = 'Cannot include child class file '.$filePath;
10126 break;
10127 }
10128 } else {
10129 // Delete record in child table
10130 $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
10131
10132 $resql = $this->db->query($sql);
10133 if (!$resql) {
10134 $error++;
10135 $this->error = $this->db->lasterror();
10136 $this->errors[] = $this->error;
10137 break;
10138 }
10139 }
10140 }
10141 }
10142
10143 if (!$error) {
10144 if (!$notrigger) {
10145 // Call triggers
10146 $result = $this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
10147 if ($result < 0) {
10148 $error++;
10149 } // Do also here what you must do to rollback action if trigger fail
10150 // End call triggers
10151 }
10152 }
10153
10154 // Delete llx_ecm_files
10155 if (!$error) {
10156 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
10157 if (!$res) {
10158 $error++;
10159 }
10160 }
10161
10162 // Delete linked object
10163 $res = $this->deleteObjectLinked();
10164 if ($res < 0) {
10165 $error++;
10166 }
10167
10168 if (!$error && !empty($this->isextrafieldmanaged)) {
10169 $result = $this->deleteExtraFields();
10170 if ($result < 0) {
10171 $error++;
10172 }
10173 }
10174
10175 if (!$error) {
10176 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.' WHERE rowid='.((int) $this->id);
10177
10178 $resql = $this->db->query($sql);
10179 if (!$resql) {
10180 $error++;
10181 $this->errors[] = $this->db->lasterror();
10182 }
10183 }
10184
10185 // Commit or rollback
10186 if ($error) {
10187 $this->db->rollback();
10188 return -1;
10189 } else {
10190 $this->db->commit();
10191 return 1;
10192 }
10193 }
10194
10205 public function deleteByParentField($parentId = 0, $parentField = '', $filter = array(), $filtermode = "AND")
10206 {
10207 global $user;
10208
10209 $error = 0;
10210 $deleted = 0;
10211
10212 if (!empty($parentId) && !empty($parentField)) {
10213 $this->db->begin();
10214
10215 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
10216 $sql .= " WHERE ".$parentField." = ".(int) $parentId;
10217
10218 // Manage filters
10219 $sqlwhere = array();
10220 if (count($filter) > 0) {
10221 foreach ($filter as $key => $value) {
10222 if ($key == 'customsql') {
10223 $sqlwhere[] = $value;
10224 } elseif (strpos($value, '%') === false) {
10225 $sqlwhere[] = $key." IN (".$this->db->sanitize($this->db->escape($value)).")";
10226 } else {
10227 $sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'";
10228 }
10229 }
10230 }
10231 if (count($sqlwhere) > 0) {
10232 $sql .= " AND (".implode(" ".$filtermode." ", $sqlwhere).")";
10233 }
10234
10235 $resql = $this->db->query($sql);
10236 if (!$resql) {
10237 $this->errors[] = $this->db->lasterror();
10238 $error++;
10239 } else {
10240 while ($obj = $this->db->fetch_object($resql)) {
10241 $result = $this->fetch($obj->rowid); // @phpstan-ignore-line
10242 if ($result < 0) {
10243 $error++;
10244 $this->errors[] = $this->error;
10245 } else {
10246 $result = $this->delete($user); // @phpstan-ignore-line
10247 if ($result < 0) {
10248 $error++;
10249 $this->errors[] = $this->error;
10250 } else {
10251 $deleted++;
10252 }
10253 }
10254 }
10255 }
10256
10257 if (empty($error)) {
10258 $this->db->commit();
10259 return $deleted;
10260 } else {
10261 $this->error = implode(', ', $this->errors);
10262 $this->db->rollback();
10263 return $error * -1;
10264 }
10265 }
10266
10267 return $deleted;
10268 }
10269
10278 public function deleteLineCommon(User $user, $idline, $notrigger = false)
10279 {
10280 global $conf;
10281
10282 $error = 0;
10283
10284 $tmpforobjectclass = get_class($this);
10285 $tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line';
10286
10287 $this->db->begin();
10288
10289 // Call trigger
10290 $result = $this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user);
10291 if ($result < 0) {
10292 $error++;
10293 }
10294 // End call triggers
10295
10296 if (empty($error)) {
10297 $sql = "DELETE FROM ".$this->db->prefix().$this->table_element_line;
10298 $sql .= " WHERE rowid = ".((int) $idline);
10299
10300 $resql = $this->db->query($sql);
10301 if (!$resql) {
10302 $this->error = "Error ".$this->db->lasterror();
10303 $error++;
10304 }
10305 }
10306
10307 if (empty($error)) {
10308 // Remove extrafields
10309 $tmpobjectline = new $tmpforobjectlineclass($this->db);
10310 if (!isset($tmpobjectline->isextrafieldmanaged) || !empty($tmpobjectline->isextrafieldmanaged)) {
10311 $tmpobjectline->id = $idline;
10312 $result = $tmpobjectline->deleteExtraFields();
10313 if ($result < 0) {
10314 $error++;
10315 $this->error = "Error ".get_class($this)."::deleteLineCommon deleteExtraFields error -4 ".$tmpobjectline->error;
10316 }
10317 }
10318 }
10319
10320 if (empty($error)) {
10321 $this->db->commit();
10322 return 1;
10323 } else {
10324 dol_syslog(get_class($this)."::deleteLineCommon ERROR:".$this->error, LOG_ERR);
10325 $this->db->rollback();
10326 return -1;
10327 }
10328 }
10329
10330
10340 public function setStatusCommon($user, $status, $notrigger = 0, $triggercode = '')
10341 {
10342 $error = 0;
10343
10344 $this->db->begin();
10345
10346 $statusfield = 'status';
10347 if (in_array($this->element, array('don', 'donation', 'shipping'))) {
10348 $statusfield = 'fk_statut';
10349 }
10350
10351 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
10352 $sql .= " SET ".$statusfield." = ".((int) $status);
10353 $sql .= " WHERE rowid = ".((int) $this->id);
10354
10355 if ($this->db->query($sql)) {
10356 if (!$error) {
10357 $this->oldcopy = clone $this;
10358 }
10359
10360 if (!$error && !$notrigger) {
10361 // Call trigger
10362 $result = $this->call_trigger($triggercode, $user);
10363 if ($result < 0) {
10364 $error++;
10365 }
10366 }
10367
10368 if (!$error) {
10369 $this->status = $status;
10370 $this->db->commit();
10371 return 1;
10372 } else {
10373 $this->db->rollback();
10374 return -1;
10375 }
10376 } else {
10377 $this->error = $this->db->error();
10378 $this->db->rollback();
10379 return -1;
10380 }
10381 }
10382
10383
10390 public function initAsSpecimenCommon()
10391 {
10392 global $user;
10393
10394 $this->id = 0;
10395 $this->specimen = 1;
10396 $fields = array(
10397 'label' => 'This is label',
10398 'ref' => 'ABCD1234',
10399 'description' => 'This is a description',
10400 'qty' => 123.12,
10401 'note_public' => 'Public note',
10402 'note_private' => 'Private note',
10403 'date_creation' => (dol_now() - 3600 * 48),
10404 'date_modification' => (dol_now() - 3600 * 24),
10405 'fk_user_creat' => $user->id,
10406 'fk_user_modif' => $user->id,
10407 'date' => dol_now(),
10408 );
10409 foreach ($fields as $key => $value) {
10410 if (array_key_exists($key, $this->fields)) {
10411 $this->{$key} = $value; // @phpstan-ignore-line
10412 }
10413 }
10414
10415 // Force values to default values when known
10416 if (property_exists($this, 'fields')) {
10417 foreach ($this->fields as $key => $value) {
10418 // If fields are already set, do nothing
10419 if (array_key_exists($key, $fields)) {
10420 continue;
10421 }
10422
10423 if (!empty($value['default'])) {
10424 $this->$key = $value['default'];
10425 }
10426 }
10427 }
10428
10429 return 1;
10430 }
10431
10432
10433 /* Part for comments */
10434
10439 public function fetchComments()
10440 {
10441 require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
10442
10443 $comment = new Comment($this->db);
10444 $result = $comment->fetchAllFor($this->element, $this->id);
10445 if ($result < 0) {
10446 $this->errors = array_merge($this->errors, $comment->errors);
10447 return -1;
10448 } else {
10449 $this->comments = $comment->comments;
10450 }
10451 return count($this->comments);
10452 }
10453
10459 public function getNbComments()
10460 {
10461 return count($this->comments);
10462 }
10463
10470 public function trimParameters($parameters)
10471 {
10472 if (!is_array($parameters)) {
10473 return;
10474 }
10475 foreach ($parameters as $parameter) {
10476 if (isset($this->$parameter)) {
10477 $this->$parameter = trim($this->$parameter);
10478 }
10479 }
10480 }
10481
10482 /* Part for categories/tags */
10483
10494 public function getCategoriesCommon($type_categ)
10495 {
10496 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10497
10498 // Get current categories
10499 $c = new Categorie($this->db);
10500 $existing = $c->containing($this->id, $type_categ, 'id');
10501
10502 return $existing;
10503 }
10504
10517 public function setCategoriesCommon($categories, $type_categ = '', $remove_existing = true)
10518 {
10519 // Handle single category
10520 if (!is_array($categories)) {
10521 $categories = array($categories);
10522 }
10523
10524 dol_syslog(get_class($this)."::setCategoriesCommon Oject Id:".$this->id.' type_categ:'.$type_categ.' nb tag add:'.count($categories), LOG_DEBUG);
10525
10526 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10527
10528 if (empty($type_categ)) {
10529 dol_syslog(__METHOD__.': Type '.$type_categ.'is an unknown category type. Done nothing.', LOG_ERR);
10530 return -1;
10531 }
10532
10533 // Get current categories
10534 $c = new Categorie($this->db);
10535 $existing = $c->containing($this->id, $type_categ, 'id');
10536 if ($remove_existing) {
10537 // Diff
10538 if (is_array($existing)) {
10539 $to_del = array_diff($existing, $categories);
10540 $to_add = array_diff($categories, $existing);
10541 } else {
10542 $to_del = array(); // Nothing to delete
10543 $to_add = $categories;
10544 }
10545 } else {
10546 $to_del = array(); // Nothing to delete
10547 $to_add = array_diff($categories, $existing);
10548 }
10549
10550 $error = 0;
10551 $ok = 0;
10552
10553 // Process
10554 foreach ($to_del as $del) {
10555 if ($c->fetch($del) > 0) {
10556 $result=$c->del_type($this, $type_categ);
10557 if ($result < 0) {
10558 $error++;
10559 $this->error = $c->error;
10560 $this->errors = $c->errors;
10561 break;
10562 } else {
10563 $ok += $result;
10564 }
10565 }
10566 }
10567 foreach ($to_add as $add) {
10568 if ($c->fetch($add) > 0) {
10569 $result = $c->add_type($this, $type_categ);
10570 if ($result < 0) {
10571 $error++;
10572 $this->error = $c->error;
10573 $this->errors = $c->errors;
10574 break;
10575 } else {
10576 $ok += $result;
10577 }
10578 }
10579 }
10580
10581 return $error ? (-1 * $error) : $ok;
10582 }
10583
10592 public function cloneCategories($fromId, $toId, $type = '')
10593 {
10594 $this->db->begin();
10595
10596 if (empty($type)) {
10597 $type = $this->table_element;
10598 }
10599
10600 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10601 $categorystatic = new Categorie($this->db);
10602
10603 $sql = "INSERT INTO ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type])." (fk_categorie, fk_product)";
10604 $sql .= " SELECT fk_categorie, $toId FROM ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type]);
10605 $sql .= " WHERE fk_product = ".((int) $fromId);
10606
10607 if (!$this->db->query($sql)) {
10608 $this->error = $this->db->lasterror();
10609 $this->db->rollback();
10610 return -1;
10611 }
10612
10613 $this->db->commit();
10614 return 1;
10615 }
10616
10623 public function deleteEcmFiles($mode = 0)
10624 {
10625 global $conf;
10626
10627 $this->db->begin();
10628
10629 // Delete in database with mode 0
10630 if ($mode == 0) {
10631 switch ($this->element) {
10632 case 'propal':
10633 $element = 'propale';
10634 break;
10635 case 'product':
10636 $element = 'produit';
10637 break;
10638 case 'order_supplier':
10639 $element = 'fournisseur/commande';
10640 break;
10641 case 'invoice_supplier':
10642 // Special cases that need to use get_exdir to get real dir of object
10643 // In future, all object should use this to define path of documents.
10644 $element = 'fournisseur/facture/'.get_exdir($this->id, 2, 0, 1, $this, 'invoice_supplier');
10645 break;
10646 case 'shipping':
10647 $element = 'expedition/sending';
10648 break;
10649 case 'task':
10650 case 'project_task':
10651 require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
10652
10653 $project_result = $this->fetch_projet();
10654 if ($project_result >= 0) {
10655 $element = 'projet/'.dol_sanitizeFileName($this->project->ref).'/';
10656 }
10657 // no break
10658 default:
10659 $element = $this->element;
10660 }
10661
10662 // Delete ecm_files_extrafields with mode 0 (using name)
10663 $sql = "DELETE FROM ".$this->db->prefix()."ecm_files_extrafields WHERE fk_object IN (";
10664 $sql .= " SELECT rowid FROM ".$this->db->prefix()."ecm_files WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
10665 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
10666 $sql .= ")";
10667
10668 if (!$this->db->query($sql)) {
10669 $this->error = $this->db->lasterror();
10670 $this->db->rollback();
10671 return false;
10672 }
10673
10674 // Delete ecm_files with mode 0 (using name)
10675 $sql = "DELETE FROM ".$this->db->prefix()."ecm_files";
10676 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
10677 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
10678
10679 if (!$this->db->query($sql)) {
10680 $this->error = $this->db->lasterror();
10681 $this->db->rollback();
10682 return false;
10683 }
10684 }
10685
10686 // Delete in database with mode 1
10687 if ($mode == 1) {
10688 $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files_extrafields";
10689 $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).")";
10690 $resql = $this->db->query($sql);
10691 if (!$resql) {
10692 $this->error = $this->db->lasterror();
10693 $this->db->rollback();
10694 return false;
10695 }
10696
10697 $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files";
10698 $sql .= " WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? "" : "@".$this->module))."' AND src_object_id = ".((int) $this->id);
10699 $resql = $this->db->query($sql);
10700 if (!$resql) {
10701 $this->error = $this->db->lasterror();
10702 $this->db->rollback();
10703 return false;
10704 }
10705 }
10706
10707 $this->db->commit();
10708 return true;
10709 }
10710}
print $langs trans("AuditedSecurityEvents").'</strong >< span class="opacitymedium"></span >< br > status
Definition security.php:604
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:455
$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.
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
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.
deleteLineCommon(User $user, $idline, $notrigger=false)
Delete a line of object in database.
static deleteAllItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
Function used to remove all items linked to an object id in association table.
deleteByParentField($parentId=0, $parentField='', $filter=array(), $filtermode="AND")
Delete all child object from a parent ID.
setFieldError($fieldKey, $msg='')
set validation error message a field
validateField($fields, $fieldKey, $fieldValue)
Return validation test result for a field.
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.
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.
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.
setExtraParameters()
Set extra parameters.
setErrorsFromObject($object)
setErrorsFromObject
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 an object id/ref exists If you don't need/want to instantiate object and just need to know if o...
isObjectUsed($id=0, $entity=0)
Function to check if an object is used by others (by children).
createCommon(User $user, $notrigger=false)
Create object into database.
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.
getTotalWeightVolume()
Return into unit=0, the calculated total of weight and volume of all lines * qty Calculate by adding ...
deleteCommon(User $user, $notrigger=false, $forcechilddeletion=0)
Delete object in database.
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.
update_price($exclspec=0, $roundingadjust='none', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
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.
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.
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.
setBankAccount($fk_account, $notrigger=false, $userused=null)
Change the bank account.
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.
setShippingMethod($shipping_method_id, $notrigger=false, $userused=null)
Change the shipping method.
initAsSpecimenCommon()
Initialise object with example values Id must be 0 if object instance is a specimen.
printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected=0, $extrafields=null, $defaulttpldir='/core/tpl')
Return HTML content of a detail line TODO Move this into an output class file (htmlline....
copy_linked_contact($objFrom, $source='internal')
Copy contact from one element to current.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
getValueFrom($table, $id, $field)
Getter generic.
trimParameters($parameters)
Trim object parameters.
fetch_product()
Load the product with id $this->fk_product into this->product.
isIndex($info)
Function test if is indexed.
quote($value, $fieldsentry)
Add quote to field value if necessary.
formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir='/core/tpl')
Show add free and predefined products/services form.
fetchComments()
Load comments linked with current task.
line_down($rowid, $fk_parent_line=true)
Update a line to have a higher rank.
setValueFrom($field, $value, $table='', $id=null, $format='', $id_field='', $fuser=null, $trigkey='', $fk_user_field='fk_user_modif')
Setter generic.
fetch_contact($contactid=null)
Load object contact with id=$this->contact_id into $this->contact.
setRetainedWarrantyPaymentTerms($id)
Change the retained warranty payments terms.
delThumbs($file)
Delete thumbs.
show_photos($modulepart, $sdir, $size=0, $nbmax=0, $nbbyrow=5, $showfilename=0, $showaction=0, $maxHeight=120, $maxWidth=160, $nolink=0, $overwritetitle=0, $usesharelink=0, $cache='', $addphotorefcss='photoref')
Show photos of an object (nbmax maximum), into several columns.
updateCommon(User $user, $notrigger=false)
Update object into database.
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.
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.
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.
getFormatedCustomerRef($objref)
Return customer ref for screen output.
getTooltipContentArray($params)
Return array of datas 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 datas.
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 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=0, $outputlangs='', $entconv=1, $searchlabel='')
Return country label, code or id from an id, code or label.
getState($id, $withcode='', $dbtouse=0, $withregion=0, $outputlangs='', $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:243
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($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:62
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 informations (by default a local PHP server timestamp) Re...
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formated for view output Used into pdf and HTML pages.
dol_print_ip($ip, $mode=0)
Return an IP formated to be shown on screen.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
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 '.
get_date_range($date_start, $date_end, $format='', $outputlangs='', $withparenthesis=1)
Format output for start and end date.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
dol_format_address($object, $withcountry=0, $sep="\n", $outputlangs='', $mode=0, $extralangcode='')
Return a formated address (part address/zip/town/state) according to country rules.
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_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.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
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_print_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0)
Format phone numbers according to country.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by second index function, which produces ascending (default) or descending output...
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_eval($s, $returnvalue=0, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
getImageFileNameForSize($file, $extName, $extImgTarget='')
Return the filename of file to get the thumbs.
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.
getElementProperties($element_type)
Get an array with properties of an element.
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_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.
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall right right takeposterminal SELECT e rowid
Definition invoice.php:1926
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall right right takeposterminal SELECT e e e e e statut
Definition invoice.php:1926
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:86
$conf db user
Definition repair.php:125
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:121
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 symetric encryption.
dolDecrypt($chain, $key='')
Decode a string with a symetric encryption.
dol_hash($chain, $type='0', $nosalt=0)
Returns a hash (non reversible encryption) of a string.
Contact()
Old copy.
Definition index.php:572