dolibarr 19.0.4
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, tc.module";
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->module ?? $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 } elseif ($objecttype == 'action') {
4188 $module = 'agenda';
4189 $subelement = 'actionComm';
4190 }
4191
4192 // Set classfile
4193 $classfile = strtolower($subelement);
4194 $classname = ucfirst($subelement);
4195
4196 if ($objecttype == 'order') {
4197 $classfile = 'commande';
4198 $classname = 'Commande';
4199 } elseif ($objecttype == 'invoice_supplier') {
4200 $classfile = 'fournisseur.facture';
4201 $classname = 'FactureFournisseur';
4202 } elseif ($objecttype == 'order_supplier') {
4203 $classfile = 'fournisseur.commande';
4204 $classname = 'CommandeFournisseur';
4205 } elseif ($objecttype == 'supplier_proposal') {
4206 $classfile = 'supplier_proposal';
4207 $classname = 'SupplierProposal';
4208 } elseif ($objecttype == 'facturerec') {
4209 $classfile = 'facture-rec';
4210 $classname = 'FactureRec';
4211 } elseif ($objecttype == 'subscription') {
4212 $classfile = 'subscription';
4213 $classname = 'Subscription';
4214 } elseif ($objecttype == 'project' || $objecttype == 'projet') {
4215 $classpath = 'projet/class';
4216 $classfile = 'project';
4217 $classname = 'Project';
4218 } elseif ($objecttype == 'conferenceorboothattendee') {
4219 $classpath = 'eventorganization/class';
4220 $classfile = 'conferenceorboothattendee';
4221 $classname = 'ConferenceOrBoothAttendee';
4222 $module = 'eventorganization';
4223 } elseif ($objecttype == 'conferenceorbooth') {
4224 $classpath = 'eventorganization/class';
4225 $classfile = 'conferenceorbooth';
4226 $classname = 'ConferenceOrBooth';
4227 $module = 'eventorganization';
4228 } elseif ($objecttype == 'mo') {
4229 $classpath = 'mrp/class';
4230 $classfile = 'mo';
4231 $classname = 'Mo';
4232 $module = 'mrp';
4233 }
4234
4235 // Here $module, $classfile and $classname are set, we can use them.
4236 if (isModEnabled($module) && (($element != $this->element) || $alsosametype)) {
4237 if ($loadalsoobjects && (is_numeric($loadalsoobjects) || ($loadalsoobjects === $objecttype))) {
4238 dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
4239 //print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
4240 if (class_exists($classname)) {
4241 foreach ($objectids as $i => $objectid) { // $i is rowid into llx_element_element
4242 $object = new $classname($this->db);
4243 $ret = $object->fetch($objectid);
4244 if ($ret >= 0) {
4245 $this->linkedObjects[$objecttype][$i] = $object;
4246 }
4247 }
4248 }
4249 }
4250 } else {
4251 unset($this->linkedObjectsIds[$objecttype]);
4252 }
4253 }
4254 }
4255 return 1;
4256 } else {
4257 dol_print_error($this->db);
4258 return -1;
4259 }
4260 }
4261
4268 public function clearObjectLinkedCache()
4269 {
4270 if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
4271 unset($this->linkedObjectsFullLoaded[$this->id]);
4272 }
4273
4274 return 1;
4275 }
4276
4289 public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $f_user = null, $notrigger = 0)
4290 {
4291 global $user;
4292 $updatesource = false;
4293 $updatetarget = false;
4294 $f_user = isset($f_user) ? $f_user : $user;
4295
4296 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4297 $updatesource = true;
4298 } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4299 $updatetarget = true;
4300 }
4301
4302 $this->db->begin();
4303 $error = 0;
4304
4305 $sql = "UPDATE " . $this->db->prefix() . "element_element SET ";
4306 if ($updatesource) {
4307 $sql .= "fk_source = " . ((int) $sourceid);
4308 $sql .= ", sourcetype = '" . $this->db->escape($sourcetype) . "'";
4309 $sql .= " WHERE fk_target = " . ((int) $this->id);
4310 $sql .= " AND targettype = '" . $this->db->escape($this->element) . "'";
4311 } elseif ($updatetarget) {
4312 $sql .= "fk_target = " . ((int) $targetid);
4313 $sql .= ", targettype = '" . $this->db->escape($targettype) . "'";
4314 $sql .= " WHERE fk_source = " . ((int) $this->id);
4315 $sql .= " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4316 }
4317
4318 dol_syslog(get_class($this) . "::updateObjectLinked", LOG_DEBUG);
4319 if ($this->db->query($sql)) {
4320 if (!$notrigger) {
4321 // Call trigger
4322 $this->context['link_source_id'] = $sourceid;
4323 $this->context['link_source_type'] = $sourcetype;
4324 $this->context['link_target_id'] = $targetid;
4325 $this->context['link_target_type'] = $targettype;
4326 $result = $this->call_trigger('OBJECT_LINK_MODIFY', $f_user);
4327 if ($result < 0) {
4328 $error++;
4329 }
4330 // End call triggers
4331 }
4332 } else {
4333 $this->error = $this->db->lasterror();
4334 $error++;
4335 }
4336
4337 if (!$error) {
4338 $this->db->commit();
4339 return 1;
4340 } else {
4341 $this->db->rollback();
4342 return -1;
4343 }
4344 }
4345
4359 public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = 0, $f_user = null, $notrigger = 0)
4360 {
4361 global $user;
4362 $deletesource = false;
4363 $deletetarget = false;
4364 $f_user = isset($f_user) ? $f_user : $user;
4365
4366 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4367 $deletesource = true;
4368 } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4369 $deletetarget = true;
4370 }
4371
4372 $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
4373 $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
4374 $targetid = (!empty($targetid) ? $targetid : $this->id);
4375 $targettype = (!empty($targettype) ? $targettype : $this->element);
4376 $this->db->begin();
4377 $error = 0;
4378
4379 if (!$notrigger) {
4380 // Call trigger
4381 $this->context['link_id'] = $rowid;
4382 $this->context['link_source_id'] = $sourceid;
4383 $this->context['link_source_type'] = $sourcetype;
4384 $this->context['link_target_id'] = $targetid;
4385 $this->context['link_target_type'] = $targettype;
4386 $result = $this->call_trigger('OBJECT_LINK_DELETE', $f_user);
4387 if ($result < 0) {
4388 $error++;
4389 }
4390 // End call triggers
4391 }
4392
4393 if (!$error) {
4394 $sql = "DELETE FROM " . $this->db->prefix() . "element_element";
4395 $sql .= " WHERE";
4396 if ($rowid > 0) {
4397 $sql .= " rowid = " . ((int) $rowid);
4398 } else {
4399 if ($deletesource) {
4400 $sql .= " fk_source = " . ((int) $sourceid) . " AND sourcetype = '" . $this->db->escape($sourcetype) . "'";
4401 $sql .= " AND fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "'";
4402 } elseif ($deletetarget) {
4403 $sql .= " fk_target = " . ((int) $targetid) . " AND targettype = '" . $this->db->escape($targettype) . "'";
4404 $sql .= " AND fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4405 } else {
4406 $sql .= " (fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "')";
4407 $sql .= " OR";
4408 $sql .= " (fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "')";
4409 }
4410 }
4411
4412 dol_syslog(get_class($this) . "::deleteObjectLinked", LOG_DEBUG);
4413 if (!$this->db->query($sql)) {
4414 $this->error = $this->db->lasterror();
4415 $this->errors[] = $this->error;
4416 $error++;
4417 }
4418 }
4419
4420 if (!$error) {
4421 $this->db->commit();
4422 return 1;
4423 } else {
4424 $this->db->rollback();
4425 return 0;
4426 }
4427 }
4428
4438 public static function getAllItemsLinkedByObjectID($fk_object_where, $field_select, $field_where, $table_element)
4439 {
4440 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4441 return -1;
4442 }
4443 if (!preg_match('/^[_a-zA-Z0-9]+$/', $field_select)) {
4444 dol_syslog('Invalid value $field_select for parameter '.$field_select.' in call to getAllItemsLinkedByObjectID(). Must be a single field name.', LOG_ERR);
4445 }
4446
4447 global $db;
4448
4449 $sql = "SELECT ".$field_select." FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4450 $resql = $db->query($sql);
4451
4452 $TRes = array();
4453 if (!empty($resql)) {
4454 while ($res = $db->fetch_object($resql)) {
4455 $TRes[] = $res->{$field_select};
4456 }
4457 }
4458
4459 return $TRes;
4460 }
4461
4470 public static function getCountOfItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
4471 {
4472 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4473 return -1;
4474 }
4475
4476 global $db;
4477
4478 $sql = "SELECT COUNT(*) as nb FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4479 $resql = $db->query($sql);
4480 $n = 0;
4481 if ($resql) {
4482 $res = $db->fetch_object($resql);
4483 if ($res) {
4484 $n = $res->nb;
4485 }
4486 }
4487
4488 return $n;
4489 }
4490
4499 public static function deleteAllItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
4500 {
4501 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4502 return -1;
4503 }
4504
4505 global $db;
4506
4507 $sql = "DELETE FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4508 $resql = $db->query($sql);
4509
4510 if (empty($resql)) {
4511 return 0;
4512 }
4513
4514 return 1;
4515 }
4516
4527 public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '', $fieldstatus = 'fk_statut')
4528 {
4529 global $user, $langs, $conf;
4530
4531 $savElementId = $elementId; // To be used later to know if we were using the method using the id of this or not.
4532
4533 $elementId = (!empty($elementId) ? $elementId : $this->id);
4534 $elementTable = (!empty($elementType) ? $elementType : $this->table_element);
4535
4536 $this->db->begin();
4537
4538 if ($elementTable == 'facture_rec') {
4539 $fieldstatus = "suspended";
4540 }
4541 if ($elementTable == 'mailing') {
4542 $fieldstatus = "statut";
4543 }
4544 if ($elementTable == 'cronjob') {
4545 $fieldstatus = "status";
4546 }
4547 if ($elementTable == 'user') {
4548 $fieldstatus = "statut";
4549 }
4550 if ($elementTable == 'expensereport') {
4551 $fieldstatus = "fk_statut";
4552 }
4553 if ($elementTable == 'commande_fournisseur_dispatch') {
4554 $fieldstatus = "status";
4555 }
4556 if ($elementTable == 'prelevement_bons') {
4557 $fieldstatus = "statut";
4558 }
4559 if (isset($this->fields) && is_array($this->fields) && array_key_exists('status', $this->fields)) {
4560 $fieldstatus = 'status';
4561 }
4562
4563 $sql = "UPDATE ".$this->db->prefix().$elementTable;
4564 $sql .= " SET ".$fieldstatus." = ".((int) $status);
4565 // If status = 1 = validated, update also fk_user_valid
4566 // TODO Replace the test on $elementTable by doing a test on existence of the field in $this->fields
4567 if ($status == 1 && in_array($elementTable, array('expensereport', 'inventory'))) {
4568 $sql .= ", fk_user_valid = ".((int) $user->id);
4569 }
4570 if ($status == 1 && in_array($elementTable, array('expensereport'))) {
4571 $sql .= ", date_valid = '".$this->db->idate(dol_now())."'";
4572 }
4573 if ($status == 1 && in_array($elementTable, array('inventory'))) {
4574 $sql .= ", date_validation = '".$this->db->idate(dol_now())."'";
4575 }
4576 $sql .= " WHERE rowid = ".((int) $elementId);
4577 $sql .= " AND ".$fieldstatus." <> ".((int) $status); // We avoid update if status already correct
4578
4579 dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
4580 $resql = $this->db->query($sql);
4581 if ($resql) {
4582 $error = 0;
4583
4584 $nb_rows_affected = $this->db->affected_rows($resql); // should be 1 or 0 if status was already correct
4585
4586 if ($nb_rows_affected > 0) {
4587 if (empty($trigkey)) {
4588 // Try to guess trigkey (for backward compatibility, now we should have trigkey defined into the call of setStatus)
4589 if ($this->element == 'supplier_proposal' && $status == 2) {
4590 $trigkey = 'SUPPLIER_PROPOSAL_SIGN'; // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
4591 }
4592 if ($this->element == 'supplier_proposal' && $status == 3) {
4593 $trigkey = 'SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
4594 }
4595 if ($this->element == 'supplier_proposal' && $status == 4) {
4596 $trigkey = 'SUPPLIER_PROPOSAL_CLOSE'; // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
4597 }
4598 if ($this->element == 'fichinter' && $status == 3) {
4599 $trigkey = 'FICHINTER_CLASSIFY_DONE';
4600 }
4601 if ($this->element == 'fichinter' && $status == 2) {
4602 $trigkey = 'FICHINTER_CLASSIFY_BILLED';
4603 }
4604 if ($this->element == 'fichinter' && $status == 1) {
4605 $trigkey = 'FICHINTER_CLASSIFY_UNBILLED';
4606 }
4607 }
4608
4609 if ($trigkey) {
4610 // Call trigger
4611 $result = $this->call_trigger($trigkey, $user);
4612 if ($result < 0) {
4613 $error++;
4614 }
4615 // End call triggers
4616 }
4617 } else {
4618 // The status was probably already good. We do nothing more, no triggers.
4619 }
4620
4621 if (!$error) {
4622 $this->db->commit();
4623
4624 if (empty($savElementId)) {
4625 // If the element we update is $this (so $elementId was provided as null)
4626 if ($fieldstatus == 'tosell') {
4627 $this->status = $status;
4628 } elseif ($fieldstatus == 'tobuy') {
4629 $this->status_buy = $status; // @phpstan-ignore-line
4630 } else {
4631 $this->statut = $status;
4632 $this->status = $status;
4633 }
4634 }
4635
4636 return 1;
4637 } else {
4638 $this->db->rollback();
4639 dol_syslog(get_class($this)."::setStatut ".$this->error, LOG_ERR);
4640 return -1;
4641 }
4642 } else {
4643 $this->error = $this->db->lasterror();
4644 $this->db->rollback();
4645 return -1;
4646 }
4647 }
4648
4649
4657 public function getCanvas($id = 0, $ref = '')
4658 {
4659 global $conf;
4660
4661 if (empty($id) && empty($ref)) {
4662 return 0;
4663 }
4664 if (getDolGlobalString('MAIN_DISABLE_CANVAS')) {
4665 return 0; // To increase speed. Not enabled by default.
4666 }
4667
4668 // Clean parameters
4669 $ref = trim($ref);
4670
4671 $sql = "SELECT rowid, canvas";
4672 $sql .= " FROM ".$this->db->prefix().$this->table_element;
4673 $sql .= " WHERE entity IN (".getEntity($this->element).")";
4674 if (!empty($id)) {
4675 $sql .= " AND rowid = ".((int) $id);
4676 }
4677 if (!empty($ref)) {
4678 $sql .= " AND ref = '".$this->db->escape($ref)."'";
4679 }
4680
4681 $resql = $this->db->query($sql);
4682 if ($resql) {
4683 $obj = $this->db->fetch_object($resql);
4684 if ($obj) {
4685 $this->canvas = $obj->canvas;
4686 return 1;
4687 } else {
4688 return 0;
4689 }
4690 } else {
4691 dol_print_error($this->db);
4692 return -1;
4693 }
4694 }
4695
4696
4703 public function getSpecialCode($lineid)
4704 {
4705 $sql = "SELECT special_code FROM ".$this->db->prefix().$this->table_element_line;
4706 $sql .= " WHERE rowid = ".((int) $lineid);
4707 $resql = $this->db->query($sql);
4708 if ($resql) {
4709 $row = $this->db->fetch_row($resql);
4710 return (!empty($row[0]) ? $row[0] : 0);
4711 }
4712
4713 return 0;
4714 }
4715
4724 public function isObjectUsed($id = 0, $entity = 0)
4725 {
4726 global $langs;
4727
4728 if (empty($id)) {
4729 $id = $this->id;
4730 }
4731
4732 // Check parameters
4733 if (!isset($this->childtables) || !is_array($this->childtables) || count($this->childtables) == 0) {
4734 dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
4735 return -1;
4736 }
4737
4738 $arraytoscan = $this->childtables; // array('tablename'=>array('fk_element'=>'parentfield'), ...) or array('tablename'=>array('parent'=>table_parent, 'parentkey'=>'nameoffieldforparentfkkey'), ...)
4739 // For backward compatibility, we check if array is old format array('tablename1', 'tablename2', ...)
4740 $tmparray = array_keys($this->childtables);
4741 if (is_numeric($tmparray[0])) {
4742 $arraytoscan = array_flip($this->childtables);
4743 }
4744
4745 // Test if child exists
4746 $haschild = 0;
4747 foreach ($arraytoscan as $table => $element) {
4748 //print $id.'-'.$table.'-'.$elementname.'<br>';
4749 // Check if element can be deleted
4750 $sql = "SELECT COUNT(*) as nb";
4751 $sql.= " FROM ".$this->db->prefix().$table." as c";
4752 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4753 $sql.= ", ".$this->db->prefix().$element['parent']." as p";
4754 }
4755 if (!empty($element['fk_element'])) {
4756 $sql.= " WHERE c.".$element['fk_element']." = ".((int) $id);
4757 } else {
4758 $sql.= " WHERE c.".$this->fk_element." = ".((int) $id);
4759 }
4760 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4761 $sql.= " AND c.".$element['parentkey']." = p.rowid";
4762 }
4763 if (!empty($element['parent']) && !empty($element['parenttypefield']) && !empty($element['parenttypevalue'])) {
4764 $sql.= " AND c.".$element['parenttypefield']." = '".$this->db->escape($element['parenttypevalue'])."'";
4765 }
4766 if (!empty($entity)) {
4767 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4768 $sql.= " AND p.entity = ".((int) $entity);
4769 } else {
4770 $sql.= " AND c.entity = ".((int) $entity);
4771 }
4772 }
4773
4774 $resql = $this->db->query($sql);
4775 if ($resql) {
4776 $obj = $this->db->fetch_object($resql);
4777 if ($obj->nb > 0) {
4778 $langs->load("errors");
4779 //print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
4780 $haschild += $obj->nb;
4781 if (is_numeric($element)) { // very old usage array('table1', 'table2', ...)
4782 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $table);
4783 } elseif (is_string($element)) { // old usage array('table1' => 'TranslateKey1', 'table2' => 'TranslateKey2', ...)
4784 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element));
4785 } else { // new usage: $element['name']=Translation key
4786 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element['name']));
4787 }
4788 break; // We found at least one, we stop here
4789 }
4790 } else {
4791 $this->errors[] = $this->db->lasterror();
4792 return -1;
4793 }
4794 }
4795 if ($haschild > 0) {
4796 $this->errors[] = "ErrorRecordHasChildren";
4797 return $haschild;
4798 } else {
4799 return 0;
4800 }
4801 }
4802
4809 public function hasProductsOrServices($predefined = -1)
4810 {
4811 $nb = 0;
4812
4813 foreach ($this->lines as $key => $val) {
4814 $qualified = 0;
4815 if ($predefined == -1) {
4816 $qualified = 1;
4817 }
4818 if ($predefined == 1 && $val->fk_product > 0) {
4819 $qualified = 1;
4820 }
4821 if ($predefined == 0 && $val->fk_product <= 0) {
4822 $qualified = 1;
4823 }
4824 if ($predefined == 2 && $val->fk_product > 0 && $val->product_type == 0) {
4825 $qualified = 1;
4826 }
4827 if ($predefined == 3 && $val->fk_product > 0 && $val->product_type == 1) {
4828 $qualified = 1;
4829 }
4830 if ($qualified) {
4831 $nb++;
4832 }
4833 }
4834 dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
4835 return $nb;
4836 }
4837
4843 public function getTotalDiscount()
4844 {
4845 if (!empty($this->table_element_line)) {
4846 $total_discount = 0.00;
4847
4848 $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
4849 $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
4850 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
4851
4852 dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
4853 $resql = $this->db->query($sql);
4854 if ($resql) {
4855 $num = $this->db->num_rows($resql);
4856 $i = 0;
4857 while ($i < $num) {
4858 $obj = $this->db->fetch_object($resql);
4859
4860 $pu_ht = $obj->pu_ht;
4861 $qty = $obj->qty;
4862 $total_ht = $obj->total_ht;
4863
4864 $total_discount_line = (float) price2num(($pu_ht * $qty) - $total_ht, 'MT');
4865 $total_discount += $total_discount_line;
4866
4867 $i++;
4868 }
4869 }
4870
4871 //print $total_discount; exit;
4872 return price2num($total_discount);
4873 }
4874
4875 return null;
4876 }
4877
4878
4885 public function getTotalWeightVolume()
4886 {
4887 $totalWeight = 0;
4888 $totalVolume = 0;
4889 // defined for shipment only
4890 $totalOrdered = '';
4891 // defined for shipment only
4892 $totalToShip = '';
4893
4894 foreach ($this->lines as $line) {
4895 if (isset($line->qty_asked)) {
4896 if (empty($totalOrdered)) {
4897 $totalOrdered = 0; // Avoid warning because $totalOrdered is ''
4898 }
4899 $totalOrdered += $line->qty_asked; // defined for shipment only
4900 }
4901 if (isset($line->qty_shipped)) {
4902 if (empty($totalToShip)) {
4903 $totalToShip = 0; // Avoid warning because $totalToShip is ''
4904 }
4905 $totalToShip += $line->qty_shipped; // defined for shipment only
4906 } elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty)) {
4907 if (empty($totalToShip)) {
4908 $totalToShip = 0;
4909 }
4910 $totalToShip += $line->qty; // defined for reception only
4911 }
4912
4913 // Define qty, weight, volume, weight_units, volume_units
4914 if ($this->element == 'shipping') {
4915 // for shipments
4916 $qty = $line->qty_shipped ? $line->qty_shipped : 0;
4917 } else {
4918 $qty = $line->qty ? $line->qty : 0;
4919 }
4920
4921 $weight = !empty($line->weight) ? $line->weight : 0;
4922 ($weight == 0 && !empty($line->product->weight)) ? $weight = $line->product->weight : 0;
4923 $volume = !empty($line->volume) ? $line->volume : 0;
4924 ($volume == 0 && !empty($line->product->volume)) ? $volume = $line->product->volume : 0;
4925
4926 $weight_units = !empty($line->weight_units) ? $line->weight_units : 0;
4927 ($weight_units == 0 && !empty($line->product->weight_units)) ? $weight_units = $line->product->weight_units : 0;
4928 $volume_units = !empty($line->volume_units) ? $line->volume_units : 0;
4929 ($volume_units == 0 && !empty($line->product->volume_units)) ? $volume_units = $line->product->volume_units : 0;
4930
4931 $weightUnit = 0;
4932 $volumeUnit = 0;
4933 if (!empty($weight_units)) {
4934 $weightUnit = $weight_units;
4935 }
4936 if (!empty($volume_units)) {
4937 $volumeUnit = $volume_units;
4938 }
4939
4940 if (empty($totalWeight)) {
4941 $totalWeight = 0; // Avoid warning because $totalWeight is ''
4942 }
4943 if (empty($totalVolume)) {
4944 $totalVolume = 0; // Avoid warning because $totalVolume is ''
4945 }
4946
4947 //var_dump($line->volume_units);
4948 if ($weight_units < 50) { // < 50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
4949 $trueWeightUnit = pow(10, $weightUnit);
4950 $totalWeight += $weight * $qty * $trueWeightUnit;
4951 } else {
4952 if ($weight_units == 99) {
4953 // conversion 1 Pound = 0.45359237 KG
4954 $trueWeightUnit = 0.45359237;
4955 $totalWeight += $weight * $qty * $trueWeightUnit;
4956 } elseif ($weight_units == 98) {
4957 // conversion 1 Ounce = 0.0283495 KG
4958 $trueWeightUnit = 0.0283495;
4959 $totalWeight += $weight * $qty * $trueWeightUnit;
4960 } else {
4961 $totalWeight += $weight * $qty; // This may be wrong if we mix different units
4962 }
4963 }
4964 if ($volume_units < 50) { // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
4965 //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
4966 $trueVolumeUnit = pow(10, $volumeUnit);
4967 //print $line->volume;
4968 $totalVolume += $volume * $qty * $trueVolumeUnit;
4969 } else {
4970 $totalVolume += $volume * $qty; // This may be wrong if we mix different units
4971 }
4972 }
4973
4974 return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
4975 }
4976
4977
4983 public function setExtraParameters()
4984 {
4985 $this->db->begin();
4986
4987 $extraparams = (!empty($this->extraparams) ? json_encode($this->extraparams) : null);
4988
4989 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
4990 $sql .= " SET extraparams = ".(!empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
4991 $sql .= " WHERE rowid = ".((int) $this->id);
4992
4993 dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
4994 $resql = $this->db->query($sql);
4995 if (!$resql) {
4996 $this->error = $this->db->lasterror();
4997 $this->db->rollback();
4998 return -1;
4999 } else {
5000 $this->db->commit();
5001 return 1;
5002 }
5003 }
5004
5005
5006 // --------------------
5007 // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
5008 // --------------------
5009
5010 /* This is to show add lines */
5011
5021 public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl')
5022 {
5023 global $conf, $user, $langs, $object, $hookmanager, $extrafields, $form;
5024
5025 // Line extrafield
5026 if (!is_object($extrafields)) {
5027 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5028 $extrafields = new ExtraFields($this->db);
5029 }
5030 $extrafields->fetch_name_optionals_label($this->table_element_line);
5031
5032 // Output template part (modules that overwrite templates must declare this into descriptor)
5033 // Use global variables + $dateSelector + $seller and $buyer
5034 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'.
5035 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5036 foreach ($dirtpls as $module => $reldir) {
5037 if (!empty($module)) {
5038 $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
5039 } else {
5040 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php';
5041 }
5042
5043 if (empty($conf->file->strict_mode)) {
5044 $res = @include $tpl;
5045 } else {
5046 $res = include $tpl; // for debug
5047 }
5048 if ($res) {
5049 break;
5050 }
5051 }
5052 }
5053
5054
5055
5056 /* This is to show array of line of details */
5057
5058
5073 public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl')
5074 {
5075 global $conf, $hookmanager, $langs, $user, $form, $extrafields, $object;
5076 // TODO We should not use global var for this
5077 global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
5078
5079 // Define usemargins
5080 $usemargins = 0;
5081 if (isModEnabled('margin') && !empty($this->element) && in_array($this->element, array('facture', 'facturerec', 'propal', 'commande'))) {
5082 $usemargins = 1;
5083 }
5084
5085 $num = count($this->lines);
5086
5087 // Line extrafield
5088 if (!is_object($extrafields)) {
5089 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5090 $extrafields = new ExtraFields($this->db);
5091 }
5092 $extrafields->fetch_name_optionals_label($this->table_element_line);
5093
5094 $parameters = array('num'=>$num, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$this->table_element_line);
5095 $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5096 if (empty($reshook)) {
5097 // Output template part (modules that overwrite templates must declare this into descriptor)
5098 // Use global variables + $dateSelector + $seller and $buyer
5099 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook.
5100 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5101 foreach ($dirtpls as $module => $reldir) {
5102 $res = 0;
5103 if (!empty($module)) {
5104 $tpl = dol_buildpath($reldir.'/objectline_title.tpl.php');
5105 } else {
5106 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php';
5107 }
5108 if (file_exists($tpl)) {
5109 if (empty($conf->file->strict_mode)) {
5110 $res = @include $tpl;
5111 } else {
5112 $res = include $tpl; // for debug
5113 }
5114 }
5115 if ($res) {
5116 break;
5117 }
5118 }
5119 }
5120
5121 $i = 0;
5122
5123 print "<!-- begin printObjectLines() --><tbody>\n";
5124 foreach ($this->lines as $line) {
5125 //Line extrafield
5126 $line->fetch_optionals();
5127
5128 //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line)))
5129 if (is_object($hookmanager)) { // Old code is commented on preceding line.
5130 if (empty($line->fk_parent_line)) {
5131 $parameters = array('line'=>$line, 'num'=>$num, 'i'=>$i, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$line->table_element, 'defaulttpldir'=>$defaulttpldir);
5132 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5133 } else {
5134 $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);
5135 $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5136 }
5137 }
5138 if (empty($reshook)) {
5139 $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafields, $defaulttpldir);
5140 }
5141
5142 $i++;
5143 }
5144 print "</tbody><!-- end printObjectLines() -->\n";
5145 }
5146
5164 public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/core/tpl')
5165 {
5166 global $conf, $langs, $user, $object, $hookmanager;
5167 global $form;
5168 global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
5169
5170 $object_rights = $this->getRights();
5171
5172 // var used into tpl
5173 $text = '';
5174 $description = '';
5175
5176 // Line in view mode
5177 if ($action != 'editline' || $selected != $line->id) {
5178 // Product
5179 if (!empty($line->fk_product) && $line->fk_product > 0) {
5180 $product_static = new Product($this->db);
5181 $product_static->fetch($line->fk_product);
5182
5183 $product_static->ref = $line->ref; //can change ref in hook
5184 $product_static->label = !empty($line->label) ? $line->label : ""; //can change label in hook
5185
5186 $text = $product_static->getNomUrl(1);
5187
5188 // Define output language and label
5189 if (getDolGlobalInt('MAIN_MULTILANGS')) {
5190 if (property_exists($this, 'socid') && !is_object($this->thirdparty)) {
5191 dol_print_error('', 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
5192 return;
5193 }
5194
5195 $prod = new Product($this->db);
5196 $prod->fetch($line->fk_product);
5197
5198 $outputlangs = $langs;
5199 $newlang = '';
5200 if (empty($newlang) && GETPOST('lang_id', 'aZ09')) {
5201 $newlang = GETPOST('lang_id', 'aZ09');
5202 }
5203 if (getDolGlobalString('PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE') && empty($newlang) && is_object($this->thirdparty)) {
5204 $newlang = $this->thirdparty->default_lang; // To use language of customer
5205 }
5206 if (!empty($newlang)) {
5207 $outputlangs = new Translate("", $conf);
5208 $outputlangs->setDefaultLang($newlang);
5209 }
5210
5211 $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
5212 } else {
5213 $label = $line->product_label;
5214 }
5215
5216 $text .= ' - '.(!empty($line->label) ? $line->label : $label);
5217 $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.
5218 }
5219
5220 $line->pu_ttc = price2num((!empty($line->subprice) ? $line->subprice : 0) * (1 + ((!empty($line->tva_tx) ? $line->tva_tx : 0) / 100)), 'MU');
5221
5222 // Output template part (modules that overwrite templates must declare this into descriptor)
5223 // Use global variables + $dateSelector + $seller and $buyer
5224 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5225 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5226 foreach ($dirtpls as $module => $reldir) {
5227 $res = 0;
5228 if (!empty($module)) {
5229 $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
5230 } else {
5231 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php';
5232 }
5233 //var_dump($tpl);
5234 if (file_exists($tpl)) {
5235 if (empty($conf->file->strict_mode)) {
5236 $res = @include $tpl;
5237 } else {
5238 $res = include $tpl; // for debug
5239 }
5240 }
5241 if ($res) {
5242 break;
5243 }
5244 }
5245 }
5246
5247 // Line in update mode
5248 if ($this->statut == 0 && $action == 'editline' && $selected == $line->id) {
5249 $label = (!empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
5250
5251 $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU');
5252
5253 // Output template part (modules that overwrite templates must declare this into descriptor)
5254 // Use global variables + $dateSelector + $seller and $buyer
5255 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5256 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5257 foreach ($dirtpls as $module => $reldir) {
5258 if (!empty($module)) {
5259 $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
5260 } else {
5261 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php';
5262 }
5263
5264 if (empty($conf->file->strict_mode)) {
5265 $res = @include $tpl;
5266 } else {
5267 $res = include $tpl; // for debug
5268 }
5269 if ($res) {
5270 break;
5271 }
5272 }
5273 }
5274 }
5275
5276
5277 /* This is to show array of line of details of source object */
5278
5279
5290 public function printOriginLinesList($restrictlist = '', $selectedLines = array())
5291 {
5292 global $langs, $hookmanager, $conf, $form, $action;
5293
5294 print '<tr class="liste_titre">';
5295 print '<td class="linecolref">'.$langs->trans('Ref').'</td>';
5296 print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
5297 print '<td class="linecolvat right">'.$langs->trans('VATRate').'</td>';
5298 print '<td class="linecoluht right">'.$langs->trans('PriceUHT').'</td>';
5299 if (isModEnabled("multicurrency")) {
5300 print '<td class="linecoluht_currency right">'.$langs->trans('PriceUHTCurrency').'</td>';
5301 }
5302 print '<td class="linecolqty right">'.$langs->trans('Qty').'</td>';
5303 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5304 print '<td class="linecoluseunit left">'.$langs->trans('Unit').'</td>';
5305 }
5306 print '<td class="linecoldiscount right">'.$langs->trans('ReductionShort').'</td>';
5307 print '<td class="linecolht right">'.$langs->trans('TotalHT').'</td>';
5308 print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
5309 print '</tr>';
5310 $i = 0;
5311
5312 if (!empty($this->lines)) {
5313 foreach ($this->lines as $line) {
5314 $reshook = 0;
5315 //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line))) {
5316 if (is_object($hookmanager)) { // Old code is commented on preceding line.
5317 $parameters = array('line'=>$line, 'i'=>$i, 'restrictlist'=>$restrictlist, 'selectedLines'=> $selectedLines);
5318 if (!empty($line->fk_parent_line)) {
5319 $parameters['fk_parent_line'] = $line->fk_parent_line;
5320 }
5321 $reshook = $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5322 }
5323 if (empty($reshook)) {
5324 $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines);
5325 }
5326
5327 $i++;
5328 }
5329 }
5330 }
5331
5345 public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
5346 {
5347 global $langs, $conf;
5348
5349 //var_dump($line);
5350 if (!empty($line->date_start)) {
5351 $date_start = $line->date_start;
5352 } else {
5353 $date_start = $line->date_debut_prevue;
5354 if ($line->date_debut_reel) {
5355 $date_start = $line->date_debut_reel;
5356 }
5357 }
5358 if (!empty($line->date_end)) {
5359 $date_end = $line->date_end;
5360 } else {
5361 $date_end = $line->date_fin_prevue;
5362 if ($line->date_fin_reel) {
5363 $date_end = $line->date_fin_reel;
5364 }
5365 }
5366
5367 $this->tpl['id'] = $line->id;
5368
5369 $this->tpl['label'] = '';
5370 if (!empty($line->fk_parent_line)) {
5371 $this->tpl['label'] .= img_picto('', 'rightarrow');
5372 }
5373
5374 if (($line->info_bits & 2) == 2) { // TODO Not sure this is used for source object
5375 $discount = new DiscountAbsolute($this->db);
5376 if (property_exists($this, 'socid')) {
5377 $discount->fk_soc = $this->socid;
5378 }
5379 $this->tpl['label'] .= $discount->getNomUrl(0, 'discount');
5380 } elseif (!empty($line->fk_product)) {
5381 $productstatic = new Product($this->db);
5382 $productstatic->id = $line->fk_product;
5383 $productstatic->ref = $line->ref;
5384 $productstatic->type = $line->fk_product_type;
5385 if (empty($productstatic->ref)) {
5386 $line->fetch_product();
5387 $productstatic = $line->product;
5388 }
5389
5390 $this->tpl['label'] .= $productstatic->getNomUrl(1);
5391 $this->tpl['label'] .= ' - '.(!empty($line->label) ? $line->label : $line->product_label);
5392 // Dates
5393 if ($line->product_type == 1 && ($date_start || $date_end)) {
5394 $this->tpl['label'] .= get_date_range($date_start, $date_end);
5395 }
5396 } else {
5397 $this->tpl['label'] .= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
5398 if (!empty($line->desc)) {
5399 $this->tpl['label'] .= $line->desc;
5400 } else {
5401 $this->tpl['label'] .= ($line->label ? '&nbsp;'.$line->label : '');
5402 }
5403
5404 // Dates
5405 if ($line->product_type == 1 && ($date_start || $date_end)) {
5406 $this->tpl['label'] .= get_date_range($date_start, $date_end);
5407 }
5408 }
5409
5410 if (!empty($line->desc)) {
5411 if ($line->desc == '(CREDIT_NOTE)') { // 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("DiscountFromCreditNote", $discount->getNomUrl(0));
5415 } elseif ($line->desc == '(DEPOSIT)') { // TODO Not sure this is used for source object
5416 $discount = new DiscountAbsolute($this->db);
5417 $discount->fetch($line->fk_remise_except);
5418 $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
5419 } elseif ($line->desc == '(EXCESS RECEIVED)') {
5420 $discount = new DiscountAbsolute($this->db);
5421 $discount->fetch($line->fk_remise_except);
5422 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
5423 } elseif ($line->desc == '(EXCESS PAID)') {
5424 $discount = new DiscountAbsolute($this->db);
5425 $discount->fetch($line->fk_remise_except);
5426 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
5427 } else {
5428 $this->tpl['description'] = dol_trunc($line->desc, 60);
5429 }
5430 } else {
5431 $this->tpl['description'] = '&nbsp;';
5432 }
5433
5434 // VAT Rate
5435 $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
5436 $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
5437 if (!empty($line->vat_src_code) && !preg_match('/\‍(/', $this->tpl['vat_rate'])) {
5438 $this->tpl['vat_rate'] .= ' ('.$line->vat_src_code.')';
5439 }
5440
5441 $this->tpl['price'] = price($line->subprice);
5442 $this->tpl['total_ht'] = price($line->total_ht);
5443 $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
5444 $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
5445 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5446 $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
5447 }
5448 $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
5449
5450 // Is the line strike or not
5451 $this->tpl['strike'] = 0;
5452 if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) {
5453 $this->tpl['strike'] = 1;
5454 }
5455
5456 // Output template part (modules that overwrite templates must declare this into descriptor)
5457 // Use global variables + $dateSelector + $seller and $buyer
5458 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5459 foreach ($dirtpls as $module => $reldir) {
5460 if (!empty($module)) {
5461 $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
5462 } else {
5463 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php';
5464 }
5465
5466 if (empty($conf->file->strict_mode)) {
5467 $res = @include $tpl;
5468 } else {
5469 $res = include $tpl; // for debug
5470 }
5471 if ($res) {
5472 break;
5473 }
5474 }
5475 }
5476
5477
5478 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5489 public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
5490 {
5491 // phpcs:enable
5492 $this->db->begin();
5493
5494 $sql = "INSERT INTO ".$this->db->prefix()."element_resources (";
5495 $sql .= "resource_id";
5496 $sql .= ", resource_type";
5497 $sql .= ", element_id";
5498 $sql .= ", element_type";
5499 $sql .= ", busy";
5500 $sql .= ", mandatory";
5501 $sql .= ") VALUES (";
5502 $sql .= ((int) $resource_id);
5503 $sql .= ", '".$this->db->escape($resource_type)."'";
5504 $sql .= ", '".$this->db->escape($this->id)."'";
5505 $sql .= ", '".$this->db->escape($this->element)."'";
5506 $sql .= ", '".$this->db->escape($busy)."'";
5507 $sql .= ", '".$this->db->escape($mandatory)."'";
5508 $sql .= ")";
5509
5510 dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
5511 if ($this->db->query($sql)) {
5512 $this->db->commit();
5513 return 1;
5514 } else {
5515 $this->error = $this->db->lasterror();
5516 $this->db->rollback();
5517 return 0;
5518 }
5519 }
5520
5521 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5530 public function delete_resource($rowid, $element, $notrigger = 0)
5531 {
5532 // phpcs:enable
5533 global $user;
5534
5535 $this->db->begin();
5536
5537 $sql = "DELETE FROM ".$this->db->prefix()."element_resources";
5538 $sql .= " WHERE rowid = ".((int) $rowid);
5539
5540 dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
5541
5542 $resql = $this->db->query($sql);
5543 if (!$resql) {
5544 $this->error = $this->db->lasterror();
5545 $this->db->rollback();
5546 return -1;
5547 } else {
5548 if (!$notrigger) {
5549 $result = $this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
5550 if ($result < 0) {
5551 $this->db->rollback();
5552 return -1;
5553 }
5554 }
5555 $this->db->commit();
5556 return 1;
5557 }
5558 }
5559
5560
5566 public function __clone()
5567 {
5568 // Force a copy of this->lines, otherwise it will point to same object.
5569 if (isset($this->lines) && is_array($this->lines)) {
5570 $nboflines = count($this->lines);
5571 for ($i = 0; $i < $nboflines; $i++) {
5572 if (is_object($this->lines[$i])) {
5573 $this->lines[$i] = clone $this->lines[$i];
5574 }
5575 }
5576 }
5577 }
5578
5592 protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
5593 {
5594 global $conf, $langs, $user, $hookmanager, $action;
5595
5596 $srctemplatepath = '';
5597
5598 $parameters = array('modelspath'=>$modelspath, 'modele'=>$modele, 'outputlangs'=>$outputlangs, 'hidedetails'=>$hidedetails, 'hidedesc'=>$hidedesc, 'hideref'=>$hideref, 'moreparams'=>$moreparams);
5599 $reshook = $hookmanager->executeHooks('commonGenerateDocument', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5600
5601 if (!empty($reshook)) {
5602 return $reshook;
5603 }
5604
5605 dol_syslog("commonGenerateDocument modele=".$modele." outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
5606
5607 if (empty($modele)) {
5608 $this->error = 'BadValueForParameterModele';
5609 return -1;
5610 }
5611
5612 // Increase limit for PDF build
5613 $err = error_reporting();
5614 error_reporting(0);
5615 @set_time_limit(120);
5616 error_reporting($err);
5617
5618 // If selected model is a filename template (then $modele="modelname" or "modelname:filename")
5619 $tmp = explode(':', $modele, 2);
5620 $saved_model = $modele;
5621 if (!empty($tmp[1])) {
5622 $modele = $tmp[0];
5623 $srctemplatepath = $tmp[1];
5624 }
5625
5626 // Search template files
5627 $file = '';
5628 $classname = '';
5629 $filefound = '';
5630 $dirmodels = array('/');
5631 if (is_array($conf->modules_parts['models'])) {
5632 $dirmodels = array_merge($dirmodels, $conf->modules_parts['models']);
5633 }
5634 foreach ($dirmodels as $reldir) {
5635 foreach (array('doc', 'pdf') as $prefix) {
5636 if (in_array(get_class($this), array('Adherent'))) {
5637 // Member module use prefix_modele.class.php
5638 $file = $prefix."_".$modele.".class.php";
5639 } else {
5640 // Other module use prefix_modele.modules.php
5641 $file = $prefix."_".$modele.".modules.php";
5642 }
5643
5644 $file = dol_sanitizeFileName($file);
5645
5646 // We chack if file exists
5647 $file = dol_buildpath($reldir.$modelspath.$file, 0);
5648 if (file_exists($file)) {
5649 $filefound = $file;
5650 $classname = $prefix.'_'.$modele;
5651 break;
5652 }
5653 }
5654 if ($filefound) {
5655 break;
5656 }
5657 }
5658
5659 if (!$filefound) {
5660 $this->error = $langs->trans("Error").' Failed to load doc generator with modelpaths='.$modelspath.' - modele='.$modele;
5661 $this->errors[] = $this->error;
5662 dol_syslog($this->error, LOG_ERR);
5663 return -1;
5664 }
5665
5666 // Sanitize $filefound
5667 $filefound = dol_sanitizePathName($filefound);
5668
5669 // If generator was found
5670 global $db; // Required to solve a conception default making an include of some code that uses $db instead of $this->db just after.
5671
5672 require_once $filefound;
5673
5674 $obj = new $classname($this->db);
5675
5676 // If generator is ODT, we must have srctemplatepath defined, if not we set it.
5677 if ($obj->type == 'odt' && empty($srctemplatepath)) {
5678 $varfortemplatedir = $obj->scandir;
5679 if ($varfortemplatedir && getDolGlobalString($varfortemplatedir)) {
5680 $dirtoscan = getDolGlobalString($varfortemplatedir);
5681
5682 $listoffiles = array();
5683
5684 // Now we add first model found in directories scanned
5685 $listofdir = explode(',', $dirtoscan);
5686 foreach ($listofdir as $key => $tmpdir) {
5687 $tmpdir = trim($tmpdir);
5688 $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
5689 if (!$tmpdir) {
5690 unset($listofdir[$key]);
5691 continue;
5692 }
5693 if (is_dir($tmpdir)) {
5694 $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
5695 if (count($tmpfiles)) {
5696 $listoffiles = array_merge($listoffiles, $tmpfiles);
5697 }
5698 }
5699 }
5700
5701 if (count($listoffiles)) {
5702 foreach ($listoffiles as $record) {
5703 $srctemplatepath = $record['fullname'];
5704 break;
5705 }
5706 }
5707 }
5708
5709 if (empty($srctemplatepath)) {
5710 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
5711 return -1;
5712 }
5713 }
5714
5715 if ($obj->type == 'odt' && !empty($srctemplatepath)) {
5716 if (!dol_is_file($srctemplatepath)) {
5717 dol_syslog("Failed to locate template file ".$srctemplatepath, LOG_WARNING);
5718 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
5719 return -1;
5720 }
5721 }
5722
5723 // We save charset_output to restore it because write_file can change it if needed for
5724 // output format that does not support UTF8.
5725 $sav_charset_output = empty($outputlangs->charset_output) ? '' : $outputlangs->charset_output;
5726
5727 // update model_pdf in object
5728 $this->model_pdf = $saved_model;
5729
5730 if (in_array(get_class($this), array('Adherent'))) {
5731 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, 'tmp_cards', $moreparams);
5732 } else {
5733 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
5734 }
5735 // After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
5736
5737 if ($resultwritefile > 0) {
5738 $outputlangs->charset_output = $sav_charset_output;
5739
5740 // We delete old preview
5741 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5742 dol_delete_preview($this);
5743
5744 // Index file in database
5745 if (!empty($obj->result['fullpath'])) {
5746 $destfull = $obj->result['fullpath'];
5747
5748 // Update the last_main_doc field into main object (if document generator has property ->update_main_doc_field set)
5749 $update_main_doc_field = 0;
5750 if (!empty($obj->update_main_doc_field)) {
5751 $update_main_doc_field = 1;
5752 }
5753
5754 // Check that the file exists, before indexing it.
5755 // Hint: It does not exist, if we create a PDF and auto delete the ODT File
5756 if (dol_is_file($destfull)) {
5757 $this->indexFile($destfull, $update_main_doc_field);
5758 }
5759 } else {
5760 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);
5761 }
5762
5763 // Success in building document. We build meta file.
5764 dol_meta_create($this);
5765
5766 return 1;
5767 } else {
5768 $outputlangs->charset_output = $sav_charset_output;
5769 $this->error = $obj->error;
5770 $this->errors = $obj->errors;
5771 dol_syslog("Error generating document for ".__CLASS__.". Error: ".$obj->error, LOG_ERR);
5772 return -1;
5773 }
5774 }
5775
5785 public function indexFile($destfull, $update_main_doc_field)
5786 {
5787 global $conf, $user;
5788
5789 $upload_dir = dirname($destfull);
5790 $destfile = basename($destfull);
5791 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
5792
5793 if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a tmp dir
5794 $filename = basename($destfile);
5795 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
5796 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
5797
5798 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
5799 $ecmfile = new EcmFiles($this->db);
5800 $result = $ecmfile->fetch(0, '', ($rel_dir ? $rel_dir.'/' : '').$filename);
5801
5802 // Set the public "share" key
5803 $setsharekey = false;
5804 if ($this->element == 'propal' || $this->element == 'proposal') {
5805 if (getDolGlobalInt("PROPOSAL_ALLOW_ONLINESIGN")) {
5806 $setsharekey = true; // feature to make online signature is not set or set to on (default)
5807 }
5808 if (getDolGlobalInt("PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD")) {
5809 $setsharekey = true;
5810 }
5811 }
5812 if ($this->element == 'commande' && getDolGlobalInt("ORDER_ALLOW_EXTERNAL_DOWNLOAD")) {
5813 $setsharekey = true;
5814 }
5815 if ($this->element == 'facture' && getDolGlobalInt("INVOICE_ALLOW_EXTERNAL_DOWNLOAD")) {
5816 $setsharekey = true;
5817 }
5818 if ($this->element == 'bank_account' && getDolGlobalInt("BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD")) {
5819 $setsharekey = true;
5820 }
5821 if ($this->element == 'product' && getDolGlobalInt("PRODUCT_ALLOW_EXTERNAL_DOWNLOAD")) {
5822 $setsharekey = true;
5823 }
5824 if ($this->element == 'contrat' && getDolGlobalInt("CONTRACT_ALLOW_EXTERNAL_DOWNLOAD")) {
5825 $setsharekey = true;
5826 }
5827 if ($this->element == 'fichinter' && getDolGlobalInt("FICHINTER_ALLOW_EXTERNAL_DOWNLOAD")) {
5828 $setsharekey = true;
5829 }
5830 if ($this->element == 'supplier_proposal' && getDolGlobalInt("SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD")) {
5831 $setsharekey = true;
5832 }
5833 if ($this->element == 'societe_rib' && getDolGlobalInt("SOCIETE_RIB_ALLOW_ONLINESIGN")) {
5834 $setsharekey = true;
5835 }
5836
5837 if ($setsharekey) {
5838 if (empty($ecmfile->share)) { // Because object not found or share not set yet
5839 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
5840 $ecmfile->share = getRandomPassword(true);
5841 }
5842 }
5843
5844 if ($result > 0) {
5845 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5846 $ecmfile->fullpath_orig = '';
5847 $ecmfile->gen_or_uploaded = 'generated';
5848 $ecmfile->description = ''; // indexed content
5849 $ecmfile->keywords = ''; // keyword content
5850 $result = $ecmfile->update($user);
5851 if ($result < 0) {
5852 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5853 return -1;
5854 }
5855 } else {
5856 $ecmfile->entity = $conf->entity;
5857 $ecmfile->filepath = $rel_dir;
5858 $ecmfile->filename = $filename;
5859 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5860 $ecmfile->fullpath_orig = '';
5861 $ecmfile->gen_or_uploaded = 'generated';
5862 $ecmfile->description = ''; // indexed content
5863 $ecmfile->keywords = ''; // keyword content
5864 $ecmfile->src_object_type = $this->table_element; // $this->table_name is 'myobject' or 'mymodule_myobject'.
5865 $ecmfile->src_object_id = $this->id;
5866
5867 $result = $ecmfile->create($user);
5868 if ($result < 0) {
5869 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5870 return -1;
5871 }
5872 }
5873
5874 /*$this->result['fullname']=$destfull;
5875 $this->result['filepath']=$ecmfile->filepath;
5876 $this->result['filename']=$ecmfile->filename;*/
5877 //var_dump($obj->update_main_doc_field);exit;
5878
5879 if ($update_main_doc_field && !empty($this->table_element)) {
5880 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath."/".$ecmfile->filename)."'";
5881 $sql .= " WHERE rowid = ".((int) $this->id);
5882
5883 $resql = $this->db->query($sql);
5884 if (!$resql) {
5885 dol_print_error($this->db);
5886 return -1;
5887 } else {
5888 $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
5889 }
5890 }
5891 }
5892
5893 return 1;
5894 }
5895
5903 public function addThumbs($file)
5904 {
5905 $file_osencoded = dol_osencode($file);
5906
5907 if (file_exists($file_osencoded)) {
5908 require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
5909
5910 $tmparraysize = getDefaultImageSizes();
5911 $maxwidthsmall = $tmparraysize['maxwidthsmall'];
5912 $maxheightsmall = $tmparraysize['maxheightsmall'];
5913 $maxwidthmini = $tmparraysize['maxwidthmini'];
5914 $maxheightmini = $tmparraysize['maxheightmini'];
5915 //$quality = $tmparraysize['quality'];
5916 $quality = 50; // For thumbs, we force quality to 50
5917
5918 // Create small thumbs for company (Ratio is near 16/9)
5919 // Used on logon for example
5920 vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
5921
5922 // Create mini thumbs for company (Ratio is near 16/9)
5923 // Used on menu or for setup page for example
5924 vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
5925 }
5926 }
5927
5935 public function delThumbs($file)
5936 {
5937 $imgThumbName = getImageFileNameForSize($file, '_small'); // Full path of thumb file
5938 dol_delete_file($imgThumbName);
5939 $imgThumbName = getImageFileNameForSize($file, '_mini'); // Full path of thumb file
5940 dol_delete_file($imgThumbName);
5941 }
5942
5943
5944 /* Functions common to commonobject and commonobjectline */
5945
5946 /* For default values */
5947
5961 public function getDefaultCreateValueFor($fieldname, $alternatevalue = null, $type = 'alphanohtml')
5962 {
5963 global $conf, $_POST;
5964
5965 // If param here has been posted, we use this value first.
5966 if (GETPOSTISSET($fieldname)) {
5967 return GETPOST($fieldname, $type, 3);
5968 }
5969
5970 if (isset($alternatevalue)) {
5971 return $alternatevalue;
5972 }
5973
5974 $newelement = $this->element;
5975 if ($newelement == 'facture') {
5976 $newelement = 'invoice';
5977 }
5978 if ($newelement == 'commande') {
5979 $newelement = 'order';
5980 }
5981 if (empty($newelement)) {
5982 dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
5983 return '';
5984 }
5985
5986 $keyforfieldname = strtoupper($newelement.'_DEFAULT_'.$fieldname);
5987 //var_dump($keyforfieldname);
5988 if (getDolGlobalString($keyforfieldname)) {
5989 return getDolGlobalString($keyforfieldname);
5990 }
5991
5992 // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
5993 // store content into $conf->cache['overwrite_default']
5994
5995 return '';
5996 }
5997
5998
5999 /* For triggers */
6000
6001
6002 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6013 public function call_trigger($triggerName, $user)
6014 {
6015 // phpcs:enable
6016 global $langs, $conf;
6017
6018 if (!empty(self::TRIGGER_PREFIX) && strpos($triggerName, self::TRIGGER_PREFIX . '_') !== 0) {
6019 dol_print_error('', 'The trigger "' . $triggerName . '" does not start with "' . self::TRIGGER_PREFIX . '_" as required.');
6020 exit;
6021 }
6022 if (!is_object($langs)) { // If lang was not defined, we set it. It is required by run_triggers().
6023 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
6024 $langs = new Translate('', $conf);
6025 }
6026
6027 include_once DOL_DOCUMENT_ROOT.'/core/class/interfaces.class.php';
6028 $interface = new Interfaces($this->db);
6029 $result = $interface->run_triggers($triggerName, $this, $user, $langs, $conf);
6030
6031 if ($result < 0) {
6032 if (!empty($this->errors)) {
6033 $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.
6034 } else {
6035 $this->errors = $interface->errors;
6036 }
6037 }
6038 return $result;
6039 }
6040
6041
6042 /* Functions for data in other language */
6043
6044
6053 {
6054 // To avoid SQL errors. Probably not the better solution though
6055 if (!$this->element) {
6056 return 0;
6057 }
6058 if (!($this->id > 0)) {
6059 return 0;
6060 }
6061 if (is_array($this->array_languages)) {
6062 return 1;
6063 }
6064
6065 $this->array_languages = array();
6066
6067 $element = $this->element;
6068 if ($element == 'categorie') {
6069 $element = 'categories'; // For compatibility
6070 }
6071
6072 // Request to get translation values for object
6073 $sql = "SELECT rowid, property, lang , value";
6074 $sql .= " FROM ".$this->db->prefix()."object_lang";
6075 $sql .= " WHERE type_object = '".$this->db->escape($element)."'";
6076 $sql .= " AND fk_object = ".((int) $this->id);
6077
6078 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6079 $resql = $this->db->query($sql);
6080 if ($resql) {
6081 $numrows = $this->db->num_rows($resql);
6082 if ($numrows) {
6083 $i = 0;
6084 while ($i < $numrows) {
6085 $obj = $this->db->fetch_object($resql);
6086 $key = $obj->property;
6087 $value = $obj->value;
6088 $codelang = $obj->lang;
6089 $type = $this->fields[$key]['type'];
6090
6091 // we can add this attribute to object
6092 if (preg_match('/date/', $type)) {
6093 $this->array_languages[$key][$codelang] = $this->db->jdate($value);
6094 } else {
6095 $this->array_languages[$key][$codelang] = $value;
6096 }
6097
6098 $i++;
6099 }
6100 }
6101
6102 $this->db->free($resql);
6103
6104 if ($numrows) {
6105 return $numrows;
6106 } else {
6107 return 0;
6108 }
6109 } else {
6110 dol_print_error($this->db);
6111 return -1;
6112 }
6113 }
6114
6121 public function setValuesForExtraLanguages($onlykey = '')
6122 {
6123 global $_POST, $langs;
6124
6125 // Get extra fields
6126 foreach ($_POST as $postfieldkey => $postfieldvalue) {
6127 $tmparray = explode('-', $postfieldkey);
6128 if ($tmparray[0] != 'field') {
6129 continue;
6130 }
6131
6132 $element = $tmparray[1];
6133 $key = $tmparray[2];
6134 $codelang = $tmparray[3];
6135 //var_dump("postfieldkey=".$postfieldkey." element=".$element." key=".$key." codelang=".$codelang);
6136
6137 if (!empty($onlykey) && $key != $onlykey) {
6138 continue;
6139 }
6140 if ($element != $this->element) {
6141 continue;
6142 }
6143
6144 $key_type = $this->fields[$key]['type'];
6145
6146 $enabled = 1;
6147 if (isset($this->fields[$key]['enabled'])) {
6148 $enabled = dol_eval($this->fields[$key]['enabled'], 1, 1, '1');
6149 }
6150 /*$perms = 1;
6151 if (isset($this->fields[$key]['perms']))
6152 {
6153 $perms = dol_eval($this->fields[$key]['perms'], 1, 1, '1');
6154 }*/
6155 if (empty($enabled)) {
6156 continue;
6157 }
6158 //if (empty($perms)) continue;
6159
6160 if (in_array($key_type, array('date'))) {
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(0, 0, 0, GETPOST($postfieldkey."month", 'int'), GETPOST($postfieldkey."day", 'int'), GETPOST($postfieldkey."year", 'int'));
6164 } elseif (in_array($key_type, array('datetime'))) {
6165 // Clean parameters
6166 // TODO GMT date in memory must be GMT so we should add gm=true in parameters
6167 $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'));
6168 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
6169 $value_arr = GETPOST($postfieldkey, 'array'); // check if an array
6170 if (!empty($value_arr)) {
6171 $value_key = implode(',', $value_arr);
6172 } else {
6173 $value_key = '';
6174 }
6175 } elseif (in_array($key_type, array('price', 'double'))) {
6176 $value_arr = GETPOST($postfieldkey, 'alpha');
6177 $value_key = price2num($value_arr);
6178 } else {
6179 $value_key = GETPOST($postfieldkey);
6180 if (in_array($key_type, array('link')) && $value_key == '-1') {
6181 $value_key = '';
6182 }
6183 }
6184
6185 $this->array_languages[$key][$codelang] = $value_key;
6186
6187 /*if ($nofillrequired) {
6188 $langs->load('errors');
6189 setEventMessages($langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required), null, 'errors');
6190 return -1;
6191 }*/
6192 }
6193
6194 return 1;
6195 }
6196
6197
6198 /* Functions for extrafields */
6199
6206 public function fetchNoCompute($id)
6207 {
6208 global $conf;
6209
6210 $savDisableCompute = $conf->disable_compute;
6211 $conf->disable_compute = 1;
6212
6213 $ret = $this->fetch($id); /* @phpstan-ignore-line */
6214
6215 $conf->disable_compute = $savDisableCompute;
6216
6217 return $ret;
6218 }
6219
6220 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6230 public function fetch_optionals($rowid = null, $optionsArray = null)
6231 {
6232 // phpcs:enable
6233 global $conf, $extrafields;
6234
6235 if (empty($rowid)) {
6236 $rowid = $this->id;
6237 }
6238 if (empty($rowid) && isset($this->rowid)) {
6239 $rowid = $this->rowid; // deprecated
6240 }
6241
6242 // To avoid SQL errors. Probably not the better solution though
6243 if (!$this->table_element) {
6244 return 0;
6245 }
6246
6247 $this->array_options = array();
6248
6249 if (!is_array($optionsArray)) {
6250 // If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
6251 if (!isset($extrafields) || !is_object($extrafields)) {
6252 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6253 $extrafields = new ExtraFields($this->db);
6254 }
6255
6256 // Load array of extrafields for elementype = $this->table_element
6257 if (empty($extrafields->attributes[$this->table_element]['loaded'])) {
6258 $extrafields->fetch_name_optionals_label($this->table_element);
6259 }
6260 $optionsArray = (!empty($extrafields->attributes[$this->table_element]['label']) ? $extrafields->attributes[$this->table_element]['label'] : null);
6261 } else {
6262 global $extrafields;
6263 dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
6264 }
6265
6266 $table_element = $this->table_element;
6267 if ($table_element == 'categorie') {
6268 $table_element = 'categories'; // For compatibility
6269 }
6270
6271 // Request to get complementary values
6272 if (is_array($optionsArray) && count($optionsArray) > 0) {
6273 $sql = "SELECT rowid";
6274 foreach ($optionsArray as $name => $label) {
6275 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate') {
6276 $sql .= ", ".$name;
6277 }
6278 }
6279 $sql .= " FROM ".$this->db->prefix().$table_element."_extrafields";
6280 $sql .= " WHERE fk_object = ".((int) $rowid);
6281
6282 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6283 $resql = $this->db->query($sql);
6284 if ($resql) {
6285 $numrows = $this->db->num_rows($resql);
6286 if ($numrows) {
6287 $tab = $this->db->fetch_array($resql);
6288
6289 foreach ($tab as $key => $value) {
6290 // 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)
6291 if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && !is_int($key)) {
6292 // we can add this attribute to object
6293 if (!empty($extrafields->attributes[$this->table_element]) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date', 'datetime'))) {
6294 //var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
6295 $this->array_options["options_".$key] = $this->db->jdate($value);
6296 } else {
6297 $this->array_options["options_".$key] = $value;
6298 }
6299
6300 //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
6301 }
6302 if (!empty($extrafields->attributes[$this->table_element]['type'][$key]) && $extrafields->attributes[$this->table_element]['type'][$key] == 'password') {
6303 if (!empty($value) && preg_match('/^dolcrypt:/', $value)) {
6304 $this->array_options["options_".$key] = dolDecrypt($value);
6305 }
6306 }
6307 }
6308 }
6309
6310 // If field is a computed field, value must become result of compute (regardless of whether a row exists
6311 // in the element's extrafields table)
6312 if (is_array($extrafields->attributes[$this->table_element]['label'])) {
6313 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
6314 if (!empty($extrafields->attributes[$this->table_element]) && !empty($extrafields->attributes[$this->table_element]['computed'][$key])) {
6315 //var_dump($conf->disable_compute);
6316 if (empty($conf->disable_compute)) {
6317 global $objectoffield; // We set a global variable to $objectoffield so
6318 $objectoffield = $this; // we can use it inside computed formula
6319 $this->array_options['options_' . $key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0, '2');
6320 }
6321 }
6322 }
6323 }
6324
6325 $this->db->free($resql);
6326
6327 if ($numrows) {
6328 return $numrows;
6329 } else {
6330 return 0;
6331 }
6332 } else {
6333 $this->errors[]=$this->db->lasterror;
6334 return -1;
6335 }
6336 }
6337 return 0;
6338 }
6339
6346 public function deleteExtraFields()
6347 {
6348 global $conf;
6349
6350 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6351 return 0;
6352 }
6353
6354 $this->db->begin();
6355
6356 $table_element = $this->table_element;
6357 if ($table_element == 'categorie') {
6358 $table_element = 'categories'; // For compatibility
6359 }
6360
6361 dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
6362
6363 $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6364
6365 $resql = $this->db->query($sql_del);
6366 if (!$resql) {
6367 $this->error = $this->db->lasterror();
6368 $this->db->rollback();
6369 return -1;
6370 } else {
6371 $this->db->commit();
6372 return 1;
6373 }
6374 }
6375
6386 public function insertExtraFields($trigger = '', $userused = null)
6387 {
6388 global $conf, $langs, $user;
6389
6390 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6391 return 0;
6392 }
6393
6394 if (empty($userused)) {
6395 $userused = $user;
6396 }
6397
6398 $error = 0;
6399
6400 if (!empty($this->array_options)) {
6401 // Check parameters
6402 $langs->load('admin');
6403 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6404 $extrafields = new ExtraFields($this->db);
6405 $target_extrafields = $extrafields->fetch_name_optionals_label($this->table_element);
6406
6407 // Eliminate copied source object extra fields that do not exist in target object
6408 $new_array_options = array();
6409 foreach ($this->array_options as $key => $value) {
6410 if (in_array(substr($key, 8), array_keys($target_extrafields))) { // We remove the 'options_' from $key for test
6411 $new_array_options[$key] = $value;
6412 } elseif (in_array($key, array_keys($target_extrafields))) { // We test on $key that does not contain the 'options_' prefix
6413 $new_array_options['options_'.$key] = $value;
6414 }
6415 }
6416
6417 foreach ($new_array_options as $key => $value) {
6418 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6419 $attributeType = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
6420 $attributeLabel = $langs->transnoentities($extrafields->attributes[$this->table_element]['label'][$attributeKey]);
6421 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
6422 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
6423 $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
6424 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$attributeKey];
6425
6426 // If we clone, we have to clean unique extrafields to prevent duplicates.
6427 // This behaviour can be prevented by external code by changing $this->context['createfromclone'] value in createFrom hook
6428 if (!empty($this->context['createfromclone']) && $this->context['createfromclone'] == 'createfromclone' && !empty($attributeUnique)) {
6429 $new_array_options[$key] = null;
6430 }
6431
6432 // Similar code than into insertExtraFields
6433 if ($attributeRequired) {
6434 $v = $this->array_options[$key];
6435 if (ExtraFields::isEmptyValue($v, $attributeType)) {
6436 $langs->load("errors");
6437 dol_syslog("Mandatory field '".$key."' is empty during create and set to required into definition of extrafields");
6438 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6439 return -1;
6440 }
6441 }
6442
6443 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6444 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6445
6446 if (!empty($attrfieldcomputed)) {
6447 if (getDolGlobalString('MAIN_STORE_COMPUTED_EXTRAFIELDS')) {
6448 $value = dol_eval($attrfieldcomputed, 1, 0, '2');
6449 dol_syslog($langs->trans("Extrafieldcomputed")." on ".$attributeLabel."(".$value.")", LOG_DEBUG);
6450 $new_array_options[$key] = $value;
6451 } else {
6452 $new_array_options[$key] = null;
6453 }
6454 }
6455
6456 switch ($attributeType) {
6457 case 'int':
6458 if (!is_numeric($value) && $value != '') {
6459 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6460 return -1;
6461 } elseif ($value == '') {
6462 $new_array_options[$key] = null;
6463 }
6464 break;
6465 case 'price':
6466 case 'double':
6467 $value = price2num($value);
6468 if (!is_numeric($value) && $value != '') {
6469 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." for ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6470 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6471 return -1;
6472 } elseif ($value == '') {
6473 $value = null;
6474 }
6475 //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6476 $new_array_options[$key] = $value;
6477 break;
6478 /*case 'select': // Not required, we chosed value='0' for undefined values
6479 if ($value=='-1')
6480 {
6481 $this->array_options[$key] = null;
6482 }
6483 break;*/
6484 case 'password':
6485 $algo = '';
6486 if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
6487 // If there is an encryption choice, we use it to crypt data before insert
6488 $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
6489 $algo = reset($tmparrays);
6490 if ($algo != '') {
6491 //global $action; // $action may be 'create', 'update', 'update_extras'...
6492 //var_dump($action);
6493 //var_dump($this->oldcopy);exit;
6494 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
6495 //var_dump('iii'.$algo.' '.$this->oldcopy->array_options[$key].' -> '.$this->array_options[$key]);
6496 if (isset($this->oldcopy->array_options[$key]) && $this->array_options[$key] == $this->oldcopy->array_options[$key]) {
6497 // If old value crypted in database is same than submited new value, it means we don't change it, so we don't update.
6498 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
6499 if (!preg_match('/^dolcrypt:/', $this->array_options[$key])) {
6500 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6501 } else {
6502 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6503 }
6504 } else {
6505 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6506 }
6507 } else {
6508 // If value has changed
6509 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
6510 if (!preg_match('/^dolcrypt:/', $this->array_options[$key])) {
6511 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6512 } else {
6513 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6514 }
6515 } else {
6516 $new_array_options[$key] = dol_hash($this->array_options[$key], $algo);
6517 }
6518 }
6519 } else {
6520 //var_dump('jjj'.$algo.' '.$this->oldcopy->array_options[$key].' -> '.$this->array_options[$key]);
6521 // If this->oldcopy is not defined, we can't know if we change attribute or not, so we must keep value
6522 if ($algo == 'dolcrypt' && !preg_match('/^dolcrypt:/', $this->array_options[$key])) { // dolibarr reversible encryption
6523 $new_array_options[$key] = dolEncrypt($this->array_options[$key]); // warning, must be called when on the master
6524 } else {
6525 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6526 }
6527 }
6528 } else {
6529 // No encryption
6530 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6531 }
6532 } else { // Common usage
6533 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6534 }
6535 break;
6536 case 'date':
6537 case 'datetime':
6538 // If data is a string instead of a timestamp, we convert it
6539 if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6540 $this->array_options[$key] = strtotime($this->array_options[$key]);
6541 }
6542 $new_array_options[$key] = $this->db->idate($this->array_options[$key]);
6543 break;
6544 case 'datetimegmt':
6545 // If data is a string instead of a timestamp, we convert it
6546 if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6547 $this->array_options[$key] = strtotime($this->array_options[$key]);
6548 }
6549 $new_array_options[$key] = $this->db->idate($this->array_options[$key], 'gmt');
6550 break;
6551 case 'link':
6552 $param_list = array_keys($attributeParam['options']);
6553 // 0 : ObjectName
6554 // 1 : classPath
6555 $InfoFieldList = explode(":", $param_list[0]);
6556 dol_include_once($InfoFieldList[1]);
6557 if ($InfoFieldList[0] && class_exists($InfoFieldList[0])) {
6558 if ($value == '-1') { // -1 is key for no defined in combo list of objects
6559 $new_array_options[$key] = '';
6560 } elseif ($value) {
6561 $object = new $InfoFieldList[0]($this->db);
6562 if (is_numeric($value)) {
6563 $res = $object->fetch($value); // Common case
6564 } else {
6565 $res = $object->fetch('', $value); // For compatibility
6566 }
6567
6568 if ($res > 0) {
6569 $new_array_options[$key] = $object->id;
6570 } else {
6571 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
6572 return -1;
6573 }
6574 }
6575 } else {
6576 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6577 }
6578 break;
6579 case 'checkbox':
6580 case 'chkbxlst':
6581 if (is_array($this->array_options[$key])) {
6582 $new_array_options[$key] = implode(',', $this->array_options[$key]);
6583 } else {
6584 $new_array_options[$key] = $this->array_options[$key];
6585 }
6586 break;
6587 }
6588 }
6589
6590 $this->db->begin();
6591
6592 $table_element = $this->table_element;
6593 if ($table_element == 'categorie') {
6594 $table_element = 'categories'; // For compatibility
6595 }
6596
6597 dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG);
6598
6599 $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6600 $this->db->query($sql_del);
6601
6602 $sql = "INSERT INTO ".$this->db->prefix().$table_element."_extrafields (fk_object";
6603 foreach ($new_array_options as $key => $value) {
6604 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6605 // Add field of attribut
6606 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator
6607 $sql .= ",".$attributeKey;
6608 }
6609 }
6610 // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6611 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6612 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6613 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6614 $sql .= ",".$tmpkey;
6615 }
6616 }
6617 }
6618 $sql .= ") VALUES (".$this->id;
6619
6620 foreach ($new_array_options as $key => $value) {
6621 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6622 // Add field of attribute
6623 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator)
6624 if ($new_array_options[$key] != '' || $new_array_options[$key] == '0') {
6625 $sql .= ",'".$this->db->escape($new_array_options[$key])."'";
6626 } else {
6627 $sql .= ",null";
6628 }
6629 }
6630 }
6631 // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6632 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6633 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6634 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6635 if (in_array($tmpval, array('int', 'double', 'price'))) {
6636 $sql .= ", 0";
6637 } else {
6638 $sql .= ", ''";
6639 }
6640 }
6641 }
6642 }
6643
6644 $sql .= ")";
6645
6646 $resql = $this->db->query($sql);
6647 if (!$resql) {
6648 $this->error = $this->db->lasterror();
6649 $error++;
6650 }
6651
6652 if (!$error && $trigger) {
6653 // Call trigger
6654 $this->context = array('extrafieldaddupdate'=>1);
6655 $result = $this->call_trigger($trigger, $userused);
6656 if ($result < 0) {
6657 $error++;
6658 }
6659 // End call trigger
6660 }
6661
6662 if ($error) {
6663 $this->db->rollback();
6664 return -1;
6665 } else {
6666 $this->db->commit();
6667 return 1;
6668 }
6669 } else {
6670 return 0;
6671 }
6672 }
6673
6684 public function insertExtraLanguages($trigger = '', $userused = null)
6685 {
6686 global $conf, $langs, $user;
6687
6688 if (empty($userused)) {
6689 $userused = $user;
6690 }
6691
6692 $error = 0;
6693
6694 if (getDolGlobalString('MAIN_EXTRALANGUAGES_DISABLED')) {
6695 return 0; // For avoid conflicts if trigger used
6696 }
6697
6698 if (is_array($this->array_languages)) {
6699 $new_array_languages = $this->array_languages;
6700
6701 foreach ($new_array_languages as $key => $value) {
6702 $attributeKey = $key;
6703 $attributeType = $this->fields[$attributeKey]['type'];
6704 $attributeLabel = $this->fields[$attributeKey]['label'];
6705
6706 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6707 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6708
6709 switch ($attributeType) {
6710 case 'int':
6711 if (!is_numeric($value) && $value != '') {
6712 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6713 return -1;
6714 } elseif ($value == '') {
6715 $new_array_languages[$key] = null;
6716 }
6717 break;
6718 case 'double':
6719 $value = price2num($value);
6720 if (!is_numeric($value) && $value != '') {
6721 dol_syslog($langs->trans("ExtraLanguageHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6722 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6723 return -1;
6724 } elseif ($value == '') {
6725 $new_array_languages[$key] = null;
6726 } else {
6727 $new_array_languages[$key] = $value;
6728 }
6729 break;
6730 /*case 'select': // Not required, we chosed value='0' for undefined values
6731 if ($value=='-1')
6732 {
6733 $this->array_options[$key] = null;
6734 }
6735 break;*/
6736 }
6737 }
6738
6739 $this->db->begin();
6740
6741 $table_element = $this->table_element;
6742 if ($table_element == 'categorie') { // TODO Rename table llx_categories_extrafields into llx_categorie_extrafields so we can remove this.
6743 $table_element = 'categories'; // For compatibility
6744 }
6745
6746 dol_syslog(get_class($this)."::insertExtraLanguages delete then insert", LOG_DEBUG);
6747
6748 foreach ($new_array_languages as $key => $langcodearray) { // $key = 'name', 'town', ...
6749 foreach ($langcodearray as $langcode => $value) {
6750 $sql_del = "DELETE FROM ".$this->db->prefix()."object_lang";
6751 $sql_del .= " WHERE fk_object = ".((int) $this->id)." AND property = '".$this->db->escape($key)."' AND type_object = '".$this->db->escape($table_element)."'";
6752 $sql_del .= " AND lang = '".$this->db->escape($langcode)."'";
6753 $this->db->query($sql_del);
6754
6755 if ($value !== '') {
6756 $sql = "INSERT INTO ".$this->db->prefix()."object_lang (fk_object, property, type_object, lang, value";
6757 $sql .= ") VALUES (".$this->id.", '".$this->db->escape($key)."', '".$this->db->escape($table_element)."', '".$this->db->escape($langcode)."', '".$this->db->escape($value)."'";
6758 $sql .= ")";
6759
6760 $resql = $this->db->query($sql);
6761 if (!$resql) {
6762 $this->error = $this->db->lasterror();
6763 $error++;
6764 break;
6765 }
6766 }
6767 }
6768 }
6769
6770 if (!$error && $trigger) {
6771 // Call trigger
6772 $this->context = array('extralanguagesaddupdate'=>1);
6773 $result = $this->call_trigger($trigger, $userused);
6774 if ($result < 0) {
6775 $error++;
6776 }
6777 // End call trigger
6778 }
6779
6780 if ($error) {
6781 $this->db->rollback();
6782 return -1;
6783 } else {
6784 $this->db->commit();
6785 return 1;
6786 }
6787 } else {
6788 return 0;
6789 }
6790 }
6791
6802 public function updateExtraField($key, $trigger = null, $userused = null)
6803 {
6804 global $conf, $langs, $user, $hookmanager;
6805
6806 if (getDolGlobalString('MAIN_EXTRAFIELDS_DISABLED')) {
6807 return 0;
6808 }
6809
6810 if (empty($userused)) {
6811 $userused = $user;
6812 }
6813
6814 $error = 0;
6815
6816 if (!empty($this->array_options) && isset($this->array_options["options_".$key])) {
6817 // Check parameters
6818 $langs->load('admin');
6819 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6820 $extrafields = new ExtraFields($this->db);
6821 $extrafields->fetch_name_optionals_label($this->table_element);
6822
6823 $value = $this->array_options["options_".$key];
6824
6825 $attributeKey = $key;
6826 $attributeType = $extrafields->attributes[$this->table_element]['type'][$key];
6827 $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key];
6828 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key];
6829 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
6830 $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
6831 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$key];
6832
6833 // Similar code than into insertExtraFields
6834 if ($attributeRequired) {
6835 $mandatorypb = false;
6836 if ($attributeType == 'link' && $this->array_options["options_".$key] == '-1') {
6837 $mandatorypb = true;
6838 }
6839 if ($this->array_options["options_".$key] === '') {
6840 $mandatorypb = true;
6841 }
6842 if ($mandatorypb) {
6843 $langs->load("errors");
6844 dol_syslog("Mandatory field 'options_".$key."' is empty during update and set to required into definition of extrafields");
6845 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6846 return -1;
6847 }
6848 }
6849
6850 // $new_array_options will be used for direct update, so must contains formated data for the UPDATE.
6851 $new_array_options = $this->array_options;
6852
6853 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6854 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6855 if (!empty($attrfieldcomputed)) {
6856 if (getDolGlobalString('MAIN_STORE_COMPUTED_EXTRAFIELDS')) {
6857 $value = dol_eval($attrfieldcomputed, 1, 0, '2');
6858 dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
6859
6860 $new_array_options["options_".$key] = $value;
6861
6862 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6863 } else {
6864 $new_array_options["options_".$key] = null;
6865
6866 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6867 }
6868 }
6869
6870 switch ($attributeType) {
6871 case 'int':
6872 if (!is_numeric($value) && $value != '') {
6873 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6874 return -1;
6875 } elseif ($value === '') {
6876 $new_array_options["options_".$key] = null;
6877
6878 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6879 }
6880 break;
6881 case 'price':
6882 case 'double':
6883 $value = price2num($value);
6884 if (!is_numeric($value) && $value != '') {
6885 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6886 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6887 return -1;
6888 } elseif ($value === '') {
6889 $value = null;
6890 }
6891 //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6892 $new_array_options["options_".$key] = $value;
6893
6894 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6895 break;
6896 /*case 'select': // Not required, we chosed value='0' for undefined values
6897 if ($value=='-1')
6898 {
6899 $new_array_options["options_".$key] = $value;
6900
6901 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6902 }
6903 break;*/
6904 case 'password':
6905 $algo = '';
6906 if ($this->array_options["options_".$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
6907 // If there is an encryption choice, we use it to crypt data before insert
6908 $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
6909 $algo = reset($tmparrays);
6910 if ($algo != '') {
6911 //global $action; // $action may be 'create', 'update', 'update_extras'...
6912 //var_dump($action);
6913 //var_dump($this->oldcopy);exit;
6914 //var_dump($key.' '.$this->array_options["options_".$key].' '.$algo);
6915 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
6916 //var_dump($this->oldcopy->array_options["options_".$key]); var_dump($this->array_options["options_".$key]);
6917 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.
6918 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
6919 if (!preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) {
6920 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]); // warning, must be called when on the master
6921 } else {
6922 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
6923 }
6924 } else {
6925 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
6926 }
6927 } else {
6928 if ($algo == 'dolcrypt') { // dolibarr reversible encryption
6929 if (!preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) {
6930 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]);
6931 } else {
6932 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
6933 }
6934 } else {
6935 $new_array_options["options_".$key] = dol_hash($this->array_options["options_".$key], $algo);
6936 }
6937 }
6938 } else {
6939 if ($algo == 'dolcrypt' && !preg_match('/^dolcrypt:/', $this->array_options["options_".$key])) { // dolibarr reversible encryption
6940 $new_array_options["options_".$key] = dolEncrypt($this->array_options["options_".$key]); // warning, must be called when on the master
6941 } else {
6942 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
6943 }
6944 }
6945 } else {
6946 // No encryption
6947 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
6948 }
6949 } else { // Common usage
6950 $new_array_options["options_".$key] = $this->array_options["options_".$key]; // Value is kept
6951 }
6952
6953 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6954 break;
6955 case 'date':
6956 case 'datetime':
6957 if (empty($this->array_options["options_".$key])) {
6958 $new_array_options["options_".$key] = null;
6959
6960 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6961 } else {
6962 $new_array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key]);
6963 }
6964 break;
6965 case 'datetimegmt':
6966 if (empty($this->array_options["options_".$key])) {
6967 $new_array_options["options_".$key] = null;
6968
6969 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6970 } else {
6971 $new_array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key], 'gmt');
6972 }
6973 break;
6974 case 'boolean':
6975 if (empty($this->array_options["options_".$key])) {
6976 $new_array_options["options_".$key] = null;
6977
6978 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6979 }
6980 break;
6981 case 'link':
6982 if ($this->array_options["options_".$key] === '') {
6983 $new_array_options["options_".$key] = null;
6984
6985 $this->array_options["options_".$key] = $new_array_options["options_".$key];
6986 }
6987 break;
6988 /*
6989 case 'link':
6990 $param_list = array_keys($attributeParam['options']);
6991 // 0 : ObjectName
6992 // 1 : classPath
6993 $InfoFieldList = explode(":", $param_list[0]);
6994 dol_include_once($InfoFieldList[1]);
6995 if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
6996 {
6997 if ($value == '-1') // -1 is key for no defined in combo list of objects
6998 {
6999 $new_array_options[$key] = '';
7000 } elseif ($value) {
7001 $object = new $InfoFieldList[0]($this->db);
7002 if (is_numeric($value)) $res = $object->fetch($value); // Common case
7003 else $res = $object->fetch('', $value); // For compatibility
7004
7005 if ($res > 0) $new_array_options[$key] = $object->id;
7006 else {
7007 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
7008 $this->db->rollback();
7009 return -1;
7010 }
7011 }
7012 } else {
7013 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
7014 }
7015 break;
7016 */
7017 case 'checkbox':
7018 case 'chkbxlst':
7019 $new_array_options = array();
7020 if (is_array($this->array_options["options_".$key])) {
7021 $new_array_options["options_".$key] = implode(',', $this->array_options["options_".$key]);
7022 } else {
7023 $new_array_options["options_".$key] = $this->array_options["options_".$key];
7024 }
7025
7026 $this->array_options["options_".$key] = $new_array_options["options_".$key];
7027 break;
7028 }
7029
7030 $this->db->begin();
7031
7032 $linealreadyfound = 0;
7033
7034 // 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)
7035 $table_element = $this->table_element;
7036 if ($table_element == 'categorie') { // TODO Rename table llx_categories_extrafields into llx_categorie_extrafields so we can remove this.
7037 $table_element = 'categories'; // For compatibility
7038 }
7039
7040 $sql = "SELECT COUNT(rowid) as nb FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
7041 $resql = $this->db->query($sql);
7042 if ($resql) {
7043 $tmpobj = $this->db->fetch_object($resql);
7044 if ($tmpobj) {
7045 $linealreadyfound = $tmpobj->nb;
7046 }
7047 }
7048
7049 //var_dump('linealreadyfound='.$linealreadyfound.' sql='.$sql); exit;
7050 if ($linealreadyfound) {
7051 if ($this->array_options["options_".$key] === null) {
7052 $sql = "UPDATE ".$this->db->prefix().$table_element."_extrafields SET ".$key." = null";
7053 } else {
7054 $sql = "UPDATE ".$this->db->prefix().$table_element."_extrafields SET ".$key." = '".$this->db->escape($new_array_options["options_".$key])."'";
7055 }
7056 $sql .= " WHERE fk_object = ".((int) $this->id);
7057
7058 $resql = $this->db->query($sql);
7059 if (!$resql) {
7060 $error++;
7061 $this->error = $this->db->lasterror();
7062 }
7063 } else {
7064 $result = $this->insertExtraFields('', $user);
7065 if ($result < 0) {
7066 $error++;
7067 }
7068 }
7069
7070 if (!$error) {
7071 $parameters = array('key'=>$key);
7072 $reshook = $hookmanager->executeHooks('updateExtraFieldBeforeCommit', $parameters, $this, $action);
7073 if ($reshook < 0) {
7074 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
7075 }
7076 }
7077
7078 if (!$error && $trigger) {
7079 // Call trigger
7080 $this->context = array('extrafieldupdate'=>1);
7081 $result = $this->call_trigger($trigger, $userused);
7082 if ($result < 0) {
7083 $error++;
7084 }
7085 // End call trigger
7086 }
7087
7088 if ($error) {
7089 dol_syslog(__METHOD__.$this->error, LOG_ERR);
7090 $this->db->rollback();
7091 return -1;
7092 } else {
7093 $this->db->commit();
7094 return 1;
7095 }
7096 } else {
7097 return 0;
7098 }
7099 }
7100
7111 public function updateExtraLanguages($key, $trigger = null, $userused = null)
7112 {
7113 global $conf, $langs, $user;
7114
7115 if (empty($userused)) {
7116 $userused = $user;
7117 }
7118
7119 $error = 0;
7120
7121 if (getDolGlobalString('MAIN_EXTRALANGUAGES_DISABLED')) {
7122 return 0; // For avoid conflicts if trigger used
7123 }
7124
7125 return 0;
7126 }
7127
7128
7143 public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0, $nonewbutton = 0)
7144 {
7145 global $conf, $langs, $form;
7146
7147 if (!is_object($form)) {
7148 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
7149 $form = new Form($this->db);
7150 }
7151
7152 if (!empty($this->fields)) {
7153 $val = $this->fields[$key];
7154 }
7155
7156 // Validation tests and output
7157 $fieldValidationErrorMsg = '';
7158 $validationClass = '';
7159 $fieldValidationErrorMsg = $this->getFieldError($key);
7160 if (!empty($fieldValidationErrorMsg)) {
7161 $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
7162 } else {
7163 $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
7164 }
7165
7166 $out = '';
7167 $type = '';
7168 $isDependList = 0;
7169 $param = array();
7170 $param['options'] = array();
7171 $reg = array();
7172 $size = !empty($this->fields[$key]['size']) ? $this->fields[$key]['size'] : 0;
7173 // Because we work on extrafields
7174 if (preg_match('/^(integer|link):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7175 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
7176 $type = 'link';
7177 } elseif (preg_match('/^(integer|link):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7178 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7179 $type = 'link';
7180 } elseif (preg_match('/^(integer|link):(.*):(.*)/i', $val['type'], $reg)) {
7181 $param['options'] = array($reg[2].':'.$reg[3] => 'N');
7182 $type = 'link';
7183 } elseif (preg_match('/^(sellist):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7184 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
7185 $type = 'sellist';
7186 } elseif (preg_match('/^(sellist):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7187 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7188 $type = 'sellist';
7189 } elseif (preg_match('/^(sellist):(.*):(.*)/i', $val['type'], $reg)) {
7190 $param['options'] = array($reg[2].':'.$reg[3] => 'N');
7191 $type = 'sellist';
7192 } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
7193 $param['options'] = array($reg[1] => 'N');
7194 $type = 'chkbxlst';
7195 } elseif (preg_match('/varchar\‍((\d+)\‍)/', $val['type'], $reg)) {
7196 $param['options'] = array();
7197 $type = 'varchar';
7198 $size = $reg[1];
7199 } elseif (preg_match('/varchar/', $val['type'])) {
7200 $param['options'] = array();
7201 $type = 'varchar';
7202 } else {
7203 $param['options'] = array();
7204 $type = $this->fields[$key]['type'];
7205 }
7206 //var_dump($type); var_dump($param['options']);
7207
7208 // Special case that force options and type ($type can be integer, varchar, ...)
7209 if (!empty($this->fields[$key]['arrayofkeyval']) && is_array($this->fields[$key]['arrayofkeyval'])) {
7210 $param['options'] = $this->fields[$key]['arrayofkeyval'];
7211 $type = (($this->fields[$key]['type']=='checkbox') ? $this->fields[$key]['type'] : 'select');
7212 }
7213
7214 $label = $this->fields[$key]['label'];
7215 //$elementtype=$this->fields[$key]['elementtype']; // Seems not used
7216 $default = (!empty($this->fields[$key]['default']) ? $this->fields[$key]['default'] : '');
7217 $computed = (!empty($this->fields[$key]['computed']) ? $this->fields[$key]['computed'] : '');
7218 $unique = (!empty($this->fields[$key]['unique']) ? $this->fields[$key]['unique'] : 0);
7219 $required = (!empty($this->fields[$key]['required']) ? $this->fields[$key]['required'] : 0);
7220 $autofocusoncreate = (!empty($this->fields[$key]['autofocusoncreate']) ? $this->fields[$key]['autofocusoncreate'] : 0);
7221
7222 $langfile = (!empty($this->fields[$key]['langfile']) ? $this->fields[$key]['langfile'] : '');
7223 $list = (!empty($this->fields[$key]['list']) ? $this->fields[$key]['list'] : 0);
7224 $hidden = (in_array(abs($this->fields[$key]['visible']), array(0, 2)) ? 1 : 0);
7225
7226 $objectid = $this->id;
7227
7228 if ($computed) {
7229 if (!preg_match('/^search_/', $keyprefix)) {
7230 return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
7231 } else {
7232 return '';
7233 }
7234 }
7235
7236 // Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine
7237 if (empty($morecss) && !empty($val['css'])) {
7238 $morecss = $val['css'];
7239 } elseif (empty($morecss)) {
7240 if ($type == 'date') {
7241 $morecss = 'minwidth100imp';
7242 } elseif ($type == 'datetime' || $type == 'link') { // link means an foreign key to another primary id
7243 $morecss = 'minwidth200imp';
7244 } elseif (in_array($type, array('int', 'integer', 'price')) || preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', $type)) {
7245 $morecss = 'maxwidth75';
7246 } elseif ($type == 'url') {
7247 $morecss = 'minwidth400';
7248 } elseif ($type == 'boolean') {
7249 $morecss = '';
7250 } else {
7251 if (round($size) < 12) {
7252 $morecss = 'minwidth100';
7253 } elseif (round($size) <= 48) {
7254 $morecss = 'minwidth200';
7255 } else {
7256 $morecss = 'minwidth400';
7257 }
7258 }
7259 }
7260
7261 // Add validation state class
7262 if (!empty($validationClass)) {
7263 $morecss.= $validationClass;
7264 }
7265
7266 if (in_array($type, array('date'))) {
7267 $tmp = explode(',', $size);
7268 $newsize = $tmp[0];
7269 $showtime = 0;
7270
7271 // Do not show current date when field not required (see selectDate() method)
7272 if (!$required && $value == '') {
7273 $value = '-1';
7274 }
7275
7276 // TODO Must also support $moreparam
7277 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
7278 } elseif (in_array($type, array('datetime'))) {
7279 $tmp = explode(',', $size);
7280 $newsize = $tmp[0];
7281 $showtime = 1;
7282
7283 // Do not show current date when field not required (see selectDate() method)
7284 if (!$required && $value == '') {
7285 $value = '-1';
7286 }
7287
7288 // TODO Must also support $moreparam
7289 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
7290 } elseif (in_array($type, array('duration'))) {
7291 $out = $form->select_duration($keyprefix.$key.$keysuffix, $value, 0, 'text', 0, 1);
7292 } elseif (in_array($type, array('int', 'integer'))) {
7293 $tmp = explode(',', $size);
7294 $newsize = $tmp[0];
7295 $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' : '').'>';
7296 } elseif (in_array($type, array('real'))) {
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('/varchar/', $type)) {
7299 $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' : '').'>';
7300 } elseif (in_array($type, array('email', 'mail', 'phone', 'url', 'ip'))) {
7301 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7302 } elseif (preg_match('/^text/', $type)) {
7303 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7304 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7305 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
7306 $out = $doleditor->Create(1);
7307 } else {
7308 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7309 }
7310 } elseif (preg_match('/^html/', $type)) {
7311 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7312 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7313 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_5, '90%');
7314 $out = $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
7315 } else {
7316 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7317 }
7318 } elseif ($type == 'boolean') {
7319 $checked = '';
7320 if (!empty($value)) {
7321 $checked = ' checked value="1" ';
7322 } else {
7323 $checked = ' value="1" ';
7324 }
7325 $out = '<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
7326 } elseif ($type == 'price') {
7327 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7328 $value = price($value);
7329 }
7330 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '.$langs->getCurrencySymbol($conf->currency);
7331 } elseif (preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', $type)) {
7332 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7333 $value = price($value);
7334 }
7335 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
7336 } elseif ($type == 'select') { // combo list
7337 $out = '';
7338 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
7339 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7340 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7341 }
7342
7343 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7344 if ((!isset($this->fields[$key]['default'])) || ($this->fields[$key]['notnull'] != 1)) {
7345 $out .= '<option value="0">&nbsp;</option>';
7346 }
7347 foreach ($param['options'] as $keyb => $valb) {
7348 if ((string) $keyb == '') {
7349 continue;
7350 }
7351 if (strpos($valb, "|") !== false) {
7352 list($valb, $parent) = explode('|', $valb);
7353 }
7354 $out .= '<option value="'.$keyb.'"';
7355 $out .= (((string) $value == (string) $keyb) ? ' selected' : '');
7356 if (!empty($parent)) {
7357 $isDependList = 1;
7358 }
7359 $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
7360 $out .= '>'.$langs->trans($valb).'</option>';
7361 }
7362 $out .= '</select>';
7363 } elseif ($type == 'sellist') {
7364 $out = '';
7365 if (!empty($conf->use_javascript_ajax) && !getDolGlobalString('MAIN_EXTRAFIELDS_DISABLE_SELECT2')) {
7366 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7367 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7368 }
7369
7370 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7371 if (is_array($param['options'])) {
7372 $param_list = array_keys($param['options']);
7373 $InfoFieldList = explode(":", $param_list[0], 5);
7374 if (! empty($InfoFieldList[4])) {
7375 $pos = 0;
7376 $parenthesisopen = 0;
7377 while (substr($InfoFieldList[4], $pos, 1) !== '' && ($parenthesisopen || $pos == 0 || substr($InfoFieldList[4], $pos, 1) != ':')) {
7378 if (substr($InfoFieldList[4], $pos, 1) == '(') {
7379 $parenthesisopen++;
7380 }
7381 if (substr($InfoFieldList[4], $pos, 1) == ')') {
7382 $parenthesisopen--;
7383 }
7384 $pos++;
7385 }
7386 $tmpbefore = substr($InfoFieldList[4], 0, $pos);
7387 $tmpafter = substr($InfoFieldList[4], $pos+1);
7388 //var_dump($InfoFieldList[4].' -> '.$pos); var_dump($tmpafter);
7389 $InfoFieldList[4] = $tmpbefore;
7390 if ($tmpafter !== '') {
7391 $InfoFieldList = array_merge($InfoFieldList, explode(':', $tmpafter));
7392 }
7393 //var_dump($InfoFieldList);
7394 }
7395 $parentName = '';
7396 $parentField = '';
7397
7398 // 0 : tableName
7399 // 1 : label field name
7400 // 2 : key fields name (if differ of rowid)
7401 // 3 : key field parent (for dependent lists)
7402 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
7403 // 5 : id category type
7404 // 6 : ids categories list separated by comma for category root
7405 // 7 : sort field
7406 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7407
7408 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7409 if (strpos($InfoFieldList[4], 'extra.') !== false) {
7410 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7411 } else {
7412 $keyList = $InfoFieldList[2].' as rowid';
7413 }
7414 }
7415 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7416 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7417 $keyList .= ', '.$parentField;
7418 }
7419
7420 $filter_categorie = false;
7421 if (count($InfoFieldList) > 5) {
7422 if ($InfoFieldList[0] == 'categorie') {
7423 $filter_categorie = true;
7424 }
7425 }
7426
7427 if ($filter_categorie === false) {
7428 $fields_label = explode('|', $InfoFieldList[1]);
7429 if (is_array($fields_label)) {
7430 $keyList .= ', ';
7431 $keyList .= implode(', ', $fields_label);
7432 }
7433
7434 $sqlwhere = '';
7435 $sql = "SELECT " . $keyList;
7436 $sql .= " FROM " . $this->db->prefix() . $InfoFieldList[0];
7437 if (!empty($InfoFieldList[4])) {
7438 // can use SELECT request
7439 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7440 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7441 }
7442
7443 // current object id can be use into filter
7444 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7445 $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
7446 } else {
7447 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7448 }
7449
7450 // We have to join on extrafield table
7451 $errstr = '';
7452 if (strpos($InfoFieldList[4], 'extra') !== false) {
7453 $sql .= " as main, " . $this->db->prefix() . $InfoFieldList[0] . "_extrafields as extra";
7454 $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2];
7455 $sqlwhere .= " AND " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
7456 } else {
7457 $sqlwhere .= " WHERE " . forgeSQLFromUniversalSearchCriteria($InfoFieldList[4], $errstr, 1);
7458 }
7459 } else {
7460 $sqlwhere .= ' WHERE 1=1';
7461 }
7462 // Some tables may have field, some other not. For the moment we disable it.
7463 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7464 $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7465 }
7466 $sql .= $sqlwhere;
7467 //print $sql;
7468
7469 // Note: $InfoFieldList can be 'sellist:TableName:LabelFieldName[:KeyFieldName[:KeyFieldParent[:Filter[:CategoryIdType[:CategoryIdList[:Sortfield]]]]]]'
7470 if (isset($InfoFieldList[7]) && preg_match('/^[a-z0-9_\-,]+$/i', $InfoFieldList[7])) {
7471 $sql .= " ORDER BY ".$this->db->escape($InfoFieldList[7]);
7472 } else {
7473 $sql .= " ORDER BY ".$this->db->sanitize(implode(', ', $fields_label));
7474 }
7475
7476 dol_syslog(get_class($this) . '::showInputField type=sellist', LOG_DEBUG);
7477 $resql = $this->db->query($sql);
7478 if ($resql) {
7479 $out .= '<option value="0">&nbsp;</option>';
7480 $num = $this->db->num_rows($resql);
7481 $i = 0;
7482 while ($i < $num) {
7483 $labeltoshow = '';
7484 $obj = $this->db->fetch_object($resql);
7485
7486 // Several field into label (eq table:code|libelle:rowid)
7487 $notrans = false;
7488 $fields_label = explode('|', $InfoFieldList[1]);
7489 if (count($fields_label) > 1) {
7490 $notrans = true;
7491 foreach ($fields_label as $field_toshow) {
7492 $labeltoshow .= $obj->$field_toshow . ' ';
7493 }
7494 } else {
7495 $labeltoshow = $obj->{$InfoFieldList[1]};
7496 }
7497 $labeltoshow = dol_trunc($labeltoshow, 45);
7498
7499 if ($value == $obj->rowid) {
7500 foreach ($fields_label as $field_toshow) {
7501 $translabel = $langs->trans($obj->$field_toshow);
7502 if ($translabel != $obj->$field_toshow) {
7503 $labeltoshow = dol_trunc($translabel) . ' ';
7504 } else {
7505 $labeltoshow = dol_trunc($obj->$field_toshow) . ' ';
7506 }
7507 }
7508 $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7509 } else {
7510 if (!$notrans) {
7511 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7512 if ($translabel != $obj->{$InfoFieldList[1]}) {
7513 $labeltoshow = dol_trunc($translabel, 18);
7514 } else {
7515 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]});
7516 }
7517 }
7518 if (empty($labeltoshow)) {
7519 $labeltoshow = '(not defined)';
7520 }
7521 if ($value == $obj->rowid) {
7522 $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7523 }
7524
7525 if (!empty($InfoFieldList[3]) && $parentField) {
7526 $parent = $parentName . ':' . $obj->{$parentField};
7527 $isDependList = 1;
7528 }
7529
7530 $out .= '<option value="' . $obj->rowid . '"';
7531 $out .= ($value == $obj->rowid ? ' selected' : '');
7532 $out .= (!empty($parent) ? ' parent="' . $parent . '"' : '');
7533 $out .= '>' . $labeltoshow . '</option>';
7534 }
7535
7536 $i++;
7537 }
7538 $this->db->free($resql);
7539 } else {
7540 print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
7541 }
7542 } else {
7543 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7544 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
7545 $out .= '<option value="0">&nbsp;</option>';
7546 foreach ($data as $data_key => $data_value) {
7547 $out .= '<option value="' . $data_key . '"';
7548 $out .= ($value == $data_key ? ' selected' : '');
7549 $out .= '>' . $data_value . '</option>';
7550 }
7551 }
7552 }
7553 $out .= '</select>';
7554 } elseif ($type == 'checkbox') {
7555 $value_arr = explode(',', $value);
7556 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ? null : $param['options']), $value_arr, '', 0, $morecss, 0, '100%');
7557 } elseif ($type == 'radio') {
7558 $out = '';
7559 foreach ($param['options'] as $keyopt => $valopt) {
7560 $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
7561 $out .= ' value="'.$keyopt.'"';
7562 $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
7563 $out .= ($value == $keyopt ? 'checked' : '');
7564 $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$valopt.'</label><br>';
7565 }
7566 } elseif ($type == 'chkbxlst') {
7567 if (is_array($value)) {
7568 $value_arr = $value;
7569 } else {
7570 $value_arr = explode(',', $value);
7571 }
7572
7573 if (is_array($param['options'])) {
7574 $param_list = array_keys($param['options']);
7575 $InfoFieldList = explode(":", $param_list[0]);
7576 $parentName = '';
7577 $parentField = '';
7578 // 0 : tableName
7579 // 1 : label field name
7580 // 2 : key fields name (if differ of rowid)
7581 // 3 : key field parent (for dependent lists)
7582 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
7583 // 5 : id category type
7584 // 6 : ids categories list separated by comma for category root
7585 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7586
7587 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7588 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7589 $keyList .= ', '.$parentField;
7590 }
7591 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7592 if (strpos($InfoFieldList[4], 'extra.') !== false) {
7593 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7594 } else {
7595 $keyList = $InfoFieldList[2].' as rowid';
7596 }
7597 }
7598
7599 $filter_categorie = false;
7600 if (count($InfoFieldList) > 5) {
7601 if ($InfoFieldList[0] == 'categorie') {
7602 $filter_categorie = true;
7603 }
7604 }
7605
7606 if ($filter_categorie === false) {
7607 $fields_label = explode('|', $InfoFieldList[1]);
7608 if (is_array($fields_label)) {
7609 $keyList .= ', ';
7610 $keyList .= implode(', ', $fields_label);
7611 }
7612
7613 $sqlwhere = '';
7614 $sql = "SELECT " . $keyList;
7615 $sql .= ' FROM ' . $this->db->prefix() . $InfoFieldList[0];
7616 if (!empty($InfoFieldList[4])) {
7617 // can use SELECT request
7618 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7619 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7620 }
7621
7622 // current object id can be use into filter
7623 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7624 $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
7625 } else {
7626 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7627 }
7628
7629 // We have to join on extrafield table
7630 if (strpos($InfoFieldList[4], 'extra') !== false) {
7631 $sql .= ' as main, ' . $this->db->prefix() . $InfoFieldList[0] . '_extrafields as extra';
7632 $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2] . " AND " . $InfoFieldList[4];
7633 } else {
7634 $sqlwhere .= " WHERE " . $InfoFieldList[4];
7635 }
7636 } else {
7637 $sqlwhere .= ' WHERE 1=1';
7638 }
7639 // Some tables may have field, some other not. For the moment we disable it.
7640 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7641 $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7642 }
7643 // $sql.=preg_replace('/^ AND /','',$sqlwhere);
7644 // print $sql;
7645
7646 $sql .= $sqlwhere;
7647 dol_syslog(get_class($this) . '::showInputField type=chkbxlst', LOG_DEBUG);
7648 $resql = $this->db->query($sql);
7649 if ($resql) {
7650 $num = $this->db->num_rows($resql);
7651 $i = 0;
7652
7653 $data = array();
7654
7655 while ($i < $num) {
7656 $labeltoshow = '';
7657 $obj = $this->db->fetch_object($resql);
7658
7659 $notrans = false;
7660 // Several field into label (eq table:code|libelle:rowid)
7661 $fields_label = explode('|', $InfoFieldList[1]);
7662 if (count($fields_label) > 1) {
7663 $notrans = true;
7664 foreach ($fields_label as $field_toshow) {
7665 $labeltoshow .= $obj->$field_toshow . ' ';
7666 }
7667 } else {
7668 $labeltoshow = $obj->{$InfoFieldList[1]};
7669 }
7670 $labeltoshow = dol_trunc($labeltoshow, 45);
7671
7672 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7673 foreach ($fields_label as $field_toshow) {
7674 $translabel = $langs->trans($obj->$field_toshow);
7675 if ($translabel != $obj->$field_toshow) {
7676 $labeltoshow = dol_trunc($translabel, 18) . ' ';
7677 } else {
7678 $labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
7679 }
7680 }
7681
7682 $data[$obj->rowid] = $labeltoshow;
7683 } else {
7684 if (!$notrans) {
7685 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7686 if ($translabel != $obj->{$InfoFieldList[1]}) {
7687 $labeltoshow = dol_trunc($translabel, 18);
7688 } else {
7689 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
7690 }
7691 }
7692 if (empty($labeltoshow)) {
7693 $labeltoshow = '(not defined)';
7694 }
7695
7696 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7697 $data[$obj->rowid] = $labeltoshow;
7698 }
7699
7700 if (!empty($InfoFieldList[3]) && $parentField) {
7701 $parent = $parentName . ':' . $obj->{$parentField};
7702 $isDependList = 1;
7703 }
7704
7705 $data[$obj->rowid] = $labeltoshow;
7706 }
7707
7708 $i++;
7709 }
7710 $this->db->free($resql);
7711
7712 $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, '', 0, $morecss, 0, '100%');
7713 } else {
7714 print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
7715 }
7716 } else {
7717 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7718 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
7719 $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, '', 0, $morecss, 0, '100%');
7720 }
7721 }
7722 } elseif ($type == 'link') {
7723 // $param_list='ObjectName:classPath[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
7724 // Filter can contains some ':' inside.
7725 $param_list = array_keys($param['options']);
7726 $param_list_array = explode(':', $param_list[0], 4);
7727
7728 $showempty = (($required && $default != '') ? 0 : 1);
7729
7730 if (!preg_match('/search_/', $keyprefix)) {
7731 if (!empty($param_list_array[2])) { // If the entry into $fields is set to add a create button
7732 if (!empty($this->fields[$key]['picto'])) {
7733 $morecss .= ' widthcentpercentminusxx';
7734 } else {
7735 $morecss .= ' widthcentpercentminusx';
7736 }
7737 } else {
7738 if (!empty($this->fields[$key]['picto'])) {
7739 $morecss .= ' widthcentpercentminusx';
7740 }
7741 }
7742 }
7743 $objectfield = $this->element.($this->module ? '@'.$this->module : '').':'.$key.$keysuffix;
7744 $out = $form->selectForForms($param_list_array[0], $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, $moreparam, 0, (empty($val['disabled']) ? 0 : 1), '', $objectfield);
7745
7746 if (!empty($param_list_array[2])) { // If the entry into $fields is set, we must add a create button
7747 if ((!GETPOSTISSET('backtopage') || strpos(GETPOST('backtopage'), $_SERVER['PHP_SELF']) === 0) // // To avoid to open several times the 'Plus' button (we accept only one level)
7748 && empty($val['disabled']) && empty($nonewbutton)) { // and to avoid to show the button if the field is protected by a "disabled".
7749 list($class, $classfile) = explode(':', $param_list[0]);
7750 if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) {
7751 $url_path = dol_buildpath(dirname(dirname($classfile)).'/card.php', 1);
7752 } else {
7753 $url_path = dol_buildpath(dirname(dirname($classfile)).'/'.strtolower($class).'_card.php', 1);
7754 }
7755 $paramforthenewlink = '';
7756 $paramforthenewlink .= (GETPOSTISSET('action') ? '&action='.GETPOST('action', 'aZ09') : '');
7757 $paramforthenewlink .= (GETPOSTISSET('id') ? '&id='.GETPOST('id', 'int') : '');
7758 $paramforthenewlink .= (GETPOSTISSET('origin') ? '&origin='.GETPOST('origin', 'aZ09') : '');
7759 $paramforthenewlink .= (GETPOSTISSET('originid') ? '&originid='.GETPOST('originid', 'int') : '');
7760 $paramforthenewlink .= '&fk_'.strtolower($class).'=--IDFORBACKTOPAGE--';
7761 // TODO Add Javascript code to add input fields already filled into $paramforthenewlink so we won't loose them when going back to main page
7762 $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>';
7763 }
7764 }
7765 } elseif ($type == 'password') {
7766 // If prefix is 'search_', field is used as a filter, we use a common text field.
7767 if ($keyprefix.$key.$keysuffix == 'pass_crypted') {
7768 $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="pass" id="pass" value="" '.($moreparam ? $moreparam : '').'>';
7769 $out .= '<input type="hidden" name="pass_crypted" id="pass_crypted" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
7770 } else {
7771 $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
7772 }
7773 } elseif ($type == 'array') {
7774 $newval = $val;
7775 $newval['type'] = 'varchar(256)';
7776
7777 $out = '';
7778 if (!empty($value)) {
7779 foreach ($value as $option) {
7780 $out .= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
7781 $out .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $morecss).'<br></span>';
7782 }
7783 }
7784 $out .= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
7785
7786 $newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
7787 $newInput .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $morecss).'<br></span>';
7788
7789 if (!empty($conf->use_javascript_ajax)) {
7790 $out .= '
7791 <script nonce="'.getNonce().'">
7792 $(document).ready(function() {
7793 $("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
7794 $("'.dol_escape_js($newInput).'").insertBefore(this);
7795 });
7796
7797 $(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
7798 $(this).parent().remove();
7799 });
7800 });
7801 </script>';
7802 }
7803 }
7804 if (!empty($hidden)) {
7805 $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
7806 }
7807
7808 if ($isDependList == 1) {
7809 $out .= $this->getJSListDependancies('_common');
7810 }
7811 /* Add comments
7812 if ($type == 'date') $out.=' (YYYY-MM-DD)';
7813 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
7814 */
7815
7816 // Display error message for field
7817 if (!empty($fieldValidationErrorMsg) && function_exists('getFieldErrorIcon')) {
7818 $out .= ' '.getFieldErrorIcon($fieldValidationErrorMsg);
7819 }
7820
7821 return $out;
7822 }
7823
7837 public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '')
7838 {
7839 global $conf, $langs, $form;
7840
7841 if (!is_object($form)) {
7842 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
7843 $form = new Form($this->db);
7844 }
7845
7846 $label = empty($val['label']) ? '' : $val['label'];
7847 $type = empty($val['type']) ? '' : $val['type'];
7848 $size = empty($val['css']) ? '' : $val['css'];
7849 $reg = array();
7850
7851 // Convert var to be able to share same code than showOutputField of extrafields
7852 if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
7853 $type = 'varchar'; // convert varchar(xx) int varchar
7854 $size = $reg[1];
7855 } elseif (preg_match('/varchar/', $type)) {
7856 $type = 'varchar'; // convert varchar(xx) int varchar
7857 }
7858 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
7859 $type = (($this->fields[$key]['type']=='checkbox') ? $this->fields[$key]['type'] : 'select');
7860 }
7861 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
7862 $type = 'link';
7863 }
7864
7865 $default = empty($val['default']) ? '' : $val['default'];
7866 $computed = empty($val['computed']) ? '' : $val['computed'];
7867 $unique = empty($val['unique']) ? '' : $val['unique'];
7868 $required = empty($val['required']) ? '' : $val['required'];
7869 $param = array();
7870 $param['options'] = array();
7871
7872 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
7873 $param['options'] = $val['arrayofkeyval'];
7874 }
7875 if (preg_match('/^integer:([^:]*):([^:]*)/i', $val['type'], $reg)) { // ex: integer:User:user/class/user.class.php
7876 $type = 'link';
7877 $stringforoptions = $reg[1].':'.$reg[2];
7878 // Special case: Force addition of getnomurlparam1 to -1 for users
7879 if ($reg[1] == 'User') {
7880 $stringforoptions .= ':#getnomurlparam1=-1';
7881 }
7882 $param['options'] = array($stringforoptions => $stringforoptions);
7883 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7884 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7885 $type = 'sellist';
7886 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
7887 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
7888 $type = 'sellist';
7889 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
7890 $param['options'] = array($reg[1].':'.$reg[2] => 'N');
7891 $type = 'sellist';
7892 } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
7893 $param['options'] = array($reg[1] => 'N');
7894 $type = 'chkbxlst';
7895 }
7896
7897 $langfile = empty($val['langfile']) ? '' : $val['langfile'];
7898 $list = (empty($val['list']) ? '' : $val['list']);
7899 $help = (empty($val['help']) ? '' : $val['help']);
7900 $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)
7901
7902 if ($hidden) {
7903 return '';
7904 }
7905
7906 // If field is a computed field, value must become result of compute
7907 if ($computed) {
7908 // Make the eval of compute string
7909 //var_dump($computed);
7910 $value = dol_eval($computed, 1, 0, '2');
7911 }
7912
7913 if (empty($morecss)) {
7914 if ($type == 'date') {
7915 $morecss = 'minwidth100imp';
7916 } elseif ($type == 'datetime' || $type == 'timestamp') {
7917 $morecss = 'minwidth200imp';
7918 } elseif (in_array($type, array('int', 'double', 'price'))) {
7919 $morecss = 'maxwidth75';
7920 } elseif ($type == 'url') {
7921 $morecss = 'minwidth400';
7922 } elseif ($type == 'boolean') {
7923 $morecss = '';
7924 } else {
7925 if (is_numeric($size) && round($size) < 12) {
7926 $morecss = 'minwidth100';
7927 } elseif (is_numeric($size) && round($size) <= 48) {
7928 $morecss = 'minwidth200';
7929 } else {
7930 $morecss = 'minwidth400';
7931 }
7932 }
7933 }
7934
7935 // Format output value differently according to properties of field
7936 if (in_array($key, array('rowid', 'ref')) && method_exists($this, 'getNomUrl')) {
7937 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.
7938 $value = $this->getNomUrl(1, '', 0, '', 1);
7939 }
7940 } elseif ($key == 'status' && method_exists($this, 'getLibStatut')) {
7941 $value = $this->getLibStatut(3);
7942 } elseif ($type == 'date') {
7943 if (!empty($value)) {
7944 $value = dol_print_date($value, 'day'); // We suppose dates without time are always gmt (storage of course + output)
7945 } else {
7946 $value = '';
7947 }
7948 } elseif ($type == 'datetime' || $type == 'timestamp') {
7949 if (!empty($value)) {
7950 $value = dol_print_date($value, 'dayhour', 'tzuserrel');
7951 } else {
7952 $value = '';
7953 }
7954 } elseif ($type == 'duration') {
7955 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
7956 if (!is_null($value) && $value !== '') {
7957 $value = convertSecondToTime($value, 'allhourmin');
7958 }
7959 } elseif ($type == 'double' || $type == 'real') {
7960 if (!is_null($value) && $value !== '') {
7961 $value = price($value);
7962 }
7963 } elseif ($type == 'boolean') {
7964 $checked = '';
7965 if (!empty($value)) {
7966 $checked = ' checked ';
7967 }
7968 $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
7969 } elseif ($type == 'mail' || $type == 'email') {
7970 $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
7971 } elseif ($type == 'url') {
7972 $value = dol_print_url($value, '_blank', 32, 1);
7973 } elseif ($type == 'phone') {
7974 $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
7975 } elseif ($type == 'ip') {
7976 $value = dol_print_ip($value, 0);
7977 } elseif ($type == 'price') {
7978 if (!is_null($value) && $value !== '') {
7979 $value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
7980 }
7981 } elseif ($type == 'select') {
7982 $value = isset($param['options'][$value]) ? $param['options'][$value] : '';
7983 if (strpos($value, "|") !== false) {
7984 $value = $langs->trans(explode('|', $value)[0]);
7985 }
7986 } elseif ($type == 'sellist') {
7987 $param_list = array_keys($param['options']);
7988 $InfoFieldList = explode(":", $param_list[0]);
7989
7990 $selectkey = "rowid";
7991 $keyList = 'rowid';
7992
7993 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7994 $selectkey = $InfoFieldList[2];
7995 $keyList = $InfoFieldList[2].' as rowid';
7996 }
7997
7998 $fields_label = explode('|', $InfoFieldList[1]);
7999 if (is_array($fields_label)) {
8000 $keyList .= ', ';
8001 $keyList .= implode(', ', $fields_label);
8002 }
8003
8004 $filter_categorie = false;
8005 if (count($InfoFieldList) > 5) {
8006 if ($InfoFieldList[0] == 'categorie') {
8007 $filter_categorie = true;
8008 }
8009 }
8010
8011 $sql = "SELECT ".$keyList;
8012 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
8013 if (strpos($InfoFieldList[4], 'extra') !== false) {
8014 $sql .= ' as main';
8015 }
8016 if ($selectkey == 'rowid' && empty($value)) {
8017 $sql .= " WHERE ".$selectkey." = 0";
8018 } elseif ($selectkey == 'rowid') {
8019 $sql .= " WHERE ".$selectkey." = ".((int) $value);
8020 } else {
8021 $sql .= " WHERE ".$selectkey." = '".$this->db->escape($value)."'";
8022 }
8023
8024 //$sql.= ' AND entity = '.$conf->entity;
8025
8026 dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
8027 $resql = $this->db->query($sql);
8028 if ($resql) {
8029 if ($filter_categorie === false) {
8030 $value = ''; // value was used, so now we reste it to use it to build final output
8031 $numrows = $this->db->num_rows($resql);
8032 if ($numrows) {
8033 $obj = $this->db->fetch_object($resql);
8034
8035 // Several field into label (eq table:code|libelle:rowid)
8036 $fields_label = explode('|', $InfoFieldList[1]);
8037
8038 if (is_array($fields_label) && count($fields_label) > 1) {
8039 foreach ($fields_label as $field_toshow) {
8040 $translabel = '';
8041 if (!empty($obj->$field_toshow)) {
8042 $translabel = $langs->trans($obj->$field_toshow);
8043 }
8044 if ($translabel != $field_toshow) {
8045 $value .= dol_trunc($translabel, 18) . ' ';
8046 } else {
8047 $value .= $obj->$field_toshow . ' ';
8048 }
8049 }
8050 } else {
8051 $translabel = '';
8052 if (!empty($obj->{$InfoFieldList[1]})) {
8053 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
8054 }
8055 if ($translabel != $obj->{$InfoFieldList[1]}) {
8056 $value = dol_trunc($translabel, 18);
8057 } else {
8058 $value = $obj->{$InfoFieldList[1]};
8059 }
8060 }
8061 }
8062 } else {
8063 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
8064
8065 $toprint = array();
8066 $obj = $this->db->fetch_object($resql);
8067 $c = new Categorie($this->db);
8068 $c->fetch($obj->rowid);
8069 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
8070 foreach ($ways as $way) {
8071 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
8072 }
8073 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
8074 }
8075 } else {
8076 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
8077 }
8078 } elseif ($type == 'radio') {
8079 $value = $param['options'][$value];
8080 } elseif ($type == 'checkbox') {
8081 $value_arr = explode(',', $value);
8082 $value = '';
8083 if (is_array($value_arr) && count($value_arr) > 0) {
8084 $toprint = array();
8085 foreach ($value_arr as $keyval => $valueval) {
8086 if (!empty($valueval)) {
8087 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $param['options'][$valueval] . '</li>';
8088 }
8089 }
8090 if (!empty($toprint)) {
8091 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">' . implode(' ', $toprint) . '</ul></div>';
8092 }
8093 }
8094 } elseif ($type == 'chkbxlst') {
8095 $value_arr = explode(',', $value);
8096
8097 $param_list = array_keys($param['options']);
8098 $InfoFieldList = explode(":", $param_list[0]);
8099
8100 $selectkey = "rowid";
8101 $keyList = 'rowid';
8102
8103 if (count($InfoFieldList) >= 3) {
8104 $selectkey = $InfoFieldList[2];
8105 $keyList = $InfoFieldList[2].' as rowid';
8106 }
8107
8108 $fields_label = explode('|', $InfoFieldList[1]);
8109 if (is_array($fields_label)) {
8110 $keyList .= ', ';
8111 $keyList .= implode(', ', $fields_label);
8112 }
8113
8114 $filter_categorie = false;
8115 if (count($InfoFieldList) > 5) {
8116 if ($InfoFieldList[0] == 'categorie') {
8117 $filter_categorie = true;
8118 }
8119 }
8120
8121 $sql = "SELECT ".$keyList;
8122 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
8123 if (strpos($InfoFieldList[4], 'extra') !== false) {
8124 $sql .= ' as main';
8125 }
8126 // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
8127 // $sql.= ' AND entity = '.$conf->entity;
8128
8129 dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG);
8130 $resql = $this->db->query($sql);
8131 if ($resql) {
8132 if ($filter_categorie === false) {
8133 $value = ''; // value was used, so now we reste it to use it to build final output
8134 $toprint = array();
8135 while ($obj = $this->db->fetch_object($resql)) {
8136 // Several field into label (eq table:code|libelle:rowid)
8137 $fields_label = explode('|', $InfoFieldList[1]);
8138 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8139 if (is_array($fields_label) && count($fields_label) > 1) {
8140 foreach ($fields_label as $field_toshow) {
8141 $translabel = '';
8142 if (!empty($obj->$field_toshow)) {
8143 $translabel = $langs->trans($obj->$field_toshow);
8144 }
8145 if ($translabel != $field_toshow) {
8146 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
8147 } else {
8148 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->$field_toshow . '</li>';
8149 }
8150 }
8151 } else {
8152 $translabel = '';
8153 if (!empty($obj->{$InfoFieldList[1]})) {
8154 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
8155 }
8156 if ($translabel != $obj->{$InfoFieldList[1]}) {
8157 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
8158 } else {
8159 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->{$InfoFieldList[1]} . '</li>';
8160 }
8161 }
8162 }
8163 }
8164 } else {
8165 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
8166
8167 $toprint = array();
8168 while ($obj = $this->db->fetch_object($resql)) {
8169 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
8170 $c = new Categorie($this->db);
8171 $c->fetch($obj->rowid);
8172 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
8173 foreach ($ways as $way) {
8174 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
8175 }
8176 }
8177 }
8178 }
8179 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
8180 } else {
8181 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
8182 }
8183 } elseif ($type == 'link') {
8184 $out = '';
8185
8186 // only if something to display (perf)
8187 if ($value) {
8188 $param_list = array_keys($param['options']);
8189 // Example: $param_list='ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
8190 // Example: $param_list='ObjectClass:PathToClass:#getnomurlparam1=-1#getnomurlparam2=customer'
8191
8192 $InfoFieldList = explode(":", $param_list[0]);
8193
8194 $classname = $InfoFieldList[0];
8195 $classpath = $InfoFieldList[1];
8196
8197 // Set $getnomurlparam1 et getnomurlparam2
8198 $getnomurlparam = 3;
8199 $getnomurlparam2 = '';
8200 $regtmp = array();
8201 if (preg_match('/#getnomurlparam1=([^#]*)/', $param_list[0], $regtmp)) {
8202 $getnomurlparam = $regtmp[1];
8203 }
8204 if (preg_match('/#getnomurlparam2=([^#]*)/', $param_list[0], $regtmp)) {
8205 $getnomurlparam2 = $regtmp[1];
8206 }
8207
8208 if (!empty($classpath)) {
8209 dol_include_once($InfoFieldList[1]);
8210
8211 if ($classname && !class_exists($classname)) {
8212 // from V19 of Dolibarr, In some cases link use element instead of class, example project_task
8213 // TODO use newObjectByElement() introduce in V20 by PR #30036 for better errors management
8214 $element_prop = getElementProperties($classname);
8215 if ($element_prop) {
8216 $classname = $element_prop['classname'];
8217 }
8218 }
8219
8220
8221 if ($classname && class_exists($classname)) {
8222 $object = new $classname($this->db);
8223 if ($object->element === 'product') { // Special case for product because default valut of fetch are wrong
8224 $result = $object->fetch($value, '', '', '', 0, 1, 1);
8225 } else {
8226 $result = $object->fetch($value);
8227 }
8228 if ($result > 0) {
8229 if ($object->element === 'product') {
8230 $get_name_url_param_arr = array($getnomurlparam, $getnomurlparam2, 0, -1, 0, '', 0);
8231 if (isset($val['get_name_url_params'])) {
8232 $get_name_url_params = explode(':', $val['get_name_url_params']);
8233 if (!empty($get_name_url_params)) {
8234 $param_num_max = count($get_name_url_param_arr) - 1;
8235 foreach ($get_name_url_params as $param_num => $param_value) {
8236 if ($param_num > $param_num_max) {
8237 break;
8238 }
8239 $get_name_url_param_arr[$param_num] = $param_value;
8240 }
8241 }
8242 }
8243
8247 $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]);
8248 } else {
8249 $value = $object->getNomUrl($getnomurlparam, $getnomurlparam2);
8250 }
8251 } else {
8252 $value = '';
8253 }
8254 }
8255 } else {
8256 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
8257 return 'Error bad setup of extrafield';
8258 }
8259 } else {
8260 $value = '';
8261 }
8262 } elseif ($type == 'password') {
8263 $value = preg_replace('/./i', '*', $value);
8264 } elseif ($type == 'array') {
8265 $value = implode('<br>', $value);
8266 } else { // text|html|varchar
8267 $value = dol_htmlentitiesbr($value);
8268 }
8269
8270 //print $type.'-'.$size.'-'.$value;
8271 $out = $value;
8272
8273 return $out;
8274 }
8275
8282 public function clearFieldError($fieldKey)
8283 {
8284 $this->error = '';
8285 unset($this->validateFieldsErrors[$fieldKey]);
8286 }
8287
8295 public function setFieldError($fieldKey, $msg = '')
8296 {
8297 global $langs;
8298 if (empty($msg)) {
8299 $msg = $langs->trans("UnknowError");
8300 }
8301
8302 $this->error = $this->validateFieldsErrors[$fieldKey] = $msg;
8303 }
8304
8311 public function getFieldError($fieldKey)
8312 {
8313 if (!empty($this->validateFieldsErrors[$fieldKey])) {
8314 return $this->validateFieldsErrors[$fieldKey];
8315 }
8316 return '';
8317 }
8318
8327 public function validateField($fields, $fieldKey, $fieldValue)
8328 {
8329 global $langs;
8330
8331 if (!class_exists('Validate')) {
8332 require_once DOL_DOCUMENT_ROOT . '/core/class/validate.class.php';
8333 }
8334
8335 $this->clearFieldError($fieldKey);
8336
8337 if (!isset($fields[$fieldKey])) {
8338 $this->setFieldError($fieldKey, $langs->trans('FieldNotFoundInObject'));
8339 return false;
8340 }
8341
8342 $val = $fields[$fieldKey];
8343
8344 $param = array();
8345 $param['options'] = array();
8346 $type = $val['type'];
8347
8348 $required = false;
8349 if (isset($val['notnull']) && $val['notnull'] === 1) {
8350 // 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
8351 $required = true;
8352 }
8353
8354 $maxSize = 0;
8355 $minSize = 0;
8356
8357 //
8358 // PREPARE Elements
8359 //
8360 $reg = array();
8361
8362 // Convert var to be able to share same code than showOutputField of extrafields
8363 if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
8364 $type = 'varchar'; // convert varchar(xx) int varchar
8365 $maxSize = $reg[1];
8366 } elseif (preg_match('/varchar/', $type)) {
8367 $type = 'varchar'; // convert varchar(xx) int varchar
8368 }
8369
8370 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8371 $type = 'select';
8372 }
8373
8374 if (!empty($val['type']) && preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8375 $type = 'link';
8376 }
8377
8378 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8379 $param['options'] = $val['arrayofkeyval'];
8380 }
8381
8382 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8383 $type = 'link';
8384 $param['options'] = array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
8385 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
8386 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
8387 $type = 'sellist';
8388 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
8389 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
8390 $type = 'sellist';
8391 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
8392 $param['options'] = array($reg[1].':'.$reg[2] => 'N');
8393 $type = 'sellist';
8394 }
8395
8396 //
8397 // TEST Value
8398 //
8399
8400 // Use Validate class to allow external Modules to use data validation part instead of concentrate all test here (factoring) or just for reuse
8401 $validate = new Validate($this->db, $langs);
8402
8403
8404 // little trick : to perform tests with good performances sort tests by quick to low
8405
8406 //
8407 // COMMON TESTS
8408 //
8409
8410 // Required test and empty value
8411 if ($required && !$validate->isNotEmptyString($fieldValue)) {
8412 $this->setFieldError($fieldKey, $validate->error);
8413 return false;
8414 } elseif (!$required && !$validate->isNotEmptyString($fieldValue)) {
8415 // if no value sent and the field is not mandatory, no need to perform tests
8416 return true;
8417 }
8418
8419 // MAX Size test
8420 if (!empty($maxSize) && !$validate->isMaxLength($fieldValue, $maxSize)) {
8421 $this->setFieldError($fieldKey, $validate->error);
8422 return false;
8423 }
8424
8425 // MIN Size test
8426 if (!empty($minSize) && !$validate->isMinLength($fieldValue, $minSize)) {
8427 $this->setFieldError($fieldKey, $validate->error);
8428 return false;
8429 }
8430
8431 //
8432 // TESTS for TYPE
8433 //
8434
8435 if (in_array($type, array('date', 'datetime', 'timestamp'))) {
8436 if (!$validate->isTimestamp($fieldValue)) {
8437 $this->setFieldError($fieldKey, $validate->error);
8438 return false;
8439 } else {
8440 return true;
8441 }
8442 } elseif ($type == 'duration') {
8443 if (!$validate->isDuration($fieldValue)) {
8444 $this->setFieldError($fieldKey, $validate->error);
8445 return false;
8446 } else {
8447 return true;
8448 }
8449 } elseif (in_array($type, array('double', 'real', 'price'))) {
8450 // is numeric
8451 if (!$validate->isNumeric($fieldValue)) {
8452 $this->setFieldError($fieldKey, $validate->error);
8453 return false;
8454 } else {
8455 return true;
8456 }
8457 } elseif ($type == 'boolean') {
8458 if (!$validate->isBool($fieldValue)) {
8459 $this->setFieldError($fieldKey, $validate->error);
8460 return false;
8461 } else {
8462 return true;
8463 }
8464 } elseif ($type == 'mail') {
8465 if (!$validate->isEmail($fieldValue)) {
8466 $this->setFieldError($fieldKey, $validate->error);
8467 return false;
8468 }
8469 } elseif ($type == 'url') {
8470 if (!$validate->isUrl($fieldValue)) {
8471 $this->setFieldError($fieldKey, $validate->error);
8472 return false;
8473 } else {
8474 return true;
8475 }
8476 } elseif ($type == 'phone') {
8477 if (!$validate->isPhone($fieldValue)) {
8478 $this->setFieldError($fieldKey, $validate->error);
8479 return false;
8480 } else {
8481 return true;
8482 }
8483 } elseif ($type == 'select' || $type == 'radio') {
8484 if (!isset($param['options'][$fieldValue])) {
8485 $this->error = $langs->trans('RequireValidValue');
8486 return false;
8487 } else {
8488 return true;
8489 }
8490 } elseif ($type == 'sellist' || $type == 'chkbxlst') {
8491 $param_list = array_keys($param['options']);
8492 $InfoFieldList = explode(":", $param_list[0]);
8493 $value_arr = explode(',', $fieldValue);
8494 $value_arr = array_map(array($this->db, 'escape'), $value_arr);
8495
8496 $selectkey = "rowid";
8497 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
8498 $selectkey = $InfoFieldList[2];
8499 }
8500
8501 if (!$validate->isInDb($value_arr, $InfoFieldList[0], $selectkey)) {
8502 $this->setFieldError($fieldKey, $validate->error);
8503 return false;
8504 } else {
8505 return true;
8506 }
8507 } elseif ($type == 'link') {
8508 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
8509 $InfoFieldList = explode(":", $param_list[0]);
8510 $classname = $InfoFieldList[0];
8511 $classpath = $InfoFieldList[1];
8512 if (!$validate->isFetchable($fieldValue, $classname, $classpath)) {
8513 $lastIsFetchableError = $validate->error;
8514
8515 // from V19 of Dolibarr, In some cases link use element instead of class, example project_task
8516 if ($validate->isFetchableElement($fieldValue, $classname)) {
8517 return true;
8518 }
8519
8520 $this->setFieldError($fieldKey, $lastIsFetchableError);
8521 return false;
8522 } else {
8523 return true;
8524 }
8525 }
8526
8527 // if no test failled all is ok
8528 return true;
8529 }
8530
8544 public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = '', $display_type = 'card')
8545 {
8546 global $db, $conf, $langs, $action, $form, $hookmanager;
8547
8548 if (!is_object($form)) {
8549 $form = new Form($db);
8550 }
8551 if (!is_object($extrafields)) {
8552 dol_syslog('Bad parameter extrafields for showOptionals', LOG_ERR);
8553 return 'Bad parameter extrafields for showOptionals';
8554 }
8555 if (!is_array($extrafields->attributes[$this->table_element])) {
8556 dol_syslog("extrafields->attributes was not loaded with extrafields->fetch_name_optionals_label(table_element);", LOG_WARNING);
8557 }
8558
8559 $out = '';
8560
8561 $parameters = array('mode'=>$mode, 'params'=>$params, 'keysuffix'=>$keysuffix, 'keyprefix'=>$keyprefix, 'display_type'=>$display_type);
8562 $reshook = $hookmanager->executeHooks('showOptionals', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
8563
8564 if (empty($reshook)) {
8565 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) {
8566 $out .= "\n";
8567 $out .= '<!-- commonobject:showOptionals --> ';
8568 $out .= "\n";
8569
8570 $nbofextrafieldsshown = 0;
8571 $e = 0; // var to manage the modulo (odd/even)
8572
8573 $lastseparatorkeyfound = '';
8574 $extrafields_collapse_num = '';
8575 $extrafields_collapse_num_old = '';
8576 $i = 0;
8577
8578 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $label) {
8579 $i++;
8580
8581 // Show only the key field in params
8582 if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) {
8583 continue;
8584 }
8585
8586 // Test on 'enabled' ('enabled' is different than 'list' = 'visibility')
8587 $enabled = 1;
8588 if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
8589 $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
8590 }
8591 if (empty($enabled)) {
8592 continue;
8593 }
8594
8595 $visibility = 1;
8596 if ($visibility && isset($extrafields->attributes[$this->table_element]['list'][$key])) {
8597 $visibility = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
8598 }
8599
8600 $perms = 1;
8601 if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
8602 $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
8603 }
8604
8605 if (($mode == 'create') && abs($visibility) != 1 && abs($visibility) != 3) {
8606 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list
8607 } elseif (($mode == 'edit') && abs($visibility) != 1 && abs($visibility) != 3 && abs($visibility) != 4) {
8608 // We need to make sure, that the values of hidden extrafields are also part of $_POST. Otherwise, they would be empty after an update of the object. See also getOptionalsFromPost
8609 $ef_name = 'options_' . $key;
8610 $ef_value = $this->array_options[$ef_name];
8611 $out .= '<input type="hidden" name="' . $ef_name . '" id="' . $ef_name . '" value="' . $ef_value . '" />' . "\n";
8612 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list and <> 4 = not visible at the creation
8613 } elseif ($mode == 'view' && empty($visibility)) {
8614 continue;
8615 }
8616 if (empty($perms)) {
8617 continue;
8618 }
8619
8620 // Load language if required
8621 if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
8622 $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
8623 }
8624
8625 $colspan = 0;
8626 if (is_array($params) && count($params) > 0 && $display_type=='card') {
8627 if (array_key_exists('cols', $params)) {
8628 $colspan = $params['cols'];
8629 } elseif (array_key_exists('colspan', $params)) { // For backward compatibility. Use cols instead now.
8630 $reg = array();
8631 if (preg_match('/colspan="(\d+)"/', $params['colspan'], $reg)) {
8632 $colspan = $reg[1];
8633 } else {
8634 $colspan = $params['colspan'];
8635 }
8636 }
8637 }
8638 $colspan = intval($colspan);
8639
8640 switch ($mode) {
8641 case "view":
8642 $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
8643 break;
8644 case "create":
8645 case "edit":
8646 // We get the value of property found with GETPOST so it takes into account:
8647 // default values overwrite, restore back to list link, ... (but not 'default value in database' of field)
8648 $check = 'alphanohtml';
8649 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text'))) {
8650 $check = 'restricthtml';
8651 }
8652 $getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, $check, 3); // GETPOST can get value from GET, POST or setup of default values overwrite.
8653 // GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
8654 if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)) {
8655 if (is_array($getposttemp)) {
8656 // $getposttemp is an array but following code expects a comma separated string
8657 $value = implode(",", $getposttemp);
8658 } else {
8659 $value = $getposttemp;
8660 }
8661 } elseif (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('int'))) {
8662 $value =( !empty($this->array_options["options_".$key]) || (isset($this->array_options["options_".$key]) && $this->array_options["options_".$key] === '0')) ? $this->array_options["options_".$key] : '';
8663 } else {
8664 $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.
8665 }
8666 //var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
8667 break;
8668 }
8669
8670 $nbofextrafieldsshown++;
8671
8672 // Output value of the current field
8673 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
8674 $extrafields_collapse_num = $key;
8675 /*
8676 $extrafield_param = $extrafields->attributes[$this->table_element]['param'][$key];
8677 if (!empty($extrafield_param) && is_array($extrafield_param)) {
8678 $extrafield_param_list = array_keys($extrafield_param['options']);
8679
8680 if (count($extrafield_param_list) > 0) {
8681 $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
8682
8683 if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
8684 //$extrafields_collapse_num = $extrafields->attributes[$this->table_element]['pos'][$key];
8685 $extrafields_collapse_num = $key;
8686 }
8687 }
8688 }
8689 */
8690
8691 // if colspan=0 or 1, the second column is not extended, so the separator must be on 2 columns
8692 $out .= $extrafields->showSeparator($key, $this, ($colspan ? $colspan + 1 : 2), $display_type, $mode);
8693
8694 $lastseparatorkeyfound = $key;
8695 } else {
8696 $collapse_group = $extrafields_collapse_num.(!empty($this->id) ? '_'.$this->id : '');
8697
8698 $class = (!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
8699 $csstyle = '';
8700 if (is_array($params) && count($params) > 0) {
8701 if (array_key_exists('class', $params)) {
8702 $class .= $params['class'].' ';
8703 }
8704 if (array_key_exists('style', $params)) {
8705 $csstyle = $params['style'];
8706 }
8707 }
8708
8709 // add html5 elements
8710 $domData = ' data-element="extrafield"';
8711 $domData .= ' data-targetelement="'.$this->element.'"';
8712 $domData .= ' data-targetid="'.$this->id.'"';
8713
8714 $html_id = (empty($this->id) ? '' : 'extrarow-'.$this->element.'_'.$key.'_'.$this->id);
8715 if ($display_type=='card') {
8716 if (getDolGlobalString('MAIN_EXTRAFIELDS_USE_TWO_COLUMS') && ($e % 2) == 0) {
8717 $colspan = 0;
8718 }
8719
8720 if ($action == 'selectlines') {
8721 $colspan++;
8722 }
8723 }
8724
8725 // Convert date into timestamp format (value in memory must be a timestamp)
8726 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date'))) {
8727 $datenotinstring = null;
8728 if (array_key_exists('options_'.$key, $this->array_options)) {
8729 $datenotinstring = $this->array_options['options_'.$key];
8730 if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
8731 $datenotinstring = $this->db->jdate($datenotinstring);
8732 }
8733 }
8734 $datekey = $keyprefix.'options_'.$key.$keysuffix;
8735 $value = (GETPOSTISSET($datekey) && $this->id == GETPOST('elrowid', 'int')) ? dol_mktime(12, 0, 0, GETPOST($datekey.'month', 'int', 3), GETPOST($datekey.'day', 'int', 3), GETPOST($datekey.'year', 'int', 3)) : $datenotinstring;
8736 }
8737 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('datetime'))) {
8738 $datenotinstring = null;
8739 if (array_key_exists('options_'.$key, $this->array_options)) {
8740 $datenotinstring = $this->array_options['options_'.$key];
8741 if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
8742 $datenotinstring = $this->db->jdate($datenotinstring);
8743 }
8744 }
8745 $timekey = $keyprefix.'options_'.$key.$keysuffix;
8746 $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;
8747 }
8748 // Convert float submited string into real php numeric (value in memory must be a php numeric)
8749 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price', 'double'))) {
8750 if (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) {
8751 $value = price2num($value);
8752 } elseif (isset($this->array_options['options_'.$key])) {
8753 $value = $this->array_options['options_'.$key];
8754 }
8755 }
8756
8757 // HTML, text, select, integer and varchar: take into account default value in database if in create mode
8758 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text', 'varchar', 'select', 'radio', 'int', 'boolean'))) {
8759 if ($action == 'create' || $mode == 'create') {
8760 $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) ? $value : $extrafields->attributes[$this->table_element]['default'][$key];
8761 }
8762 }
8763
8764 $labeltoshow = $langs->trans($label);
8765 $helptoshow = $langs->trans($extrafields->attributes[$this->table_element]['help'][$key]);
8766
8767 if ($display_type == 'card') {
8768 $out .= '<tr '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="field_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
8769 if (getDolGlobalString('MAIN_VIEW_LINE_NUMBER') && ($action == 'view' || $action == 'valid' || $action == 'editline' || $action == 'confirm_valid' || $action == 'confirm_cancel')) {
8770 $out .= '<td></td>';
8771 }
8772 $out .= '<td class="'.(empty($params['tdclass']) ? 'titlefieldcreate' : $params['tdclass']).' wordbreak';
8773 } elseif ($display_type == 'line') {
8774 $out .= '<div '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="fieldline_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
8775 $out .= '<div style="display: inline-block; padding-right:4px" class="wordbreak';
8776 }
8777 //$out .= "titlefield";
8778 //if (GETPOST('action', 'restricthtml') == 'create') $out.='create';
8779 // BUG #11554 : For public page, use red dot for required fields, instead of bold label
8780 $tpl_context = isset($params["tpl_context"]) ? $params["tpl_context"] : "none";
8781 if ($tpl_context != "public") { // Public page : red dot instead of fieldrequired characters
8782 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
8783 $out .= ' fieldrequired';
8784 }
8785 }
8786 $out .= '">';
8787 if ($tpl_context == "public") { // Public page : red dot instead of fieldrequired characters
8788 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
8789 $out .= $form->textwithpicto($labeltoshow, $helptoshow);
8790 } else {
8791 $out .= $labeltoshow;
8792 }
8793 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
8794 $out .= '&nbsp;<span style="color: red">*</span>';
8795 }
8796 } else {
8797 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
8798 $out .= $form->textwithpicto($labeltoshow, $helptoshow);
8799 } else {
8800 $out .= $labeltoshow;
8801 }
8802 }
8803
8804 $out .= ($display_type == 'card' ? '</td>' : '</div>');
8805
8806 $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
8807 if ($display_type == 'card') {
8808 // 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
8809 $out .= '<td '.($html_id ? 'id="'.$html_id.'" ' : '').' class="valuefieldcreate '.$this->element.'_extras_'.$key.'" '.($colspan ? ' colspan="'.$colspan.'"' : '').'>';
8810 } elseif ($display_type == 'line') {
8811 $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].'">';
8812 }
8813
8814 switch ($mode) {
8815 case "view":
8816 $out .= $extrafields->showOutputField($key, $value, '', $this->table_element);
8817 break;
8818 case "create":
8819 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
8820 break;
8821 case "edit":
8822 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
8823 break;
8824 }
8825
8826 $out .= ($display_type=='card' ? '</td>' : '</div>');
8827
8828 if (getDolGlobalString('MAIN_EXTRAFIELDS_USE_TWO_COLUMS') && (($e % 2) == 1)) {
8829 $out .= ($display_type=='card' ? '</tr>' : '</div>');
8830 } else {
8831 $out .= ($display_type=='card' ? '</tr>' : '</div>');
8832 }
8833
8834 $e++;
8835 }
8836 }
8837 $out .= "\n";
8838 // Add code to manage list depending on others
8839 if (!empty($conf->use_javascript_ajax)) {
8840 $out .= $this->getJSListDependancies();
8841 }
8842
8843 $out .= '<!-- commonobject:showOptionals end --> '."\n";
8844
8845 if (empty($nbofextrafieldsshown)) {
8846 $out = '';
8847 }
8848 }
8849 }
8850
8851 $out .= $hookmanager->resPrint;
8852
8853 return $out;
8854 }
8855
8860 public function getJSListDependancies($type = '_extra')
8861 {
8862 $out = '
8863 <script nonce="'.getNonce().'">
8864 jQuery(document).ready(function() {
8865 function showOptions'.$type.'(child_list, parent_list, orig_select)
8866 {
8867 var val = $("select[name=\""+parent_list+"\"]").val();
8868 var parentVal = parent_list + ":" + val;
8869 if(typeof val == "string"){
8870 if(val != "") {
8871 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
8872 $("select[name=\""+child_list+"\"] option[parent]").remove();
8873 $("select[name=\""+child_list+"\"]").append(options);
8874 } else {
8875 var options = orig_select.find("option[parent]").clone();
8876 $("select[name=\""+child_list+"\"] option[parent]").remove();
8877 $("select[name=\""+child_list+"\"]").append(options);
8878 }
8879 } else if(val > 0) {
8880 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
8881 $("select[name=\""+child_list+"\"] option[parent]").remove();
8882 $("select[name=\""+child_list+"\"]").append(options);
8883 } else {
8884 var options = orig_select.find("option[parent]").clone();
8885 $("select[name=\""+child_list+"\"] option[parent]").remove();
8886 $("select[name=\""+child_list+"\"]").append(options);
8887 }
8888 }
8889 function setListDependencies'.$type.'() {
8890 jQuery("select option[parent]").parent().each(function() {
8891 var orig_select = {};
8892 var child_list = $(this).attr("name");
8893 orig_select[child_list] = $(this).clone();
8894 var parent = $(this).find("option[parent]:first").attr("parent");
8895 var infos = parent.split(":");
8896 var parent_list = infos[0];
8897
8898 //Hide daughters lists
8899 if ($("#"+child_list).val() == 0 && $("#"+parent_list).val() == 0){
8900 $("#"+child_list).hide();
8901 //Show mother lists
8902 } else if ($("#"+parent_list).val() != 0){
8903 $("#"+parent_list).show();
8904 }
8905 //Show the child list if the parent list value is selected
8906 $("select[name=\""+parent_list+"\"]").click(function() {
8907 if ($(this).val() != 0){
8908 $("#"+child_list).show()
8909 }
8910 });
8911
8912 //When we change parent list
8913 $("select[name=\""+parent_list+"\"]").change(function() {
8914 showOptions'.$type.'(child_list, parent_list, orig_select[child_list]);
8915 //Select the value 0 on child list after a change on the parent list
8916 $("#"+child_list).val(0).trigger("change");
8917 //Hide child lists if the parent value is set to 0
8918 if ($(this).val() == 0){
8919 $("#"+child_list).hide();
8920 }
8921 });
8922 });
8923 }
8924
8925 setListDependencies'.$type.'();
8926 });
8927 </script>'."\n";
8928 return $out;
8929 }
8930
8936 public function getRights()
8937 {
8938 global $user;
8939
8940 $module = empty($this->module) ? '' : $this->module;
8941 $element = $this->element;
8942
8943 if ($element == 'facturerec') {
8944 $element = 'facture';
8945 } elseif ($element == 'invoice_supplier_rec') {
8946 return !$user->hasRight('fournisseur', 'facture') ? null : $user->hasRight('fournisseur', 'facture');
8947 } elseif ($module && $user->hasRight($module, $element)) {
8948 // for modules built with ModuleBuilder
8949 return $user->hasRight($module, $element);
8950 }
8951
8952 return $user->rights->$element;
8953 }
8954
8967 public static function commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
8968 {
8969 foreach ($tables as $table) {
8970 $sql = 'UPDATE '.$dbs->prefix().$table.' SET fk_soc = '.((int) $dest_id).' WHERE fk_soc = '.((int) $origin_id);
8971
8972 if (!$dbs->query($sql)) {
8973 if ($ignoreerrors) {
8974 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.
8975 }
8976 //$this->errors = $db->lasterror();
8977 return false;
8978 }
8979 }
8980
8981 return true;
8982 }
8983
8996 public static function commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
8997 {
8998 foreach ($tables as $table) {
8999 $sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_product = '.((int) $dest_id).' WHERE fk_product = '.((int) $origin_id);
9000
9001 if (!$dbs->query($sql)) {
9002 if ($ignoreerrors) {
9003 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.
9004 }
9005 //$this->errors = $db->lasterror();
9006 return false;
9007 }
9008 }
9009
9010 return true;
9011 }
9012
9025 public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
9026 {
9027 global $conf;
9028
9029 $buyPrice = 0;
9030
9031 if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && getDolGlobalInt('ForceBuyingPriceIfNull') > 0)) {
9032 // When ForceBuyingPriceIfNull is set
9033 $buyPrice = $unitPrice * (1 - $discountPercent / 100);
9034 } else {
9035 // Get cost price for margin calculation
9036 if (!empty($fk_product) && $fk_product > 0) {
9037 if (getDolGlobalString('MARGIN_TYPE') == 'costprice') {
9038 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
9039 $product = new Product($this->db);
9040 $result = $product->fetch($fk_product);
9041 if ($result <= 0) {
9042 $this->errors[] = 'ErrorProductIdDoesNotExists';
9043 return -1;
9044 }
9045 if ($product->cost_price > 0) {
9046 $buyPrice = $product->cost_price;
9047 } elseif ($product->pmp > 0) {
9048 $buyPrice = $product->pmp;
9049 }
9050 } elseif (getDolGlobalString('MARGIN_TYPE') == 'pmp') {
9051 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
9052 $product = new Product($this->db);
9053 $result = $product->fetch($fk_product);
9054 if ($result <= 0) {
9055 $this->errors[] = 'ErrorProductIdDoesNotExists';
9056 return -1;
9057 }
9058 if ($product->pmp > 0) {
9059 $buyPrice = $product->pmp;
9060 }
9061 }
9062
9063 if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1', 'pmp', 'costprice'))) {
9064 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
9065 $productFournisseur = new ProductFournisseur($this->db);
9066 if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0) {
9067 $buyPrice = $productFournisseur->fourn_unitprice;
9068 } elseif ($result < 0) {
9069 $this->errors[] = $productFournisseur->error;
9070 return -2;
9071 }
9072 }
9073 }
9074 }
9075 return $buyPrice;
9076 }
9077
9078 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
9098 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')
9099 {
9100 // phpcs:enable
9101 global $conf, $user, $langs;
9102
9103 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
9104 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
9105
9106 $sortfield = 'position_name';
9107 $sortorder = 'asc';
9108
9109 $dir = $sdir.'/';
9110 $pdir = '/';
9111
9112 $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
9113 $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
9114
9115 // For backward compatibility
9116 if ($modulepart == 'product') {
9117 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
9118 $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9119 $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
9120 }
9121 }
9122
9123 // Defined relative dir to DOL_DATA_ROOT
9124 $relativedir = '';
9125 if ($dir) {
9126 $relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
9127 $relativedir = preg_replace('/^[\\/]/', '', $relativedir);
9128 $relativedir = preg_replace('/[\\/]$/', '', $relativedir);
9129 }
9130
9131 $dirthumb = $dir.'thumbs/';
9132 $pdirthumb = $pdir.'thumbs/';
9133
9134 $return = '<!-- Photo -->'."\n";
9135 $nbphoto = 0;
9136
9137 $filearray = dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ? SORT_DESC : SORT_ASC), 1);
9138
9139 /*if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) // For backward compatiblity, we scan also old dirs
9140 {
9141 $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
9142 $filearray=array_merge($filearray, $filearrayold);
9143 }*/
9144
9145 completeFileArrayWithDatabaseInfo($filearray, $relativedir);
9146
9147 if (count($filearray)) {
9148 if ($sortfield && $sortorder) {
9149 $filearray = dol_sort_array($filearray, $sortfield, $sortorder);
9150 }
9151
9152 foreach ($filearray as $key => $val) {
9153 $photo = '';
9154 $file = $val['name'];
9155
9156 //if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
9157 if (image_format_supported($file) >= 0) {
9158 $nbphoto++;
9159 $photo = $file;
9160 $viewfilename = $file;
9161
9162 if ($size == 1 || $size == 'small') { // Format vignette
9163 // Find name of thumb file
9164 $photo_vignette = basename(getImageFileNameForSize($dir.$file, '_small'));
9165 if (!dol_is_file($dirthumb.$photo_vignette)) {
9166 // The thumb does not exists, so we will use the original file
9167 $dirthumb = $dir;
9168 $pdirthumb = $pdir;
9169 $photo_vignette = basename($file);
9170 }
9171
9172 // Get filesize of original file
9173 $imgarray = dol_getImageSize($dir.$photo);
9174
9175 if ($nbbyrow > 0) {
9176 if ($nbphoto == 1) {
9177 $return .= '<table class="valigntop center centpercent" style="border: 0; padding: 2px; border-spacing: 2px; border-collapse: separate;">';
9178 }
9179
9180 if ($nbphoto % $nbbyrow == 1) {
9181 $return .= '<tr class="center valignmiddle" style="border: 1px">';
9182 }
9183 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%" class="photo">'."\n";
9184 } elseif ($nbbyrow < 0) {
9185 $return .= '<div class="inline-block">'."\n";
9186 }
9187
9188 $relativefile = preg_replace('/^\//', '', $pdir.$photo);
9189 if (empty($nolink)) {
9190 $urladvanced = getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
9191 if ($urladvanced) {
9192 $return .= '<a href="'.$urladvanced.'">';
9193 } else {
9194 $return .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank" rel="noopener noreferrer">';
9195 }
9196 }
9197
9198 // Show image (width height=$maxHeight)
9199 // Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
9200 $alt = $langs->transnoentitiesnoconv('File').': '.$relativefile;
9201 $alt .= ' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
9202 if ($overwritetitle) {
9203 if (is_numeric($overwritetitle)) {
9204 $alt = '';
9205 } else {
9206 $alt = $overwritetitle;
9207 }
9208 }
9209
9210 if ($usesharelink) {
9211 if ($val['share']) {
9212 if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
9213 $return .= '<!-- Show original file (thumb not yet available with shared links) -->';
9214 $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).'">';
9215 } else {
9216 $return .= '<!-- Show original file -->';
9217 $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).'">';
9218 }
9219 } else {
9220 $return .= '<!-- Show nophoto file (because file is not shared) -->';
9221 $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
9222 }
9223 } else {
9224 if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
9225 $return .= '<!-- Show thumb -->';
9226 $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).'">';
9227 } else {
9228 $return .= '<!-- Show original file -->';
9229 $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).'">';
9230 }
9231 }
9232
9233 if (empty($nolink)) {
9234 $return .= '</a>';
9235 }
9236
9237 if ($showfilename) {
9238 $return .= '<br>'.$viewfilename;
9239 }
9240 if ($showaction) {
9241 $return .= '<br>';
9242 // If $photo_vignette set, we add link to generate thumbs if file is an image and ->imgWidth or->imgHeight higher than limits
9243 if ($photo_vignette && (image_format_supported($photo) > 0) && ((isset($this->imgWidth) && $this->imgWidth > $maxWidth) || (isset($this->imgHeight) && $this->imgHeight > $maxHeight))) {
9244 $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>';
9245 }
9246 // Special cas for product
9247 if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
9248 // Link to resize
9249 $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; ';
9250
9251 // Link to delete
9252 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
9253 $return .= img_delete().'</a>';
9254 }
9255 }
9256 $return .= "\n";
9257
9258 if ($nbbyrow > 0) {
9259 $return .= '</td>';
9260 if (($nbphoto % $nbbyrow) == 0) {
9261 $return .= '</tr>';
9262 }
9263 } elseif ($nbbyrow < 0) {
9264 $return .= '</div>'."\n";
9265 }
9266 }
9267
9268 if (empty($size)) { // Format origine
9269 $return .= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
9270
9271 if ($showfilename) {
9272 $return .= '<br>'.$viewfilename;
9273 }
9274 if ($showaction) {
9275 // Special case for product
9276 if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
9277 // Link to resize
9278 $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; ';
9279
9280 // Link to delete
9281 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
9282 $return .= img_delete().'</a>';
9283 }
9284 }
9285 }
9286
9287 // On continue ou on arrete de boucler ?
9288 if ($nbmax && $nbphoto >= $nbmax) {
9289 break;
9290 }
9291 }
9292 }
9293
9294 if ($size == 1 || $size == 'small') {
9295 if ($nbbyrow > 0) {
9296 // Ferme tableau
9297 while ($nbphoto % $nbbyrow) {
9298 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%">&nbsp;</td>';
9299 $nbphoto++;
9300 }
9301
9302 if ($nbphoto) {
9303 $return .= '</table>';
9304 }
9305 }
9306 }
9307 }
9308
9309 $this->nbphoto = $nbphoto;
9310
9311 return $return;
9312 }
9313
9314
9321 protected function isArray($info)
9322 {
9323 if (is_array($info)) {
9324 if (isset($info['type']) && $info['type'] == 'array') {
9325 return true;
9326 } else {
9327 return false;
9328 }
9329 }
9330 return false;
9331 }
9332
9339 public function isDate($info)
9340 {
9341 if (isset($info['type']) && ($info['type'] == 'date' || $info['type'] == 'datetime' || $info['type'] == 'timestamp')) {
9342 return true;
9343 }
9344 return false;
9345 }
9346
9353 public function isDuration($info)
9354 {
9355 if (is_array($info)) {
9356 if (isset($info['type']) && ($info['type'] == 'duration')) {
9357 return true;
9358 } else {
9359 return false;
9360 }
9361 } else {
9362 return false;
9363 }
9364 }
9365
9372 public function isInt($info)
9373 {
9374 if (is_array($info)) {
9375 if (isset($info['type']) && (preg_match('/(^int|int$)/i', $info['type']))) {
9376 return true;
9377 } else {
9378 return false;
9379 }
9380 } else {
9381 return false;
9382 }
9383 }
9384
9391 public function isFloat($info)
9392 {
9393 if (is_array($info)) {
9394 if (isset($info['type']) && (preg_match('/^(double|real|price)/i', $info['type']))) {
9395 return true;
9396 } else {
9397 return false;
9398 }
9399 }
9400 return false;
9401 }
9402
9409 public function isText($info)
9410 {
9411 if (is_array($info)) {
9412 if (isset($info['type']) && $info['type'] == 'text') {
9413 return true;
9414 } else {
9415 return false;
9416 }
9417 }
9418 return false;
9419 }
9420
9427 protected function canBeNull($info)
9428 {
9429 if (is_array($info)) {
9430 if (isset($info['notnull']) && $info['notnull'] != '1') {
9431 return true;
9432 } else {
9433 return false;
9434 }
9435 }
9436 return true;
9437 }
9438
9445 protected function isForcedToNullIfZero($info)
9446 {
9447 if (is_array($info)) {
9448 if (isset($info['notnull']) && $info['notnull'] == '-1') {
9449 return true;
9450 } else {
9451 return false;
9452 }
9453 }
9454 return false;
9455 }
9456
9463 protected function isIndex($info)
9464 {
9465 if (is_array($info)) {
9466 if (isset($info['index']) && $info['index'] == true) {
9467 return true;
9468 } else {
9469 return false;
9470 }
9471 }
9472 return false;
9473 }
9474
9475
9484 protected function setSaveQuery()
9485 {
9486 global $conf;
9487
9488 $queryarray = array();
9489 foreach ($this->fields as $field => $info) { // Loop on definition of fields
9490 // Depending on field type ('datetime', ...)
9491 if ($this->isDate($info)) {
9492 if (empty($this->{$field})) {
9493 $queryarray[$field] = null;
9494 } else {
9495 $queryarray[$field] = $this->db->idate($this->{$field});
9496 }
9497 } elseif ($this->isDuration($info)) {
9498 // $this->{$field} may be null, '', 0, '0', 123, '123'
9499 if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
9500 if (!isset($this->{$field})) {
9501 if (!empty($info['default'])) {
9502 $queryarray[$field] = $info['default'];
9503 } else {
9504 $queryarray[$field] = 0;
9505 }
9506 } else {
9507 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9508 }
9509 } else {
9510 $queryarray[$field] = null;
9511 }
9512 } elseif ($this->isInt($info) || $this->isFloat($info)) {
9513 if ($field == 'entity' && is_null($this->{$field})) {
9514 $queryarray[$field] = ((int) $conf->entity);
9515 } else {
9516 // $this->{$field} may be null, '', 0, '0', 123, '123'
9517 if ((isset($this->{$field}) && ((string) $this->{$field}) != '') || !empty($info['notnull'])) {
9518 if (!isset($this->{$field})) {
9519 $queryarray[$field] = 0;
9520 } elseif ($this->isInt($info)) {
9521 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9522 } elseif ($this->isFloat($info)) {
9523 $queryarray[$field] = (float) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9524 }
9525 } else {
9526 $queryarray[$field] = null;
9527 }
9528 }
9529 } else {
9530 // Note: If $this->{$field} is not defined, it means there is a bug into definition of ->fields or a missing declaration of property
9531 // We should keep the warning generated by this because it is a bug somewhere else in code, not here.
9532 $queryarray[$field] = $this->{$field};
9533 }
9534
9535 if ($info['type'] == 'timestamp' && empty($queryarray[$field])) {
9536 unset($queryarray[$field]);
9537 }
9538 if (!empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) {
9539 $queryarray[$field] = null; // May force 0 to null
9540 }
9541 }
9542
9543 return $queryarray;
9544 }
9545
9552 public function setVarsFromFetchObj(&$obj)
9553 {
9554 global $db;
9555
9556 foreach ($this->fields as $field => $info) {
9557 if ($this->isDate($info)) {
9558 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') {
9559 $this->$field = '';
9560 } else {
9561 $this->$field = $db->jdate($obj->$field);
9562 }
9563 } elseif ($this->isInt($info)) {
9564 if ($field == 'rowid') {
9565 $this->id = (int) $obj->$field;
9566 } else {
9567 if ($this->isForcedToNullIfZero($info)) {
9568 if (empty($obj->$field)) {
9569 $this->$field = null;
9570 } else {
9571 $this->$field = (float) $obj->$field;
9572 }
9573 } else {
9574 if (isset($obj->$field) && (!is_null($obj->$field) || (isset($info['notnull']) && $info['notnull'] == 1))) {
9575 $this->$field = (int) $obj->$field;
9576 } else {
9577 $this->$field = null;
9578 }
9579 }
9580 }
9581 } elseif ($this->isFloat($info)) {
9582 if ($this->isForcedToNullIfZero($info)) {
9583 if (empty($obj->$field)) {
9584 $this->$field = null;
9585 } else {
9586 $this->$field = (float) $obj->$field;
9587 }
9588 } else {
9589 if (isset($obj->$field) && (!is_null($obj->$field) || (isset($info['notnull']) && $info['notnull'] == 1))) {
9590 $this->$field = (float) $obj->$field;
9591 } else {
9592 $this->$field = null;
9593 }
9594 }
9595 } else {
9596 $this->$field = isset($obj->$field) ? $obj->$field : null;
9597 }
9598 }
9599
9600 // If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
9601 if (!isset($this->fields['ref']) && isset($this->id)) {
9602 $this->ref = $this->id;
9603 }
9604 }
9605
9610 public function emtpyObjectVars()
9611 {
9612 foreach ($this->fields as $field => $arr) {
9613 $this->$field = null;
9614 }
9615 }
9616
9624 public function getFieldList($alias = '', $excludefields = array())
9625 {
9626 $keys = array_keys($this->fields);
9627 if (!empty($alias)) {
9628 $keys_with_alias = array();
9629 foreach ($keys as $fieldname) {
9630 if (!empty($excludefields)) {
9631 if (in_array($fieldname, $excludefields)) { // The field is excluded and must not be in output
9632 continue;
9633 }
9634 }
9635 $keys_with_alias[] = $alias . '.' . $fieldname;
9636 }
9637 return implode(',', $keys_with_alias);
9638 } else {
9639 return implode(',', $keys);
9640 }
9641 }
9642
9650 protected function quote($value, $fieldsentry)
9651 {
9652 if (is_null($value)) {
9653 return 'NULL';
9654 } elseif (preg_match('/^(int|double|real|price)/i', $fieldsentry['type'])) {
9655 return price2num("$value");
9656 } elseif (preg_match('/int$/i', $fieldsentry['type'])) {
9657 return (int) $value;
9658 } elseif ($fieldsentry['type'] == 'boolean') {
9659 if ($value) {
9660 return 'true';
9661 } else {
9662 return 'false';
9663 }
9664 } else {
9665 return "'".$this->db->escape($value)."'";
9666 }
9667 }
9668
9669
9677 public function createCommon(User $user, $notrigger = false)
9678 {
9679 global $langs;
9680
9681 dol_syslog(get_class($this)."::createCommon create", LOG_DEBUG);
9682
9683 $error = 0;
9684
9685 $now = dol_now();
9686
9687 $fieldvalues = $this->setSaveQuery();
9688
9689 if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) {
9690 $fieldvalues['date_creation'] = $this->db->idate($now);
9691 }
9692 if (array_key_exists('fk_user_creat', $fieldvalues) && !($fieldvalues['fk_user_creat'] > 0)) {
9693 $fieldvalues['fk_user_creat'] = $user->id;
9694 }
9695 if (array_key_exists('pass_crypted', $fieldvalues) && property_exists($this, 'pass')) {
9696 $fieldvalues['pass_crypted'] = dol_hash($this->pass);
9697 }
9698 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
9699 if (array_key_exists('ref', $fieldvalues)) {
9700 $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
9701 }
9702
9703 $keys = array();
9704 $values = array(); // Array to store string forged for SQL syntax
9705 foreach ($fieldvalues as $k => $v) {
9706 $keys[$k] = $k;
9707 $value = $this->fields[$k];
9708 $values[$k] = $this->quote($v, $value); // May return string 'NULL' if $value is null
9709 }
9710
9711 // Clean and check mandatory
9712 foreach ($keys as $key) {
9713 // If field is an implicit foreign key field (so type = 'integer:...')
9714 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
9715 $values[$key] = '';
9716 }
9717 if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
9718 $values[$key] = '';
9719 }
9720
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 $error++;
9723 $langs->load("errors");
9724 dol_syslog("Mandatory field '".$key."' is empty and required into ->fields definition of class");
9725 $this->errors[] = $langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
9726 }
9727
9728 // If value is null and there is a default value for field
9729 if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && !is_null($this->fields[$key]['default'])) {
9730 $values[$key] = $this->quote($this->fields[$key]['default'], $this->fields[$key]);
9731 }
9732
9733 // If field is an implicit foreign key field (so type = 'integer:...')
9734 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) {
9735 if (isset($this->fields[$key]['default'])) {
9736 $values[$key] = ((int) $this->fields[$key]['default']);
9737 } else {
9738 $values[$key] = 'null';
9739 }
9740 }
9741 if (!empty($this->fields[$key]['foreignkey']) && empty($values[$key])) {
9742 $values[$key] = 'null';
9743 }
9744 }
9745
9746 if ($error) {
9747 return -1;
9748 }
9749
9750 $this->db->begin();
9751
9752 if (!$error) {
9753 $sql = "INSERT INTO ".$this->db->prefix().$this->table_element;
9754 $sql .= " (".implode(", ", $keys).')';
9755 $sql .= " VALUES (".implode(", ", $values).")"; // $values can contains 'abc' or 123
9756
9757 $res = $this->db->query($sql);
9758 if (!$res) {
9759 $error++;
9760 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
9761 $this->errors[] = "ErrorRefAlreadyExists";
9762 } else {
9763 $this->errors[] = $this->db->lasterror();
9764 }
9765 }
9766 }
9767
9768 if (!$error) {
9769 $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
9770 }
9771
9772 // If we have a field ref with a default value of (PROV)
9773 if (!$error) {
9774 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)') {
9775 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ref = '(PROV".((int) $this->id).")' WHERE (ref = '(PROV)' OR ref = '') AND rowid = ".((int) $this->id);
9776 $resqlupdate = $this->db->query($sql);
9777
9778 if ($resqlupdate === false) {
9779 $error++;
9780 $this->errors[] = $this->db->lasterror();
9781 } else {
9782 $this->ref = '(PROV'.$this->id.')';
9783 }
9784 }
9785 }
9786
9787 // Create extrafields
9788 if (!$error) {
9789 $result = $this->insertExtraFields();
9790 if ($result < 0) {
9791 $error++;
9792 }
9793 }
9794
9795 // Create lines
9796 if (!empty($this->table_element_line) && !empty($this->fk_element)) {
9797 foreach ($this->lines as $line) {
9798 $keyforparent = $this->fk_element;
9799 $line->$keyforparent = $this->id;
9800
9801 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
9802 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
9803 if (!is_object($line)) {
9804 $line = (object) $line;
9805 }
9806
9807 $result = 0;
9808 if (method_exists($line, 'insert')) {
9809 $result = $line->insert($user, 1);
9810 } elseif (method_exists($line, 'create')) {
9811 $result = $line->create($user, 1);
9812 }
9813 if ($result < 0) {
9814 $this->error = $line->error;
9815 $this->db->rollback();
9816 return -1;
9817 }
9818 }
9819 }
9820
9821 // Triggers
9822 if (!$error && !$notrigger) {
9823 // Call triggers
9824 $result = $this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user);
9825 if ($result < 0) {
9826 $error++;
9827 }
9828 // End call triggers
9829 }
9830
9831 // Commit or rollback
9832 if ($error) {
9833 $this->db->rollback();
9834 return -1;
9835 } else {
9836 $this->db->commit();
9837 return $this->id;
9838 }
9839 }
9840
9841
9851 public function fetchCommon($id, $ref = null, $morewhere = '', $noextrafields = 0)
9852 {
9853 if (empty($id) && empty($ref) && empty($morewhere)) {
9854 return -1;
9855 }
9856
9857 $fieldlist = $this->getFieldList('t');
9858 if (empty($fieldlist)) {
9859 return 0;
9860 }
9861
9862 $sql = "SELECT ".$fieldlist;
9863 $sql .= " FROM ".$this->db->prefix().$this->table_element.' as t';
9864
9865 if (!empty($id)) {
9866 $sql .= ' WHERE t.rowid = '.((int) $id);
9867 } elseif (!empty($ref)) {
9868 $sql .= " WHERE t.ref = '".$this->db->escape($ref)."'";
9869 } else {
9870 $sql .= ' WHERE 1 = 1'; // usage with empty id and empty ref is very rare
9871 }
9872 if (empty($id) && isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
9873 $sql .= ' AND t.entity IN ('.getEntity($this->element).')';
9874 }
9875 if ($morewhere) {
9876 $sql .= $morewhere;
9877 }
9878 $sql .= ' LIMIT 1'; // This is a fetch, to be sure to get only one record
9879
9880 $res = $this->db->query($sql);
9881 if ($res) {
9882 $obj = $this->db->fetch_object($res);
9883 if ($obj) {
9884 $this->setVarsFromFetchObj($obj);
9885
9886 // Retrieve all extrafield
9887 // fetch optionals attributes and labels
9888 if (empty($noextrafields)) {
9889 $result = $this->fetch_optionals();
9890 if ($result < 0) {
9891 $this->error = $this->db->lasterror();
9892 $this->errors[] = $this->error;
9893 return -4;
9894 }
9895 }
9896
9897 return $this->id;
9898 } else {
9899 return 0;
9900 }
9901 } else {
9902 $this->error = $this->db->lasterror();
9903 $this->errors[] = $this->error;
9904 return -1;
9905 }
9906 }
9907
9915 public function fetchLinesCommon($morewhere = '', $noextrafields = 0)
9916 {
9917 $objectlineclassname = get_class($this).'Line';
9918 if (!class_exists($objectlineclassname)) {
9919 $this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
9920 return -1;
9921 }
9922
9923 $objectline = new $objectlineclassname($this->db);
9924
9925 $sql = "SELECT ".$objectline->getFieldList('l');
9926 $sql .= " FROM ".$this->db->prefix().$objectline->table_element." as l";
9927 $sql .= " WHERE l.fk_".$this->db->escape($this->element)." = ".((int) $this->id);
9928 if ($morewhere) {
9929 $sql .= $morewhere;
9930 }
9931 if (isset($objectline->fields['position'])) {
9932 $sql .= $this->db->order('position', 'ASC');
9933 }
9934
9935 $resql = $this->db->query($sql);
9936 if ($resql) {
9937 $num_rows = $this->db->num_rows($resql);
9938 $i = 0;
9939 $this->lines = array();
9940 while ($i < $num_rows) {
9941 $obj = $this->db->fetch_object($resql);
9942 if ($obj) {
9943 $newline = new $objectlineclassname($this->db);
9944 $newline->setVarsFromFetchObj($obj);
9945
9946 // Note: extrafields load of line not yet supported
9947 /*
9948 if (empty($noextrafields)) {
9949 // Load extrafields of line
9950 }*/
9951
9952 $this->lines[] = $newline;
9953 }
9954 $i++;
9955 }
9956
9957 return 1;
9958 } else {
9959 $this->error = $this->db->lasterror();
9960 $this->errors[] = $this->error;
9961 return -1;
9962 }
9963 }
9964
9972 public function updateCommon(User $user, $notrigger = false)
9973 {
9974 dol_syslog(get_class($this)."::updateCommon update", LOG_DEBUG);
9975
9976 $error = 0;
9977
9978 $now = dol_now();
9979
9980 // $this->oldcopy should have been set by the caller of update
9981 //if (empty($this->oldcopy)) {
9982 // $this->oldcopy = dol_clone($this);
9983 //}
9984
9985 $fieldvalues = $this->setSaveQuery();
9986
9987 if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) {
9988 $fieldvalues['date_modification'] = $this->db->idate($now);
9989 }
9990 if (array_key_exists('fk_user_modif', $fieldvalues) && !($fieldvalues['fk_user_modif'] > 0)) {
9991 $fieldvalues['fk_user_modif'] = $user->id;
9992 }
9993 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
9994 if (array_key_exists('ref', $fieldvalues)) {
9995 $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
9996 }
9997
9998 // Add quotes and escape on fields with type string
9999 $keys = array();
10000 $values = array();
10001 $tmp = array();
10002 foreach ($fieldvalues as $k => $v) {
10003 $keys[$k] = $k;
10004 $value = $this->fields[$k];
10005 $values[$k] = $this->quote($v, $value);
10006 $tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
10007 }
10008
10009 // Clean and check mandatory fields
10010 foreach ($keys as $key) {
10011 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
10012 $values[$key] = ''; // This is an implicit foreign key field
10013 }
10014 if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
10015 $values[$key] = ''; // This is an explicit foreign key field
10016 }
10017
10018 //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
10019 /*
10020 if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
10021 {
10022 $error++;
10023 $this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
10024 }*/
10025 }
10026
10027 $sql = 'UPDATE '.$this->db->prefix().$this->table_element.' SET '.implode(', ', $tmp).' WHERE rowid='.((int) $this->id);
10028
10029 $this->db->begin();
10030
10031 if (!$error) {
10032 $res = $this->db->query($sql);
10033 if (!$res) {
10034 $error++;
10035 $this->errors[] = $this->db->lasterror();
10036 }
10037 }
10038
10039 // Update extrafield
10040 if (!$error) {
10041 $result = $this->insertExtraFields(); // This delete and reinsert extrafields
10042 if ($result < 0) {
10043 $error++;
10044 }
10045 }
10046
10047 // Triggers
10048 if (!$error && !$notrigger) {
10049 // Call triggers
10050 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
10051 if ($result < 0) {
10052 $error++;
10053 } //Do also here what you must do to rollback action if trigger fail
10054 // End call triggers
10055 }
10056
10057 // Commit or rollback
10058 if ($error) {
10059 $this->db->rollback();
10060 return -1;
10061 } else {
10062 $this->db->commit();
10063 return $this->id;
10064 }
10065 }
10066
10075 public function deleteCommon(User $user, $notrigger = false, $forcechilddeletion = 0)
10076 {
10077 dol_syslog(get_class($this)."::deleteCommon delete", LOG_DEBUG);
10078
10079 $error = 0;
10080
10081 $this->db->begin();
10082
10083 if ($forcechilddeletion) { // Force also delete of childtables that should lock deletion in standard case when option force is off
10084 foreach ($this->childtables as $table) {
10085 $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
10086 $resql = $this->db->query($sql);
10087 if (!$resql) {
10088 $this->error = $this->db->lasterror();
10089 $this->errors[] = $this->error;
10090 $this->db->rollback();
10091 return -1;
10092 }
10093 }
10094 } elseif (!empty($this->childtables)) { // If object has childs linked with a foreign key field, we check all child tables.
10095 $objectisused = $this->isObjectUsed($this->id);
10096 if (!empty($objectisused)) {
10097 dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
10098 $this->error = 'ErrorRecordHasChildren';
10099 $this->errors[] = $this->error;
10100 $this->db->rollback();
10101 return 0;
10102 }
10103 }
10104
10105 // Delete cascade first
10106 if (is_array($this->childtablesoncascade) && !empty($this->childtablesoncascade)) {
10107 foreach ($this->childtablesoncascade as $table) {
10108 $deleteFromObject = explode(':', $table);
10109 if (count($deleteFromObject) >= 2) {
10110 $className = str_replace('@', '', $deleteFromObject[0]);
10111 $filePath = $deleteFromObject[1];
10112 $columnName = $deleteFromObject[2];
10113 $TMoreSQL = array();
10114 if (!empty($deleteFromObject[3])) {
10115 $TMoreSQL['customsql'] = $deleteFromObject[3];
10116 }
10117 if (dol_include_once($filePath)) {
10118 $childObject = new $className($this->db);
10119 if (method_exists($childObject, 'deleteByParentField')) {
10120 $result = $childObject->deleteByParentField($this->id, $columnName, $TMoreSQL);
10121 if ($result < 0) {
10122 $error++;
10123 $this->errors[] = $childObject->error;
10124 break;
10125 }
10126 } else {
10127 $error++;
10128 $this->errors[] = "You defined a cascade delete on an object $childObject but there is no method deleteByParentField for it";
10129 break;
10130 }
10131 } else {
10132 $error++;
10133 $this->errors[] = 'Cannot include child class file '.$filePath;
10134 break;
10135 }
10136 } else {
10137 // Delete record in child table
10138 $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
10139
10140 $resql = $this->db->query($sql);
10141 if (!$resql) {
10142 $error++;
10143 $this->error = $this->db->lasterror();
10144 $this->errors[] = $this->error;
10145 break;
10146 }
10147 }
10148 }
10149 }
10150
10151 if (!$error) {
10152 if (!$notrigger) {
10153 // Call triggers
10154 $result = $this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
10155 if ($result < 0) {
10156 $error++;
10157 } // Do also here what you must do to rollback action if trigger fail
10158 // End call triggers
10159 }
10160 }
10161
10162 // Delete llx_ecm_files
10163 if (!$error) {
10164 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
10165 if (!$res) {
10166 $error++;
10167 }
10168 }
10169
10170 // Delete linked object
10171 $res = $this->deleteObjectLinked();
10172 if ($res < 0) {
10173 $error++;
10174 }
10175
10176 if (!$error && !empty($this->isextrafieldmanaged)) {
10177 $result = $this->deleteExtraFields();
10178 if ($result < 0) {
10179 $error++;
10180 }
10181 }
10182
10183 if (!$error) {
10184 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.' WHERE rowid='.((int) $this->id);
10185
10186 $resql = $this->db->query($sql);
10187 if (!$resql) {
10188 $error++;
10189 $this->errors[] = $this->db->lasterror();
10190 }
10191 }
10192
10193 // Commit or rollback
10194 if ($error) {
10195 $this->db->rollback();
10196 return -1;
10197 } else {
10198 $this->db->commit();
10199 return 1;
10200 }
10201 }
10202
10213 public function deleteByParentField($parentId = 0, $parentField = '', $filter = array(), $filtermode = "AND")
10214 {
10215 global $user;
10216
10217 $error = 0;
10218 $deleted = 0;
10219
10220 if (!empty($parentId) && !empty($parentField)) {
10221 $this->db->begin();
10222
10223 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
10224 $sql .= " WHERE ".$parentField." = ".(int) $parentId;
10225
10226 // Manage filters
10227 $sqlwhere = array();
10228 if (count($filter) > 0) {
10229 foreach ($filter as $key => $value) {
10230 if ($key == 'customsql') {
10231 $sqlwhere[] = $value;
10232 } elseif (strpos($value, '%') === false) {
10233 $sqlwhere[] = $key." IN (".$this->db->sanitize($this->db->escape($value)).")";
10234 } else {
10235 $sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'";
10236 }
10237 }
10238 }
10239 if (count($sqlwhere) > 0) {
10240 $sql .= " AND (".implode(" ".$filtermode." ", $sqlwhere).")";
10241 }
10242
10243 $resql = $this->db->query($sql);
10244 if (!$resql) {
10245 $this->errors[] = $this->db->lasterror();
10246 $error++;
10247 } else {
10248 while ($obj = $this->db->fetch_object($resql)) {
10249 $result = $this->fetch($obj->rowid); // @phpstan-ignore-line
10250 if ($result < 0) {
10251 $error++;
10252 $this->errors[] = $this->error;
10253 } else {
10254 $result = $this->delete($user); // @phpstan-ignore-line
10255 if ($result < 0) {
10256 $error++;
10257 $this->errors[] = $this->error;
10258 } else {
10259 $deleted++;
10260 }
10261 }
10262 }
10263 }
10264
10265 if (empty($error)) {
10266 $this->db->commit();
10267 return $deleted;
10268 } else {
10269 $this->error = implode(', ', $this->errors);
10270 $this->db->rollback();
10271 return $error * -1;
10272 }
10273 }
10274
10275 return $deleted;
10276 }
10277
10286 public function deleteLineCommon(User $user, $idline, $notrigger = false)
10287 {
10288 global $conf;
10289
10290 $error = 0;
10291
10292 $tmpforobjectclass = get_class($this);
10293 $tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line';
10294
10295 $this->db->begin();
10296
10297 // Call trigger
10298 $result = $this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user);
10299 if ($result < 0) {
10300 $error++;
10301 }
10302 // End call triggers
10303
10304 if (empty($error)) {
10305 $sql = "DELETE FROM ".$this->db->prefix().$this->table_element_line;
10306 $sql .= " WHERE rowid = ".((int) $idline);
10307
10308 $resql = $this->db->query($sql);
10309 if (!$resql) {
10310 $this->error = "Error ".$this->db->lasterror();
10311 $error++;
10312 }
10313 }
10314
10315 if (empty($error)) {
10316 // Remove extrafields
10317 $tmpobjectline = new $tmpforobjectlineclass($this->db);
10318 if (!isset($tmpobjectline->isextrafieldmanaged) || !empty($tmpobjectline->isextrafieldmanaged)) {
10319 $tmpobjectline->id = $idline;
10320 $result = $tmpobjectline->deleteExtraFields();
10321 if ($result < 0) {
10322 $error++;
10323 $this->error = "Error ".get_class($this)."::deleteLineCommon deleteExtraFields error -4 ".$tmpobjectline->error;
10324 }
10325 }
10326 }
10327
10328 if (empty($error)) {
10329 $this->db->commit();
10330 return 1;
10331 } else {
10332 dol_syslog(get_class($this)."::deleteLineCommon ERROR:".$this->error, LOG_ERR);
10333 $this->db->rollback();
10334 return -1;
10335 }
10336 }
10337
10338
10348 public function setStatusCommon($user, $status, $notrigger = 0, $triggercode = '')
10349 {
10350 $error = 0;
10351
10352 $this->db->begin();
10353
10354 $statusfield = 'status';
10355 if (in_array($this->element, array('don', 'donation', 'shipping'))) {
10356 $statusfield = 'fk_statut';
10357 }
10358
10359 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
10360 $sql .= " SET ".$statusfield." = ".((int) $status);
10361 $sql .= " WHERE rowid = ".((int) $this->id);
10362
10363 if ($this->db->query($sql)) {
10364 if (!$error) {
10365 $this->oldcopy = clone $this;
10366 }
10367
10368 if (!$error && !$notrigger) {
10369 // Call trigger
10370 $result = $this->call_trigger($triggercode, $user);
10371 if ($result < 0) {
10372 $error++;
10373 }
10374 }
10375
10376 if (!$error) {
10377 $this->status = $status;
10378 $this->db->commit();
10379 return 1;
10380 } else {
10381 $this->db->rollback();
10382 return -1;
10383 }
10384 } else {
10385 $this->error = $this->db->error();
10386 $this->db->rollback();
10387 return -1;
10388 }
10389 }
10390
10391
10398 public function initAsSpecimenCommon()
10399 {
10400 global $user;
10401
10402 $this->id = 0;
10403 $this->specimen = 1;
10404 $fields = array(
10405 'label' => 'This is label',
10406 'ref' => 'ABCD1234',
10407 'description' => 'This is a description',
10408 'qty' => 123.12,
10409 'note_public' => 'Public note',
10410 'note_private' => 'Private note',
10411 'date_creation' => (dol_now() - 3600 * 48),
10412 'date_modification' => (dol_now() - 3600 * 24),
10413 'fk_user_creat' => $user->id,
10414 'fk_user_modif' => $user->id,
10415 'date' => dol_now(),
10416 );
10417 foreach ($fields as $key => $value) {
10418 if (array_key_exists($key, $this->fields)) {
10419 $this->{$key} = $value; // @phpstan-ignore-line
10420 }
10421 }
10422
10423 // Force values to default values when known
10424 if (property_exists($this, 'fields')) {
10425 foreach ($this->fields as $key => $value) {
10426 // If fields are already set, do nothing
10427 if (array_key_exists($key, $fields)) {
10428 continue;
10429 }
10430
10431 if (!empty($value['default'])) {
10432 $this->$key = $value['default'];
10433 }
10434 }
10435 }
10436
10437 return 1;
10438 }
10439
10440
10441 /* Part for comments */
10442
10447 public function fetchComments()
10448 {
10449 require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
10450
10451 $comment = new Comment($this->db);
10452 $result = $comment->fetchAllFor($this->element, $this->id);
10453 if ($result < 0) {
10454 $this->errors = array_merge($this->errors, $comment->errors);
10455 return -1;
10456 } else {
10457 $this->comments = $comment->comments;
10458 }
10459 return count($this->comments);
10460 }
10461
10467 public function getNbComments()
10468 {
10469 return count($this->comments);
10470 }
10471
10478 public function trimParameters($parameters)
10479 {
10480 if (!is_array($parameters)) {
10481 return;
10482 }
10483 foreach ($parameters as $parameter) {
10484 if (isset($this->$parameter)) {
10485 $this->$parameter = trim($this->$parameter);
10486 }
10487 }
10488 }
10489
10490 /* Part for categories/tags */
10491
10502 public function getCategoriesCommon($type_categ)
10503 {
10504 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10505
10506 // Get current categories
10507 $c = new Categorie($this->db);
10508 $existing = $c->containing($this->id, $type_categ, 'id');
10509
10510 return $existing;
10511 }
10512
10525 public function setCategoriesCommon($categories, $type_categ = '', $remove_existing = true)
10526 {
10527 // Handle single category
10528 if (!is_array($categories)) {
10529 $categories = array($categories);
10530 }
10531
10532 dol_syslog(get_class($this)."::setCategoriesCommon Oject Id:".$this->id.' type_categ:'.$type_categ.' nb tag add:'.count($categories), LOG_DEBUG);
10533
10534 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10535
10536 if (empty($type_categ)) {
10537 dol_syslog(__METHOD__.': Type '.$type_categ.'is an unknown category type. Done nothing.', LOG_ERR);
10538 return -1;
10539 }
10540
10541 // Get current categories
10542 $c = new Categorie($this->db);
10543 $existing = $c->containing($this->id, $type_categ, 'id');
10544 if ($remove_existing) {
10545 // Diff
10546 if (is_array($existing)) {
10547 $to_del = array_diff($existing, $categories);
10548 $to_add = array_diff($categories, $existing);
10549 } else {
10550 $to_del = array(); // Nothing to delete
10551 $to_add = $categories;
10552 }
10553 } else {
10554 $to_del = array(); // Nothing to delete
10555 $to_add = array_diff($categories, $existing);
10556 }
10557
10558 $error = 0;
10559 $ok = 0;
10560
10561 // Process
10562 foreach ($to_del as $del) {
10563 if ($c->fetch($del) > 0) {
10564 $result=$c->del_type($this, $type_categ);
10565 if ($result < 0) {
10566 $error++;
10567 $this->error = $c->error;
10568 $this->errors = $c->errors;
10569 break;
10570 } else {
10571 $ok += $result;
10572 }
10573 }
10574 }
10575 foreach ($to_add as $add) {
10576 if ($c->fetch($add) > 0) {
10577 $result = $c->add_type($this, $type_categ);
10578 if ($result < 0) {
10579 $error++;
10580 $this->error = $c->error;
10581 $this->errors = $c->errors;
10582 break;
10583 } else {
10584 $ok += $result;
10585 }
10586 }
10587 }
10588
10589 return $error ? (-1 * $error) : $ok;
10590 }
10591
10600 public function cloneCategories($fromId, $toId, $type = '')
10601 {
10602 $this->db->begin();
10603
10604 if (empty($type)) {
10605 $type = $this->table_element;
10606 }
10607
10608 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10609 $categorystatic = new Categorie($this->db);
10610
10611 $sql = "INSERT INTO ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type])." (fk_categorie, fk_product)";
10612 $sql .= " SELECT fk_categorie, $toId FROM ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type]);
10613 $sql .= " WHERE fk_product = ".((int) $fromId);
10614
10615 if (!$this->db->query($sql)) {
10616 $this->error = $this->db->lasterror();
10617 $this->db->rollback();
10618 return -1;
10619 }
10620
10621 $this->db->commit();
10622 return 1;
10623 }
10624
10631 public function deleteEcmFiles($mode = 0)
10632 {
10633 global $conf;
10634
10635 $this->db->begin();
10636
10637 // Delete in database with mode 0
10638 if ($mode == 0) {
10639 switch ($this->element) {
10640 case 'propal':
10641 $element = 'propale';
10642 break;
10643 case 'product':
10644 $element = 'produit';
10645 break;
10646 case 'order_supplier':
10647 $element = 'fournisseur/commande';
10648 break;
10649 case 'invoice_supplier':
10650 // Special cases that need to use get_exdir to get real dir of object
10651 // In future, all object should use this to define path of documents.
10652 $element = 'fournisseur/facture/'.get_exdir($this->id, 2, 0, 1, $this, 'invoice_supplier');
10653 break;
10654 case 'shipping':
10655 $element = 'expedition/sending';
10656 break;
10657 case 'task':
10658 case 'project_task':
10659 require_once DOL_DOCUMENT_ROOT.'/projet/class/task.class.php';
10660
10661 $project_result = $this->fetch_projet();
10662 if ($project_result >= 0) {
10663 $element = 'projet/'.dol_sanitizeFileName($this->project->ref).'/';
10664 }
10665 // no break
10666 default:
10667 $element = $this->element;
10668 }
10669
10670 // Delete ecm_files_extrafields with mode 0 (using name)
10671 $sql = "DELETE FROM ".$this->db->prefix()."ecm_files_extrafields WHERE fk_object IN (";
10672 $sql .= " SELECT rowid FROM ".$this->db->prefix()."ecm_files WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
10673 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
10674 $sql .= ")";
10675
10676 if (!$this->db->query($sql)) {
10677 $this->error = $this->db->lasterror();
10678 $this->db->rollback();
10679 return false;
10680 }
10681
10682 // Delete ecm_files with mode 0 (using name)
10683 $sql = "DELETE FROM ".$this->db->prefix()."ecm_files";
10684 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
10685 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
10686
10687 if (!$this->db->query($sql)) {
10688 $this->error = $this->db->lasterror();
10689 $this->db->rollback();
10690 return false;
10691 }
10692 }
10693
10694 // Delete in database with mode 1
10695 if ($mode == 1) {
10696 $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files_extrafields";
10697 $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).")";
10698 $resql = $this->db->query($sql);
10699 if (!$resql) {
10700 $this->error = $this->db->lasterror();
10701 $this->db->rollback();
10702 return false;
10703 }
10704
10705 $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files";
10706 $sql .= " WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? "" : "@".$this->module))."' AND src_object_id = ".((int) $this->id);
10707 $resql = $this->db->query($sql);
10708 if (!$resql) {
10709 $this->error = $this->db->lasterror();
10710 $this->db->rollback();
10711 return false;
10712 }
10713 }
10714
10715 $this->db->commit();
10716 return true;
10717 }
10718}
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