dolibarr 18.0.6
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;
173 public $oldref;
174
178 protected $table_ref_field = '';
179
183 public $restrictiononfksoc = 0;
184
185
186 // Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them.
187
191 public $context = array();
192
196 public $canvas;
197
202 public $project;
203
208 public $fk_project;
209
215 public $projet;
216
222
227 public $contact;
228
233 public $contact_id;
234
239 public $thirdparty;
240
245 public $user;
246
251 public $origin;
252
257 public $origin_id;
258
262 public $ref;
263
267 public $ref_ext;
268
272 public $ref_previous;
273
277 public $ref_next;
278
282 public $newref;
283
288 public $statut;
289
294 public $status;
295
296
301 public $country;
302
307 public $country_id;
308
313 public $country_code;
314
319 public $state;
320
325 public $state_id;
326
331 public $state_code;
332
337 public $region_id;
338
343 public $region_code;
344
349 public $region;
350
351
356 public $barcode_type;
357
362 public $barcode_type_code;
363
368 public $barcode_type_label;
369
374 public $barcode_type_coder;
375
380 public $mode_reglement_id;
381
386 public $cond_reglement_id;
387
391 public $demand_reason_id;
392
397 public $transport_mode_id;
398
404 public $cond_reglement;
405
411 public $fk_delivery_address;
412
417 public $shipping_method_id;
418
423 public $shipping_method;
424
428 public $multicurrency_code;
429
433 public $multicurrency_tx;
434
439 public $model_pdf;
440
446 public $modelpdf;
447
452 public $last_main_doc;
453
459 public $fk_bank;
460
465 public $fk_account;
466
470 public $openid;
471
476 public $note_public;
477
482 public $note_private;
483
488 public $note;
489
494 public $total_ht;
495
500 public $total_tva;
501
506 public $total_localtax1;
507
512 public $total_localtax2;
513
518 public $total_ttc;
519
523 public $lines;
524
529 public $comments = array();
530
534 public $name;
535
539 public $lastname;
540
544 public $firstname;
545
549 public $civility_id;
550
551 // Dates
555 public $date_creation;
556
560 public $date_validation; // Date validation
561
565 public $date_modification; // Date last change (tms field)
570 public $date_update;
571
575 public $date_cloture; // Date closing (tms field)
576
581 public $user_author;
586 public $user_creation;
590 public $user_creation_id;
591
596 public $user_valid;
601 public $user_validation;
605 public $user_validation_id;
609 public $user_closing_id;
610
615 public $user_modification;
619 public $user_modification_id;
620
621
626 public $fk_user_creat;
627
632 public $fk_user_modif;
633
634
635 public $next_prev_filter;
636
640 public $specimen = 0;
641
645 public $sendtoid;
646
650 public $alreadypaid;
651
652
653 public $labelStatus;
654 protected $labelStatusShort;
655
659 public $showphoto_on_popup;
660
664 public $nb = array();
665
669 public $output;
670
674 public $extraparams = array();
675
679 protected $childtables = array();
680
686 protected $childtablesoncascade = array();
687
688
689 // No constructor as it is an abstract class
690
691
702 public static function isExistingObject($element, $id, $ref = '', $ref_ext = '')
703 {
704 global $db, $conf;
705
706 $sql = "SELECT rowid, ref, ref_ext";
707 $sql .= " FROM ".$db->prefix().$element;
708 $sql .= " WHERE entity IN (".getEntity($element).")";
709
710 if ($id > 0) {
711 $sql .= " AND rowid = ".((int) $id);
712 } elseif ($ref) {
713 $sql .= " AND ref = '".$db->escape($ref)."'";
714 } elseif ($ref_ext) {
715 $sql .= " AND ref_ext = '".$db->escape($ref_ext)."'";
716 } else {
717 $error = 'ErrorWrongParameters';
718 dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
719 return -1;
720 }
721 if ($ref || $ref_ext) { // Because the same ref can exists in 2 different entities, we force the current one in priority
722 $sql .= " AND entity = ".((int) $conf->entity);
723 }
724
725 dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
726 $resql = $db->query($sql);
727 if ($resql) {
728 $num = $db->num_rows($resql);
729 if ($num > 0) {
730 return 1;
731 } else {
732 return 0;
733 }
734 }
735 return -1;
736 }
737
744 public function setErrorsFromObject($object)
745 {
746 if (!empty($object->error)) {
747 $this->error = $object->error;
748 }
749 if (!empty($object->errors)) {
750 $this->errors = array_merge($this->errors, $object->errors);
751 }
752 }
753
761 public function getTooltipContentArray($params)
762 {
763 return [];
764 }
765
773 public function getTooltipContent($params)
774 {
775 global $action, $extrafields, $langs, $hookmanager;
776
777 // If there is too much extrafields, we do not include them into tooltip
778 $MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP = getDolGlobalInt('MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP', 3);
779
780 $datas = $this->getTooltipContentArray($params);
781 $count = 0;
782
783 // Add extrafields
784 if (!empty($extrafields->attributes[$this->table_element]['label'])) {
785 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
786 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
787 continue;
788 }
789 if ($count >= abs($MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP)) {
790 $datas['more_extrafields'] = '<br>...';
791 break;
792 }
793 $enabled = 1;
794 if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
795 $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
796 }
797 if ($enabled && isset($extrafields->attributes[$this->table_element]['list'][$key])) {
798 $enabled = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
799 }
800 $perms = 1;
801 if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
802 $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
803 }
804 if (empty($enabled)) {
805 continue; // 0 = Never visible field
806 }
807 if (abs($enabled) != 1 && abs($enabled) != 3 && abs($enabled) != 5 && abs($enabled) != 4) {
808 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list <> 4 = not visible at the creation
809 }
810 if (empty($perms)) {
811 continue; // 0 = Not visible
812 }
813 if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
814 $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
815 }
816 $labelextra = $langs->trans((string) $extrafields->attributes[$this->table_element]['label'][$key]);
817 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
818 $datas[$key]= '<br><b><u>'. $labelextra . '</u></b>';
819 } else {
820 $value = (empty($this->array_options['options_' . $key]) ? '' : $this->array_options['options_' . $key]);
821 $datas[$key]= '<br><b>'. $labelextra . ':</b> ' . $extrafields->showOutputField($key, $value, '', $this->table_element);
822 $count++;
823 }
824 }
825 }
826
827 $hookmanager->initHooks(array($this->element . 'dao'));
828 $parameters = array(
829 'tooltipcontentarray' => &$datas,
830 'params' => $params,
831 );
832 // Note that $action and $object may have been modified by some hooks
833 $hookmanager->executeHooks('getTooltipContent', $parameters, $this, $action);
834
835 //var_dump($datas);
836 $label = implode($datas);
837
838 return $label;
839 }
840
841
847 public function errorsToString()
848 {
849 return $this->error.(is_array($this->errors) ? (($this->error != '' ? ', ' : '').join(', ', $this->errors)) : '');
850 }
851
852
859 public function getFormatedCustomerRef($objref)
860 {
861 global $hookmanager;
862
863 $parameters = array('objref'=>$objref);
864 $action = '';
865 $reshook = $hookmanager->executeHooks('getFormatedCustomerRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
866 if ($reshook > 0) {
867 return $hookmanager->resArray['objref'];
868 }
869 return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
870 }
871
878 public function getFormatedSupplierRef($objref)
879 {
880 global $hookmanager;
881
882 $parameters = array('objref'=>$objref);
883 $action = '';
884 $reshook = $hookmanager->executeHooks('getFormatedSupplierRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
885 if ($reshook > 0) {
886 return $hookmanager->resArray['objref'];
887 }
888 return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
889 }
890
900 public function getFullAddress($withcountry = 0, $sep = "\n", $withregion = 0, $extralangcode = '')
901 {
902 if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country))) {
903 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
904 $tmparray = getCountry($this->country_id, 'all');
905 $this->country_code = $tmparray['code'];
906 $this->country = $tmparray['label'];
907 }
908
909 if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_code))) {
910 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
911 $tmparray = getState($this->state_id, 'all', 0, 1);
912 $this->state_code = $tmparray['code'];
913 $this->state = $tmparray['label'];
914 $this->region_code = $tmparray['region_code'];
915 $this->region = $tmparray['region'];
916 }
917
918 return dol_format_address($this, $withcountry, $sep, '', 0, $extralangcode);
919 }
920
921
930 public function getLastMainDocLink($modulepart, $initsharekey = 0, $relativelink = 0)
931 {
932 global $user, $dolibarr_main_url_root;
933
934 if (empty($this->last_main_doc)) {
935 return ''; // No way to known which document name to use
936 }
937
938 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
939 $ecmfile = new EcmFiles($this->db);
940 $result = $ecmfile->fetch(0, '', $this->last_main_doc);
941 if ($result < 0) {
942 $this->error = $ecmfile->error;
943 $this->errors = $ecmfile->errors;
944 return -1;
945 }
946
947 if (empty($ecmfile->id)) {
948 // Add entry into index
949 if ($initsharekey) {
950 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
951
952 // 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
953 /*
954 $ecmfile->filepath = $rel_dir;
955 $ecmfile->filename = $filename;
956 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
957 $ecmfile->fullpath_orig = '';
958 $ecmfile->gen_or_uploaded = 'generated';
959 $ecmfile->description = ''; // indexed content
960 $ecmfile->keywords = ''; // keyword content
961 $ecmfile->share = getRandomPassword(true);
962 $result = $ecmfile->create($user);
963 if ($result < 0)
964 {
965 $this->error = $ecmfile->error;
966 $this->errors = $ecmfile->errors;
967 }
968 */
969 } else {
970 return '';
971 }
972 } elseif (empty($ecmfile->share)) {
973 // Add entry into index
974 if ($initsharekey) {
975 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
976 $ecmfile->share = getRandomPassword(true);
977 $ecmfile->update($user);
978 } else {
979 return '';
980 }
981 }
982 // Define $urlwithroot
983 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
984 // This is to use external domain name found into config file
985 //if (DOL_URL_ROOT && ! preg_match('/\/$/', $urlwithouturlroot) && ! preg_match('/^\//', DOL_URL_ROOT)) $urlwithroot=$urlwithouturlroot.'/'.DOL_URL_ROOT;
986 //else
987 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT;
988 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
989
990 $forcedownload = 0;
991
992 $paramlink = '';
993 //if (!empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart; // For sharing with hash (so public files), modulepart is not required.
994 //if (!empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; // For sharing with hash (so public files), entity is not required.
995 //$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath); // No need of name of file for public link, we will use the hash
996 if (!empty($ecmfile->share)) {
997 $paramlink .= ($paramlink ? '&' : '').'hashp='.$ecmfile->share; // Hash for public share
998 }
999 if ($forcedownload) {
1000 $paramlink .= ($paramlink ? '&' : '').'attachment=1';
1001 }
1002
1003 if ($relativelink) {
1004 $linktoreturn = 'document.php'.($paramlink ? '?'.$paramlink : '');
1005 } else {
1006 $linktoreturn = $urlwithroot.'/document.php'.($paramlink ? '?'.$paramlink : '');
1007 }
1008
1009 // Here $ecmfile->share is defined
1010 return $linktoreturn;
1011 }
1012
1013
1014 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1024 public function add_contact($fk_socpeople, $type_contact, $source = 'external', $notrigger = 0)
1025 {
1026 // phpcs:enable
1027 global $user, $langs;
1028
1029
1030 dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
1031
1032 // Check parameters
1033 if ($fk_socpeople <= 0) {
1034 $langs->load("errors");
1035 $this->error = $langs->trans("ErrorWrongValueForParameterX", "1");
1036 dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
1037 return -1;
1038 }
1039 if (!$type_contact) {
1040 $langs->load("errors");
1041 $this->error = $langs->trans("ErrorWrongValueForParameterX", "2");
1042 dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
1043 return -2;
1044 }
1045
1046 $id_type_contact = 0;
1047 if (is_numeric($type_contact)) {
1048 $id_type_contact = $type_contact;
1049 } else {
1050 // We look for id type_contact
1051 $sql = "SELECT tc.rowid";
1052 $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1053 $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1054 $sql .= " AND tc.source='".$this->db->escape($source)."'";
1055 $sql .= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
1056 //print $sql;
1057 $resql = $this->db->query($sql);
1058 if ($resql) {
1059 $obj = $this->db->fetch_object($resql);
1060 if ($obj) {
1061 $id_type_contact = $obj->rowid;
1062 }
1063 }
1064 }
1065
1066 if ($id_type_contact == 0) {
1067 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");
1068 return 0;
1069 }
1070
1071 $datecreate = dol_now();
1072
1073 // Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
1074 $TListeContacts = $this->liste_contact(-1, $source);
1075 $already_added = false;
1076 if (is_array($TListeContacts) && !empty($TListeContacts)) {
1077 foreach ($TListeContacts as $array_contact) {
1078 if ($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
1079 $already_added = true;
1080 break;
1081 }
1082 }
1083 }
1084
1085 if (!$already_added) {
1086 $this->db->begin();
1087
1088 // Insert into database
1089 $sql = "INSERT INTO ".$this->db->prefix()."element_contact";
1090 $sql .= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
1091 $sql .= " VALUES (".$this->id.", ".((int) $fk_socpeople)." , ";
1092 $sql .= "'".$this->db->idate($datecreate)."'";
1093 $sql .= ", 4, ".((int) $id_type_contact);
1094 $sql .= ")";
1095
1096 $resql = $this->db->query($sql);
1097 if ($resql) {
1098 if (!$notrigger) {
1099 $result = $this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
1100 if ($result < 0) {
1101 $this->db->rollback();
1102 return -1;
1103 }
1104 }
1105
1106 $this->db->commit();
1107 return 1;
1108 } else {
1109 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1110 $this->error = $this->db->errno();
1111 $this->db->rollback();
1112 return -2;
1113 } else {
1114 $this->error = $this->db->lasterror();
1115 $this->db->rollback();
1116 return -1;
1117 }
1118 }
1119 } else {
1120 return 0;
1121 }
1122 }
1123
1124 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1132 public function copy_linked_contact($objFrom, $source = 'internal')
1133 {
1134 // phpcs:enable
1135 $contacts = $objFrom->liste_contact(-1, $source);
1136 foreach ($contacts as $contact) {
1137 if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0) {
1138 return -1;
1139 }
1140 }
1141 return 1;
1142 }
1143
1144 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1154 public function update_contact($rowid, $statut, $type_contact_id = 0, $fk_socpeople = 0)
1155 {
1156 // phpcs:enable
1157 // Insert into database
1158 $sql = "UPDATE ".$this->db->prefix()."element_contact set";
1159 $sql .= " statut = ".$statut;
1160 if ($type_contact_id) {
1161 $sql .= ", fk_c_type_contact = ".((int) $type_contact_id);
1162 }
1163 if ($fk_socpeople) {
1164 $sql .= ", fk_socpeople = ".((int) $fk_socpeople);
1165 }
1166 $sql .= " where rowid = ".((int) $rowid);
1167 $resql = $this->db->query($sql);
1168 if ($resql) {
1169 return 0;
1170 } else {
1171 $this->error = $this->db->lasterror();
1172 return -1;
1173 }
1174 }
1175
1176 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1184 public function delete_contact($rowid, $notrigger = 0)
1185 {
1186 // phpcs:enable
1187 global $user;
1188
1189 $error = 0;
1190
1191 $this->db->begin();
1192
1193 if (!$error && empty($notrigger)) {
1194 // Call trigger
1195 $this->context['contact_id'] = ((int) $rowid);
1196 $result = $this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
1197 if ($result < 0) {
1198 $error++;
1199 }
1200 // End call triggers
1201 }
1202
1203 if (!$error) {
1204 dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
1205
1206 $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
1207 $sql .= " WHERE rowid = ".((int) $rowid);
1208
1209 $result = $this->db->query($sql);
1210 if (!$result) {
1211 $error++;
1212 $this->errors[] = $this->db->lasterror();
1213 }
1214 }
1215
1216 if (!$error) {
1217 $this->db->commit();
1218 return 1;
1219 } else {
1220 $this->error = $this->db->lasterror();
1221 $this->db->rollback();
1222 return -1;
1223 }
1224 }
1225
1226 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1234 public function delete_linked_contact($source = '', $code = '')
1235 {
1236 // phpcs:enable
1237 $listId = '';
1238 $temp = array();
1239 $typeContact = $this->liste_type_contact($source, '', 0, 0, $code);
1240
1241 if (!empty($typeContact)) {
1242 foreach ($typeContact as $key => $value) {
1243 array_push($temp, $key);
1244 }
1245 $listId = implode(",", $temp);
1246 }
1247
1248 // If $listId is empty, we have not criteria on fk_c_type_contact so we will delete record on element_id for
1249 // any type or record instead of only the ones of the current object. So we do nothing in such a case.
1250 if (empty($listId)) {
1251 return 0;
1252 }
1253
1254 $sql = "DELETE FROM ".$this->db->prefix()."element_contact";
1255 $sql .= " WHERE element_id = ".((int) $this->id);
1256 $sql .= " AND fk_c_type_contact IN (".$this->db->sanitize($listId).")";
1257
1258 dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
1259 if ($this->db->query($sql)) {
1260 return 1;
1261 } else {
1262 $this->error = $this->db->lasterror();
1263 return -1;
1264 }
1265 }
1266
1267 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1279 public function liste_contact($statusoflink = -1, $source = 'external', $list = 0, $code = '', $status = -1, $arrayoftcids = array())
1280 {
1281 // phpcs:enable
1282 global $langs;
1283
1284 $tab = array();
1285
1286 $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
1287 if ($source == 'internal') {
1288 $sql .= ", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
1289 }
1290 if ($source == 'external' || $source == 'thirdparty') {
1291 $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
1292 }
1293 $sql .= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
1294 $sql .= ", tc.source, tc.element, tc.code, tc.libelle";
1295 $sql .= " FROM ".$this->db->prefix()."c_type_contact tc,";
1296 $sql .= " ".$this->db->prefix()."element_contact ec";
1297 if ($source == 'internal') { // internal contact (user)
1298 $sql .= " LEFT JOIN ".$this->db->prefix()."user t on ec.fk_socpeople = t.rowid";
1299 }
1300 if ($source == 'external' || $source == 'thirdparty') { // external contact (socpeople)
1301 $sql .= " LEFT JOIN ".$this->db->prefix()."socpeople t on ec.fk_socpeople = t.rowid";
1302 }
1303 $sql .= " WHERE ec.element_id = ".((int) $this->id);
1304 $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1305 $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1306 if ($code) {
1307 $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1308 }
1309 if ($source == 'internal') {
1310 $sql .= " AND tc.source = 'internal'";
1311 if ($status >= 0) {
1312 $sql .= " AND t.statut = ".((int) $status);
1313 }
1314 }
1315 if ($source == 'external' || $source == 'thirdparty') {
1316 $sql .= " AND tc.source = 'external'";
1317 if ($status >= 0) {
1318 $sql .= " AND t.statut = ".((int) $status); // t is llx_socpeople
1319 }
1320 }
1321 $sql .= " AND tc.active = 1";
1322 if ($statusoflink >= 0) {
1323 $sql .= " AND ec.statut = ".((int) $statusoflink);
1324 }
1325 $sql .= " ORDER BY t.lastname ASC";
1326
1327 dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
1328 $resql = $this->db->query($sql);
1329 if ($resql) {
1330 $num = $this->db->num_rows($resql);
1331 $i = 0;
1332 while ($i < $num) {
1333 $obj = $this->db->fetch_object($resql);
1334
1335 if (!$list) {
1336 $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
1337 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1338 $tab[$i] = array(
1339 'parentId' => $this->id,
1340 'source' => $obj->source,
1341 'socid' => $obj->socid,
1342 'id' => $obj->id,
1343 'nom' => $obj->lastname, // For backward compatibility
1344 'civility' => $obj->civility,
1345 'lastname' => $obj->lastname,
1346 'firstname' => $obj->firstname,
1347 'email'=>$obj->email,
1348 'login'=> (empty($obj->login) ? '' : $obj->login),
1349 'photo' => (empty($obj->photo) ? '' : $obj->photo),
1350 'statuscontact' => $obj->statuscontact,
1351 'rowid' => $obj->rowid,
1352 'code' => $obj->code,
1353 'libelle' => $libelle_type,
1354 'status' => $obj->statuslink,
1355 'fk_c_type_contact' => $obj->fk_c_type_contact
1356 );
1357 } else {
1358 $tab[$i] = $obj->id;
1359 }
1360
1361 $i++;
1362 }
1363
1364 return $tab;
1365 } else {
1366 $this->error = $this->db->lasterror();
1367 dol_print_error($this->db);
1368 return -1;
1369 }
1370 }
1371
1372
1379 public function swapContactStatus($rowid)
1380 {
1381 $sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1382 $sql .= " tc.code, tc.libelle";
1383 $sql .= " FROM (".$this->db->prefix()."element_contact as ec, ".$this->db->prefix()."c_type_contact as tc)";
1384 $sql .= " WHERE ec.rowid =".((int) $rowid);
1385 $sql .= " AND ec.fk_c_type_contact=tc.rowid";
1386 $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1387
1388 dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1389 $resql = $this->db->query($sql);
1390 if ($resql) {
1391 $obj = $this->db->fetch_object($resql);
1392 $newstatut = ($obj->statut == 4) ? 5 : 4;
1393 $result = $this->update_contact($rowid, $newstatut);
1394 $this->db->free($resql);
1395 return $result;
1396 } else {
1397 $this->error = $this->db->error();
1398 dol_print_error($this->db);
1399 return -1;
1400 }
1401 }
1402
1403 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1414 public function liste_type_contact($source = 'internal', $order = 'position', $option = 0, $activeonly = 0, $code = '')
1415 {
1416 // phpcs:enable
1417 global $langs;
1418
1419 if (empty($order)) {
1420 $order = 'position';
1421 }
1422 if ($order == 'position') {
1423 $order .= ',code';
1424 }
1425
1426 $tab = array();
1427 $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position";
1428 $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1429 $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1430 if ($activeonly == 1) {
1431 $sql .= " AND tc.active=1"; // only the active types
1432 }
1433 if (!empty($source) && $source != 'all') {
1434 $sql .= " AND tc.source='".$this->db->escape($source)."'";
1435 }
1436 if (!empty($code)) {
1437 $sql .= " AND tc.code='".$this->db->escape($code)."'";
1438 }
1439 $sql .= $this->db->order($order, 'ASC');
1440
1441 //print "sql=".$sql;
1442 $resql = $this->db->query($sql);
1443 if ($resql) {
1444 $num = $this->db->num_rows($resql);
1445 $i = 0;
1446 while ($i < $num) {
1447 $obj = $this->db->fetch_object($resql);
1448
1449 $transkey = "TypeContact_".$this->element."_".$source."_".$obj->code;
1450 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1451 if (empty($option)) {
1452 $tab[$obj->rowid] = $libelle_type;
1453 } else {
1454 $tab[$obj->code] = $libelle_type;
1455 }
1456 $i++;
1457 }
1458 return $tab;
1459 } else {
1460 $this->error = $this->db->lasterror();
1461 //dol_print_error($this->db);
1462 return null;
1463 }
1464 }
1465
1477 public function listeTypeContacts($source = 'internal', $option = 0, $activeonly = 0, $code = '', $element = '', $excludeelement = '')
1478 {
1479 global $langs, $conf;
1480
1481 $langs->loadLangs(array('bills', 'contracts', 'interventions', 'orders', 'projects', 'propal', 'ticket', 'agenda'));
1482
1483 $tab = array();
1484
1485 $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position, tc.element";
1486 $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1487
1488 $sqlWhere = array();
1489 if (!empty($element)) {
1490 $sqlWhere[] = " tc.element='".$this->db->escape($element)."'";
1491 }
1492 if (!empty($excludeelement)) {
1493 $sqlWhere[] = " tc.element <> '".$this->db->escape($excludeelement)."'";
1494 }
1495
1496 if ($activeonly == 1) {
1497 $sqlWhere[] = " tc.active=1"; // only the active types
1498 }
1499
1500 if (!empty($source) && $source != 'all') {
1501 $sqlWhere[] = " tc.source='".$this->db->escape($source)."'";
1502 }
1503
1504 if (!empty($code)) {
1505 $sqlWhere[] = " tc.code='".$this->db->escape($code)."'";
1506 }
1507
1508 if (count($sqlWhere) > 0) {
1509 $sql .= " WHERE ".implode(' AND ', $sqlWhere);
1510 }
1511
1512 $sql .= $this->db->order('tc.element, tc.position', 'ASC');
1513
1514 dol_syslog(__METHOD__, LOG_DEBUG);
1515 $resql = $this->db->query($sql);
1516 if ($resql) {
1517 $num = $this->db->num_rows($resql);
1518 if ($num > 0) {
1519 $langs->loadLangs(array("propal", "orders", "bills", "suppliers", "contracts", "supplier_proposal"));
1520
1521 while ($obj = $this->db->fetch_object($resql)) {
1522 $modulename = $obj->element;
1523 if (strpos($obj->element, 'project') !== false) {
1524 $modulename = 'projet';
1525 } elseif ($obj->element == 'contrat') {
1526 $element = 'contract';
1527 } elseif ($obj->element == 'action') {
1528 $modulename = 'agenda';
1529 } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1530 $modulename = 'fournisseur';
1531 } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1532 $modulename = 'fournisseur';
1533 }
1534 if (!empty($conf->{$modulename}->enabled)) {
1535 $libelle_element = $langs->trans('ContactDefault_'.$obj->element);
1536 $tmpelement = $obj->element;
1537 $transkey = "TypeContact_".$tmpelement."_".$source."_".$obj->code;
1538 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1539 if (empty($option)) {
1540 $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1541 } else {
1542 $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1543 }
1544 }
1545 }
1546 }
1547 return $tab;
1548 } else {
1549 $this->error = $this->db->lasterror();
1550 return null;
1551 }
1552 }
1553
1565 public function getIdContact($source, $code, $status = 0)
1566 {
1567 global $conf;
1568
1569 $result = array();
1570 $i = 0;
1571 //cas particulier pour les expeditions
1572 if ($this->element == 'shipping' && $this->origin_id != 0) {
1573 $id = $this->origin_id;
1574 $element = 'commande';
1575 } elseif ($this->element == 'reception' && $this->origin_id != 0) {
1576 $id = $this->origin_id;
1577 $element = 'order_supplier';
1578 } else {
1579 $id = $this->id;
1580 $element = $this->element;
1581 }
1582
1583 $sql = "SELECT ec.fk_socpeople";
1584 $sql .= " FROM ".$this->db->prefix()."element_contact as ec,";
1585 if ($source == 'internal') {
1586 $sql .= " ".$this->db->prefix()."user as c,";
1587 }
1588 if ($source == 'external') {
1589 $sql .= " ".$this->db->prefix()."socpeople as c,";
1590 }
1591 $sql .= " ".$this->db->prefix()."c_type_contact as tc";
1592 $sql .= " WHERE ec.element_id = ".((int) $id);
1593 $sql .= " AND ec.fk_socpeople = c.rowid";
1594 if ($source == 'internal') {
1595 $sql .= " AND c.entity IN (".getEntity('user').")";
1596 }
1597 if ($source == 'external') {
1598 $sql .= " AND c.entity IN (".getEntity('societe').")";
1599 }
1600 $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1601 $sql .= " AND tc.element = '".$this->db->escape($element)."'";
1602 $sql .= " AND tc.source = '".$this->db->escape($source)."'";
1603 if ($code) {
1604 $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1605 }
1606 $sql .= " AND tc.active = 1";
1607 if ($status) {
1608 $sql .= " AND ec.statut = ".((int) $status);
1609 }
1610
1611 dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1612 $resql = $this->db->query($sql);
1613 if ($resql) {
1614 while ($obj = $this->db->fetch_object($resql)) {
1615 $result[$i] = $obj->fk_socpeople;
1616 $i++;
1617 }
1618 } else {
1619 $this->error = $this->db->error();
1620 return null;
1621 }
1622
1623 return $result;
1624 }
1625
1626 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1633 public function fetch_contact($contactid = null)
1634 {
1635 // phpcs:enable
1636 if (empty($contactid)) {
1637 $contactid = $this->contact_id;
1638 }
1639
1640 if (empty($contactid)) {
1641 return 0;
1642 }
1643
1644 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1645 $contact = new Contact($this->db);
1646 $result = $contact->fetch($contactid);
1647 $this->contact = $contact;
1648 return $result;
1649 }
1650
1651 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1658 public function fetch_thirdparty($force_thirdparty_id = 0)
1659 {
1660 // phpcs:enable
1661 global $conf;
1662
1663 if (empty($this->socid) && empty($this->fk_soc) && empty($force_thirdparty_id)) {
1664 return 0;
1665 }
1666
1667 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1668
1669 $idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : 0);
1670 if ($force_thirdparty_id) {
1671 $idtofetch = $force_thirdparty_id;
1672 }
1673
1674 if ($idtofetch) {
1675 $thirdparty = new Societe($this->db);
1676 $result = $thirdparty->fetch($idtofetch);
1677 if ($result<0) {
1678 $this->errors=array_merge($this->errors, $thirdparty->errors);
1679 }
1680 $this->thirdparty = $thirdparty;
1681
1682 // Use first price level if level not defined for third party
1683 if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
1684 $this->thirdparty->price_level = 1;
1685 }
1686
1687 return $result;
1688 } else {
1689 return -1;
1690 }
1691 }
1692
1693
1701 public function fetchOneLike($ref)
1702 {
1703 if (!$this->table_ref_field) {
1704 return 0;
1705 }
1706
1707 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element." WHERE ".$this->table_ref_field." LIKE '".$this->db->escape($ref)."' LIMIT 1";
1708
1709 $query = $this->db->query($sql);
1710
1711 if (!$this->db->num_rows($query)) {
1712 return 0;
1713 }
1714
1715 $result = $this->db->fetch_object($query);
1716
1717 return $this->fetch($result->rowid);
1718 }
1719
1720 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1728 public function fetch_barcode()
1729 {
1730 // phpcs:enable
1731 global $conf;
1732
1733 dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1734
1735 $idtype = $this->barcode_type;
1736 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
1737 if ($this->element == 'product' && !empty($conf->global->PRODUIT_DEFAULT_BARCODE_TYPE)) {
1738 $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1739 } elseif ($this->element == 'societe') {
1740 $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1741 } else {
1742 dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1743 }
1744 }
1745
1746 if ($idtype > 0) {
1747 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
1748 $sql = "SELECT rowid, code, libelle as label, coder";
1749 $sql .= " FROM ".$this->db->prefix()."c_barcode_type";
1750 $sql .= " WHERE rowid = ".((int) $idtype);
1751 dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1752 $resql = $this->db->query($sql);
1753 if ($resql) {
1754 $obj = $this->db->fetch_object($resql);
1755 $this->barcode_type = $obj->rowid;
1756 $this->barcode_type_code = $obj->code;
1757 $this->barcode_type_label = $obj->label;
1758 $this->barcode_type_coder = $obj->coder;
1759 return 1;
1760 } else {
1761 dol_print_error($this->db);
1762 return -1;
1763 }
1764 }
1765 }
1766 return 0;
1767 }
1768
1769 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1775 public function fetch_project()
1776 {
1777 // phpcs:enable
1778 return $this->fetch_projet();
1779 }
1780
1781 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1787 public function fetch_projet()
1788 {
1789 // phpcs:enable
1790 include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1791
1792 if (empty($this->fk_project) && !empty($this->fk_projet)) {
1793 $this->fk_project = $this->fk_projet; // For backward compatibility
1794 }
1795 if (empty($this->fk_project)) {
1796 return 0;
1797 }
1798
1799 $project = new Project($this->db);
1800 $result = $project->fetch($this->fk_project);
1801
1802 $this->projet = $project; // deprecated
1803 $this->project = $project;
1804 return $result;
1805 }
1806
1807 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1813 public function fetch_product()
1814 {
1815 // phpcs:enable
1816 include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1817
1818 if (empty($this->fk_product)) {
1819 return 0;
1820 }
1821
1822 $product = new Product($this->db);
1823 $result = $product->fetch($this->fk_product);
1824
1825 $this->product = $product;
1826 return $result;
1827 }
1828
1829 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1836 public function fetch_user($userid)
1837 {
1838 // phpcs:enable
1839 $user = new User($this->db);
1840 $result = $user->fetch($userid);
1841 $this->user = $user;
1842 return $result;
1843 }
1844
1845 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1851 public function fetch_origin()
1852 {
1853 // phpcs:enable
1854 if ($this->origin == 'shipping') {
1855 $this->origin = 'expedition';
1856 }
1857 if ($this->origin == 'delivery') {
1858 $this->origin = 'livraison';
1859 }
1860 if ($this->origin == 'order_supplier') {
1861 $this->origin = 'commandeFournisseur';
1862 }
1863
1864 $origin = $this->origin;
1865
1866 $classname = ucfirst($origin);
1867 $this->$origin = new $classname($this->db);
1868 $this->$origin->fetch($this->origin_id);
1869 }
1870
1880 public function fetchObjectFrom($table, $field, $key, $element = null)
1881 {
1882 global $conf;
1883
1884 $result = false;
1885
1886 $sql = "SELECT rowid FROM ".$this->db->prefix().$table;
1887 $sql .= " WHERE ".$field." = '".$this->db->escape($key)."'";
1888 if (!empty($element)) {
1889 $sql .= " AND entity IN (".getEntity($element).")";
1890 } else {
1891 $sql .= " AND entity = ".((int) $conf->entity);
1892 }
1893
1894 dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
1895 $resql = $this->db->query($sql);
1896 if ($resql) {
1897 $row = $this->db->fetch_row($resql);
1898 // Test for avoid error -1
1899 if ($row[0] > 0) {
1900 $result = $this->fetch($row[0]);
1901 }
1902 }
1903
1904 return $result;
1905 }
1906
1915 public function getValueFrom($table, $id, $field)
1916 {
1917 $result = false;
1918 if (!empty($id) && !empty($field) && !empty($table)) {
1919 $sql = "SELECT ".$field." FROM ".$this->db->prefix().$table;
1920 $sql .= " WHERE rowid = ".((int) $id);
1921
1922 dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
1923 $resql = $this->db->query($sql);
1924 if ($resql) {
1925 $row = $this->db->fetch_row($resql);
1926 $result = $row[0];
1927 }
1928 }
1929 return $result;
1930 }
1931
1948 public function setValueFrom($field, $value, $table = '', $id = null, $format = '', $id_field = '', $fuser = null, $trigkey = '', $fk_user_field = 'fk_user_modif')
1949 {
1950 global $user;
1951
1952 if (empty($table)) {
1953 $table = $this->table_element;
1954 }
1955 if (empty($id)) {
1956 $id = $this->id;
1957 }
1958 if (empty($format)) {
1959 $format = 'text';
1960 }
1961 if (empty($id_field)) {
1962 $id_field = 'rowid';
1963 }
1964
1965 // Special case
1966 if ($table == 'product' && $field == 'note_private') {
1967 $field = 'note';
1968 }
1969
1970 if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
1971 $fk_user_field = 'fk_user_mod';
1972 }
1973 if (in_array($table, array('prelevement_bons'))) { // TODO Add a field fk_user_modif into llx_prelevement_bons
1974 $fk_user_field = '';
1975 }
1976
1977 if ($trigkey) {
1978 $oldvalue = null;
1979
1980 $sql = "SELECT " . $field;
1981 $sql .= " FROM " . MAIN_DB_PREFIX . $table;
1982 $sql .= " WHERE " . $id_field . " = " . ((int) $id);
1983
1984 $resql = $this->db->query($sql);
1985 if ($resql) {
1986 if ($obj = $this->db->fetch_object($resql)) {
1987 if ($format == 'date') {
1988 $oldvalue = $this->db->jdate($obj->$field);
1989 } else {
1990 $oldvalue = $obj->$field;
1991 }
1992 }
1993 } else {
1994 $this->error = $this->db->lasterror();
1995 return -1;
1996 }
1997 }
1998
1999 $error = 0;
2000
2001 dol_syslog(__METHOD__, LOG_DEBUG);
2002
2003 $this->db->begin();
2004
2005 $sql = "UPDATE ".$this->db->prefix().$table." SET ";
2006
2007 if ($format == 'text') {
2008 $sql .= $field." = '".$this->db->escape($value)."'";
2009 } elseif ($format == 'int') {
2010 $sql .= $field." = ".((int) $value);
2011 } elseif ($format == 'date') {
2012 $sql .= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
2013 } elseif ($format == 'dategmt') {
2014 $sql .= $field." = ".($value ? "'".$this->db->idate($value, 'gmt')."'" : "null");
2015 }
2016
2017 if ($fk_user_field) {
2018 if (!empty($fuser) && is_object($fuser)) {
2019 $sql .= ", ".$fk_user_field." = ".((int) $fuser->id);
2020 } elseif (empty($fuser) || $fuser != 'none') {
2021 $sql .= ", ".$fk_user_field." = ".((int) $user->id);
2022 }
2023 }
2024
2025 $sql .= " WHERE ".$id_field." = ".((int) $id);
2026
2027 $resql = $this->db->query($sql);
2028 if ($resql) {
2029 if ($trigkey) {
2030 // call trigger with updated object values
2031 if (method_exists($this, 'fetch')) {
2032 $result = $this->fetch($id);
2033 } else {
2034 $result = $this->fetchCommon($id);
2035 }
2036 $this->oldcopy = clone $this;
2037 if (property_exists($this->oldcopy, $field)) {
2038 $this->oldcopy->$field = $oldvalue;
2039 }
2040
2041 if ($result >= 0) {
2042 $result = $this->call_trigger($trigkey, (!empty($fuser) && is_object($fuser)) ? $fuser : $user); // This may set this->errors
2043 }
2044 if ($result < 0) {
2045 $error++;
2046 }
2047 }
2048
2049 if (!$error) {
2050 if (property_exists($this, $field)) {
2051 $this->$field = $value;
2052 }
2053 $this->db->commit();
2054 return 1;
2055 } else {
2056 $this->db->rollback();
2057 return -2;
2058 }
2059 } else {
2060 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2061 $this->error = 'DB_ERROR_RECORD_ALREADY_EXISTS';
2062 } else {
2063 $this->error = $this->db->lasterror();
2064 }
2065 $this->db->rollback();
2066 return -1;
2067 }
2068 }
2069
2070 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2080 public function load_previous_next_ref($filter, $fieldid, $nodbprefix = 0)
2081 {
2082 // phpcs:enable
2083 global $conf, $user;
2084
2085 if (!$this->table_element) {
2086 dol_print_error('', get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
2087 return -1;
2088 }
2089 if ($fieldid == 'none') {
2090 return 1;
2091 }
2092
2093 // For backward compatibility
2094 if (in_array($this->table_element, array('facture_rec', 'facture_fourn_rec')) && $fieldid == 'title') {
2095 $fieldid = 'titre';
2096 }
2097
2098 // Security on socid
2099 $socid = 0;
2100 if ($user->socid > 0) {
2101 $socid = $user->socid;
2102 }
2103
2104 // this->ismultientitymanaged contains
2105 // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
2106 $aliastablesociete = 's';
2107 if ($this->element == 'societe') {
2108 $aliastablesociete = 'te'; // te as table_element
2109 }
2110 $restrictiononfksoc = empty($this->restrictiononfksoc) ? 0 : $this->restrictiononfksoc;
2111 $sql = "SELECT MAX(te.".$fieldid.")";
2112 $sql .= " FROM ".(empty($nodbprefix) ?$this->db->prefix():'').$this->table_element." as te";
2113 if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2114 $sql .= ",".$this->db->prefix()."usergroup_user as ug";
2115 }
2116 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2117 $tmparray = explode('@', $this->ismultientitymanaged);
2118 $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
2119 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2120 $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
2121 } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2122 $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
2123 }
2124 if ($restrictiononfksoc && empty($user->rights->societe->client->voir) && !$socid) {
2125 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
2126 }
2127 $sql .= " WHERE te.".$fieldid." < '".$this->db->escape($fieldid == 'rowid' ? $this->id : $this->ref)."'"; // ->ref must always be defined (set to id if field does not exists)
2128 if ($restrictiononfksoc == 1 && empty($user->rights->societe->client->voir) && !$socid) {
2129 $sql .= " AND sc.fk_user = ".((int) $user->id);
2130 }
2131 if ($restrictiononfksoc == 2 && empty($user->rights->societe->client->voir) && !$socid) {
2132 $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
2133 }
2134 if (!empty($filter)) {
2135 if (!preg_match('/^\s*AND/i', $filter)) {
2136 $sql .= " AND ";
2137 }
2138 $sql .= $filter;
2139 }
2140 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2141 $tmparray = explode('@', $this->ismultientitymanaged);
2142 $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
2143 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2144 $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
2145 }
2146 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2147 if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2148 if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
2149 $sql .= " AND te.entity IS NOT NULL"; // Show all users
2150 } else {
2151 $sql .= " AND ug.fk_user = te.rowid";
2152 $sql .= " AND ug.entity IN (".getEntity('usergroup').")";
2153 }
2154 } else {
2155 $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
2156 }
2157 }
2158 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
2159 $tmparray = explode('@', $this->ismultientitymanaged);
2160 $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
2161 }
2162 if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
2163 $sql .= ' AND te.fk_soc = '.((int) $socid);
2164 }
2165 if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
2166 $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
2167 }
2168 if ($restrictiononfksoc && $socid && $this->element == 'societe') {
2169 $sql .= ' AND te.rowid = '.((int) $socid);
2170 }
2171 //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
2172
2173 $result = $this->db->query($sql);
2174 if (!$result) {
2175 $this->error = $this->db->lasterror();
2176 return -1;
2177 }
2178 $row = $this->db->fetch_row($result);
2179 $this->ref_previous = $row[0];
2180
2181 $sql = "SELECT MIN(te.".$fieldid.")";
2182 $sql .= " FROM ".(empty($nodbprefix) ?$this->db->prefix():'').$this->table_element." as te";
2183 if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2184 $sql .= ",".$this->db->prefix()."usergroup_user as ug";
2185 }
2186 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2187 $tmparray = explode('@', $this->ismultientitymanaged);
2188 $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
2189 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2190 $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
2191 } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2192 $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
2193 }
2194 if ($restrictiononfksoc && empty($user->rights->societe->client->voir) && !$socid) {
2195 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
2196 }
2197 $sql .= " WHERE te.".$fieldid." > '".$this->db->escape($fieldid == 'rowid' ? $this->id : $this->ref)."'"; // ->ref must always be defined (set to id if field does not exists)
2198 if ($restrictiononfksoc == 1 && empty($user->rights->societe->client->voir) && !$socid) {
2199 $sql .= " AND sc.fk_user = ".((int) $user->id);
2200 }
2201 if ($restrictiononfksoc == 2 && empty($user->rights->societe->client->voir) && !$socid) {
2202 $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
2203 }
2204 if (!empty($filter)) {
2205 if (!preg_match('/^\s*AND/i', $filter)) {
2206 $sql .= " AND "; // For backward compatibility
2207 }
2208 $sql .= $filter;
2209 }
2210 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2211 $tmparray = explode('@', $this->ismultientitymanaged);
2212 $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
2213 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2214 $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
2215 }
2216 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2217 if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2218 if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
2219 $sql .= " AND te.entity IS NOT NULL"; // Show all users
2220 } else {
2221 $sql .= " AND ug.fk_user = te.rowid";
2222 $sql .= " AND ug.entity IN (".getEntity('usergroup').")";
2223 }
2224 } else {
2225 $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
2226 }
2227 }
2228 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
2229 $tmparray = explode('@', $this->ismultientitymanaged);
2230 $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
2231 }
2232 if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
2233 $sql .= ' AND te.fk_soc = '.((int) $socid);
2234 }
2235 if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
2236 $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
2237 }
2238 if ($restrictiononfksoc && $socid && $this->element == 'societe') {
2239 $sql .= ' AND te.rowid = '.((int) $socid);
2240 }
2241 //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
2242 // 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
2243
2244 $result = $this->db->query($sql);
2245 if (!$result) {
2246 $this->error = $this->db->lasterror();
2247 return -2;
2248 }
2249 $row = $this->db->fetch_row($result);
2250 $this->ref_next = $row[0];
2251
2252 return 1;
2253 }
2254
2255
2263 public function getListContactId($source = 'external')
2264 {
2265 $contactAlreadySelected = array();
2266 $tab = $this->liste_contact(-1, $source);
2267 $num = count($tab);
2268 $i = 0;
2269 while ($i < $num) {
2270 if ($source == 'thirdparty') {
2271 $contactAlreadySelected[$i] = $tab[$i]['socid'];
2272 } else {
2273 $contactAlreadySelected[$i] = $tab[$i]['id'];
2274 }
2275 $i++;
2276 }
2277 return $contactAlreadySelected;
2278 }
2279
2280
2288 public function setProject($projectid, $notrigger = 0)
2289 {
2290 global $user;
2291 $error = 0;
2292
2293 if (!$this->table_element) {
2294 dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined", LOG_ERR);
2295 return -1;
2296 }
2297
2298 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2299 if (!empty($this->fields['fk_project'])) { // Common case
2300 if ($projectid) {
2301 $sql .= " SET fk_project = ".((int) $projectid);
2302 } else {
2303 $sql .= " SET fk_project = NULL";
2304 }
2305 $sql .= ' WHERE rowid = '.((int) $this->id);
2306 } elseif ($this->table_element == 'actioncomm') { // Special case for actioncomm
2307 if ($projectid) {
2308 $sql .= " SET fk_project = ".((int) $projectid);
2309 } else {
2310 $sql .= " SET fk_project = NULL";
2311 }
2312 $sql .= ' WHERE id = '.((int) $this->id);
2313 } else // Special case for old architecture objects
2314 {
2315 if ($projectid) {
2316 $sql .= ' SET fk_projet = '.((int) $projectid);
2317 } else {
2318 $sql .= ' SET fk_projet = NULL';
2319 }
2320 $sql .= " WHERE rowid = ".((int) $this->id);
2321 }
2322
2323 $this->db->begin();
2324
2325 dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
2326 if ($this->db->query($sql)) {
2327 $this->fk_project = ((int) $projectid);
2328 } else {
2329 dol_print_error($this->db);
2330 $error++;
2331 }
2332
2333 // Triggers
2334 if (!$error && !$notrigger) {
2335 // Call triggers
2336 $result = $this->call_trigger(strtoupper($this->element) . '_MODIFY', $user);
2337 if ($result < 0) {
2338 $error++;
2339 } //Do also here what you must do to rollback action if trigger fail
2340 // End call triggers
2341 }
2342
2343 // Commit or rollback
2344 if ($error) {
2345 $this->db->rollback();
2346 return -1;
2347 } else {
2348 $this->db->commit();
2349 return 1;
2350 }
2351 }
2352
2359 public function setPaymentMethods($id)
2360 {
2361 global $user;
2362
2363 $error = 0; $notrigger = 0;
2364
2365 dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
2366
2367 if ($this->statut >= 0 || $this->element == 'societe') {
2368 // TODO uniformize field name
2369 $fieldname = 'fk_mode_reglement';
2370 if ($this->element == 'societe') {
2371 $fieldname = 'mode_reglement';
2372 }
2373 if (get_class($this) == 'Fournisseur') {
2374 $fieldname = 'mode_reglement_supplier';
2375 }
2376 if (get_class($this) == 'Tva') {
2377 $fieldname = 'fk_typepayment';
2378 }
2379 if (get_class($this) == 'Salary') {
2380 $fieldname = 'fk_typepayment';
2381 }
2382
2383 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2384 $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2385 $sql .= ' WHERE rowid='.((int) $this->id);
2386
2387 if ($this->db->query($sql)) {
2388 $this->mode_reglement_id = $id;
2389 // for supplier
2390 if (get_class($this) == 'Fournisseur') {
2391 $this->mode_reglement_supplier_id = $id;
2392 }
2393 // Triggers
2394 if (!$error && !$notrigger) {
2395 // Call triggers
2396 if (get_class($this) == 'Commande') {
2397 $result = $this->call_trigger('ORDER_MODIFY', $user);
2398 } else {
2399 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
2400 }
2401 if ($result < 0) {
2402 $error++;
2403 }
2404 // End call triggers
2405 }
2406 return 1;
2407 } else {
2408 dol_syslog(get_class($this).'::setPaymentMethods Error '.$this->db->error());
2409 $this->error = $this->db->error();
2410 return -1;
2411 }
2412 } else {
2413 dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
2414 $this->error = 'Status of the object is incompatible '.$this->statut;
2415 return -2;
2416 }
2417 }
2418
2425 public function setMulticurrencyCode($code)
2426 {
2427 dol_syslog(get_class($this).'::setMulticurrencyCode('.$code.')');
2428 if ($this->statut >= 0 || $this->element == 'societe') {
2429 $fieldname = 'multicurrency_code';
2430
2431 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2432 $sql .= " SET ".$fieldname." = '".$this->db->escape($code)."'";
2433 $sql .= ' WHERE rowid='.((int) $this->id);
2434
2435 if ($this->db->query($sql)) {
2436 $this->multicurrency_code = $code;
2437
2438 list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
2439 if ($rate) {
2440 $this->setMulticurrencyRate($rate, 2);
2441 }
2442
2443 return 1;
2444 } else {
2445 dol_syslog(get_class($this).'::setMulticurrencyCode Error '.$sql.' - '.$this->db->error());
2446 $this->error = $this->db->error();
2447 return -1;
2448 }
2449 } else {
2450 dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
2451 $this->error = 'Status of the object is incompatible '.$this->statut;
2452 return -2;
2453 }
2454 }
2455
2463 public function setMulticurrencyRate($rate, $mode = 1)
2464 {
2465 dol_syslog(get_class($this).'::setMulticurrencyRate('.$rate.','.$mode.')');
2466 if ($this->statut >= 0 || $this->element == 'societe') {
2467 $fieldname = 'multicurrency_tx';
2468
2469 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2470 $sql .= " SET ".$fieldname." = ".((float) $rate);
2471 $sql .= ' WHERE rowid='.((int) $this->id);
2472
2473 if ($this->db->query($sql)) {
2474 $this->multicurrency_tx = $rate;
2475
2476 // Update line price
2477 if (!empty($this->lines)) {
2478 foreach ($this->lines as &$line) {
2479 // Amounts in company currency will be recalculated
2480 if ($mode == 1) {
2481 $line->subprice = 0;
2482 }
2483
2484 // Amounts in foreign currency will be recalculated
2485 if ($mode == 2) {
2486 $line->multicurrency_subprice = 0;
2487 }
2488
2489 switch ($this->element) {
2490 case 'propal':
2493 $this->updateline(
2494 $line->id,
2495 $line->subprice,
2496 $line->qty,
2497 $line->remise_percent,
2498 $line->tva_tx,
2499 $line->localtax1_tx,
2500 $line->localtax2_tx,
2501 ($line->description ? $line->description : $line->desc),
2502 'HT',
2503 $line->info_bits,
2504 $line->special_code,
2505 $line->fk_parent_line,
2506 $line->skip_update_total,
2507 $line->fk_fournprice,
2508 $line->pa_ht,
2509 $line->label,
2510 $line->product_type,
2511 $line->date_start,
2512 $line->date_end,
2513 $line->array_options,
2514 $line->fk_unit,
2515 $line->multicurrency_subprice
2516 );
2517 break;
2518 case 'commande':
2521 $this->updateline(
2522 $line->id,
2523 ($line->description ? $line->description : $line->desc),
2524 $line->subprice,
2525 $line->qty,
2526 $line->remise_percent,
2527 $line->tva_tx,
2528 $line->localtax1_tx,
2529 $line->localtax2_tx,
2530 'HT',
2531 $line->info_bits,
2532 $line->date_start,
2533 $line->date_end,
2534 $line->product_type,
2535 $line->fk_parent_line,
2536 $line->skip_update_total,
2537 $line->fk_fournprice,
2538 $line->pa_ht,
2539 $line->label,
2540 $line->special_code,
2541 $line->array_options,
2542 $line->fk_unit,
2543 $line->multicurrency_subprice
2544 );
2545 break;
2546 case 'facture':
2549 $this->updateline(
2550 $line->id,
2551 ($line->description ? $line->description : $line->desc),
2552 $line->subprice,
2553 $line->qty,
2554 $line->remise_percent,
2555 $line->date_start,
2556 $line->date_end,
2557 $line->tva_tx,
2558 $line->localtax1_tx,
2559 $line->localtax2_tx,
2560 'HT',
2561 $line->info_bits,
2562 $line->product_type,
2563 $line->fk_parent_line,
2564 $line->skip_update_total,
2565 $line->fk_fournprice,
2566 $line->pa_ht,
2567 $line->label,
2568 $line->special_code,
2569 $line->array_options,
2570 $line->situation_percent,
2571 $line->fk_unit,
2572 $line->multicurrency_subprice
2573 );
2574 break;
2575 case 'supplier_proposal':
2578 $this->updateline(
2579 $line->id,
2580 $line->subprice,
2581 $line->qty,
2582 $line->remise_percent,
2583 $line->tva_tx,
2584 $line->localtax1_tx,
2585 $line->localtax2_tx,
2586 ($line->description ? $line->description : $line->desc),
2587 'HT',
2588 $line->info_bits,
2589 $line->special_code,
2590 $line->fk_parent_line,
2591 $line->skip_update_total,
2592 $line->fk_fournprice,
2593 $line->pa_ht,
2594 $line->label,
2595 $line->product_type,
2596 $line->array_options,
2597 $line->ref_fourn,
2598 $line->multicurrency_subprice
2599 );
2600 break;
2601 case 'order_supplier':
2604 $this->updateline(
2605 $line->id,
2606 ($line->description ? $line->description : $line->desc),
2607 $line->subprice,
2608 $line->qty,
2609 $line->remise_percent,
2610 $line->tva_tx,
2611 $line->localtax1_tx,
2612 $line->localtax2_tx,
2613 'HT',
2614 $line->info_bits,
2615 $line->product_type,
2616 false,
2617 $line->date_start,
2618 $line->date_end,
2619 $line->array_options,
2620 $line->fk_unit,
2621 $line->multicurrency_subprice,
2622 $line->ref_supplier
2623 );
2624 break;
2625 case 'invoice_supplier':
2628 $this->updateline(
2629 $line->id,
2630 ($line->description ? $line->description : $line->desc),
2631 $line->subprice,
2632 $line->tva_tx,
2633 $line->localtax1_tx,
2634 $line->localtax2_tx,
2635 $line->qty,
2636 0,
2637 'HT',
2638 $line->info_bits,
2639 $line->product_type,
2640 $line->remise_percent,
2641 false,
2642 $line->date_start,
2643 $line->date_end,
2644 $line->array_options,
2645 $line->fk_unit,
2646 $line->multicurrency_subprice,
2647 $line->ref_supplier
2648 );
2649 break;
2650 default:
2651 dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
2652 break;
2653 }
2654 }
2655 }
2656
2657 return 1;
2658 } else {
2659 dol_syslog(get_class($this).'::setMulticurrencyRate Error '.$sql.' - '.$this->db->error());
2660 $this->error = $this->db->error();
2661 return -1;
2662 }
2663 } else {
2664 dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
2665 $this->error = 'Status of the object is incompatible '.$this->statut;
2666 return -2;
2667 }
2668 }
2669
2677 public function setPaymentTerms($id, $deposit_percent = null)
2678 {
2679 dol_syslog(get_class($this).'::setPaymentTerms('.$id.', '.var_export($deposit_percent, true).')');
2680 if ($this->statut >= 0 || $this->element == 'societe') {
2681 // TODO uniformize field name
2682 $fieldname = 'fk_cond_reglement';
2683 if ($this->element == 'societe') {
2684 $fieldname = 'cond_reglement';
2685 }
2686 if (get_class($this) == 'Fournisseur') {
2687 $fieldname = 'cond_reglement_supplier';
2688 }
2689
2690 if (empty($deposit_percent) || $deposit_percent < 0) {
2691 $deposit_percent = getDictionaryValue('c_payment_term', 'deposit_percent', $id);
2692 }
2693
2694 if ($deposit_percent > 100) {
2695 $deposit_percent = 100;
2696 }
2697
2698 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2699 $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2700 if (in_array($this->table_element, array('propal', 'commande', 'societe'))) {
2701 $sql .= " , deposit_percent = " . (empty($deposit_percent) ? 'NULL' : "'".$this->db->escape($deposit_percent)."'");
2702 }
2703 $sql .= ' WHERE rowid='.((int) $this->id);
2704
2705 if ($this->db->query($sql)) {
2706 $this->cond_reglement_id = $id;
2707 // for supplier
2708 if (get_class($this) == 'Fournisseur') {
2709 $this->cond_reglement_supplier_id = $id;
2710 }
2711 $this->cond_reglement = $id; // for compatibility
2712 $this->deposit_percent = $deposit_percent;
2713 return 1;
2714 } else {
2715 dol_syslog(get_class($this).'::setPaymentTerms Error '.$sql.' - '.$this->db->error());
2716 $this->error = $this->db->error();
2717 return -1;
2718 }
2719 } else {
2720 dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
2721 $this->error = 'Status of the object is incompatible '.$this->statut;
2722 return -2;
2723 }
2724 }
2725
2732 public function setTransportMode($id)
2733 {
2734 dol_syslog(get_class($this).'::setTransportMode('.$id.')');
2735 if ($this->statut >= 0 || $this->element == 'societe') {
2736 $fieldname = 'fk_transport_mode';
2737 if ($this->element == 'societe') {
2738 $fieldname = 'transport_mode';
2739 }
2740 if (get_class($this) == 'Fournisseur') {
2741 $fieldname = 'transport_mode_supplier';
2742 }
2743
2744 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2745 $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2746 $sql .= ' WHERE rowid='.((int) $this->id);
2747
2748 if ($this->db->query($sql)) {
2749 $this->transport_mode_id = $id;
2750 // for supplier
2751 if (get_class($this) == 'Fournisseur') {
2752 $this->transport_mode_supplier_id = $id;
2753 }
2754 return 1;
2755 } else {
2756 dol_syslog(get_class($this).'::setTransportMode Error '.$sql.' - '.$this->db->error());
2757 $this->error = $this->db->error();
2758 return -1;
2759 }
2760 } else {
2761 dol_syslog(get_class($this).'::setTransportMode, status of the object is incompatible');
2762 $this->error = 'Status of the object is incompatible '.$this->statut;
2763 return -2;
2764 }
2765 }
2766
2774 {
2775 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms('.$id.')');
2776 if ($this->statut >= 0 || $this->element == 'societe') {
2777 $fieldname = 'retained_warranty_fk_cond_reglement';
2778
2779 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2780 $sql .= " SET ".$fieldname." = ".((int) $id);
2781 $sql .= ' WHERE rowid='.((int) $this->id);
2782
2783 if ($this->db->query($sql)) {
2784 $this->retained_warranty_fk_cond_reglement = $id;
2785 return 1;
2786 } else {
2787 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms Error '.$sql.' - '.$this->db->error());
2788 $this->error = $this->db->error();
2789 return -1;
2790 }
2791 } else {
2792 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms, status of the object is incompatible');
2793 $this->error = 'Status of the object is incompatible '.$this->statut;
2794 return -2;
2795 }
2796 }
2797
2805 public function setDeliveryAddress($id)
2806 {
2807 $fieldname = 'fk_delivery_address';
2808 if ($this->element == 'delivery' || $this->element == 'shipping') {
2809 $fieldname = 'fk_address';
2810 }
2811
2812 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ".$fieldname." = ".((int) $id);
2813 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
2814
2815 if ($this->db->query($sql)) {
2816 $this->fk_delivery_address = $id;
2817 return 1;
2818 } else {
2819 $this->error = $this->db->error();
2820 dol_syslog(get_class($this).'::setDeliveryAddress Error '.$this->error);
2821 return -1;
2822 }
2823 }
2824
2825
2835 public function setShippingMethod($shipping_method_id, $notrigger = false, $userused = null)
2836 {
2837 global $user;
2838
2839 if (empty($userused)) {
2840 $userused = $user;
2841 }
2842
2843 $error = 0;
2844
2845 if (!$this->table_element) {
2846 dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined", LOG_ERR);
2847 return -1;
2848 }
2849
2850 $this->db->begin();
2851
2852 if ($shipping_method_id < 0) {
2853 $shipping_method_id = 'NULL';
2854 }
2855 dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
2856
2857 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2858 $sql .= " SET fk_shipping_method = ".((int) $shipping_method_id);
2859 $sql .= " WHERE rowid=".((int) $this->id);
2860 $resql = $this->db->query($sql);
2861 if (!$resql) {
2862 dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
2863 $this->error = $this->db->lasterror();
2864 $error++;
2865 } else {
2866 if (!$notrigger) {
2867 // Call trigger
2868 $this->context = array('shippingmethodupdate'=>1);
2869 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused);
2870 if ($result < 0) {
2871 $error++;
2872 }
2873 // End call trigger
2874 }
2875 }
2876 if ($error) {
2877 $this->db->rollback();
2878 return -1;
2879 } else {
2880 $this->shipping_method_id = ($shipping_method_id == 'NULL') ?null:$shipping_method_id;
2881 $this->db->commit();
2882 return 1;
2883 }
2884 }
2885
2886
2893 public function setWarehouse($warehouse_id)
2894 {
2895 if (!$this->table_element) {
2896 dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined", LOG_ERR);
2897 return -1;
2898 }
2899 if ($warehouse_id < 0) {
2900 $warehouse_id = 'NULL';
2901 }
2902 dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
2903
2904 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2905 $sql .= " SET fk_warehouse = ".((int) $warehouse_id);
2906 $sql .= " WHERE rowid=".((int) $this->id);
2907
2908 if ($this->db->query($sql)) {
2909 $this->warehouse_id = ($warehouse_id == 'NULL') ?null:$warehouse_id;
2910 return 1;
2911 } else {
2912 dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
2913 $this->error = $this->db->error();
2914 return 0;
2915 }
2916 }
2917
2918
2926 public function setDocModel($user, $modelpdf)
2927 {
2928 if (!$this->table_element) {
2929 dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined", LOG_ERR);
2930 return -1;
2931 }
2932
2933 $newmodelpdf = dol_trunc($modelpdf, 255);
2934
2935 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2936 $sql .= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
2937 $sql .= " WHERE rowid = ".((int) $this->id);
2938
2939 dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
2940 $resql = $this->db->query($sql);
2941 if ($resql) {
2942 $this->model_pdf = $modelpdf;
2943 $this->modelpdf = $modelpdf; // For bakward compatibility
2944 return 1;
2945 } else {
2946 dol_print_error($this->db);
2947 return 0;
2948 }
2949 }
2950
2951
2960 public function setBankAccount($fk_account, $notrigger = false, $userused = null)
2961 {
2962 global $user;
2963
2964 if (empty($userused)) {
2965 $userused = $user;
2966 }
2967
2968 $error = 0;
2969
2970 if (!$this->table_element) {
2971 dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined", LOG_ERR);
2972 return -1;
2973 }
2974 $this->db->begin();
2975
2976 if ($fk_account < 0) {
2977 $fk_account = 'NULL';
2978 }
2979 dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
2980
2981 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2982 $sql .= " SET fk_account = ".((int) $fk_account);
2983 $sql .= " WHERE rowid=".((int) $this->id);
2984
2985 $resql = $this->db->query($sql);
2986 if (!$resql) {
2987 dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
2988 $this->error = $this->db->lasterror();
2989 $error++;
2990 } else {
2991 if (!$notrigger) {
2992 // Call trigger
2993 $this->context = array('bankaccountupdate'=>1);
2994 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused);
2995 if ($result < 0) {
2996 $error++;
2997 }
2998 // End call trigger
2999 }
3000 }
3001 if ($error) {
3002 $this->db->rollback();
3003 return -1;
3004 } else {
3005 $this->fk_account = ($fk_account == 'NULL') ?null:$fk_account;
3006 $this->db->commit();
3007 return 1;
3008 }
3009 }
3010
3011
3012 // TODO: Move line related operations to CommonObjectLine?
3013
3014 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3024 public function line_order($renum = false, $rowidorder = 'ASC', $fk_parent_line = true)
3025 {
3026 // phpcs:enable
3027 if (!$this->table_element_line) {
3028 dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined", LOG_ERR);
3029 return -1;
3030 }
3031 if (!$this->fk_element) {
3032 dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined", LOG_ERR);
3033 return -1;
3034 }
3035
3036 $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3037 if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3038 $fieldposition = 'position';
3039 }
3040
3041 // Count number of lines to reorder (according to choice $renum)
3042 $nl = 0;
3043 $sql = "SELECT count(rowid) FROM ".$this->db->prefix().$this->table_element_line;
3044 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3045 if (!$renum) {
3046 $sql .= " AND " . $fieldposition . " = 0";
3047 }
3048 if ($renum) {
3049 $sql .= " AND " . $fieldposition . " <> 0";
3050 }
3051
3052 dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
3053 $resql = $this->db->query($sql);
3054 if ($resql) {
3055 $row = $this->db->fetch_row($resql);
3056 $nl = $row[0];
3057 } else {
3058 dol_print_error($this->db);
3059 }
3060 if ($nl > 0) {
3061 // The goal of this part is to reorder all lines, with all children lines sharing the same counter that parents.
3062 $rows = array();
3063
3064 // We first search all lines that are parent lines (for multilevel details lines)
3065 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3066 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3067 if ($fk_parent_line) {
3068 $sql .= ' AND fk_parent_line IS NULL';
3069 }
3070 $sql .= " ORDER BY " . $fieldposition . " ASC, rowid " . $rowidorder;
3071
3072 dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
3073 $resql = $this->db->query($sql);
3074 if ($resql) {
3075 $i = 0;
3076 $num = $this->db->num_rows($resql);
3077 while ($i < $num) {
3078 $row = $this->db->fetch_row($resql);
3079 $rows[] = $row[0]; // Add parent line into array rows
3080 $childrens = $this->getChildrenOfLine($row[0]);
3081 if (!empty($childrens)) {
3082 foreach ($childrens as $child) {
3083 array_push($rows, $child);
3084 }
3085 }
3086 $i++;
3087 }
3088
3089 // Now we set a new number for each lines (parent and children with children included into parent tree)
3090 if (!empty($rows)) {
3091 foreach ($rows as $key => $row) {
3092 $this->updateRangOfLine($row, ($key + 1));
3093 }
3094 }
3095 } else {
3096 dol_print_error($this->db);
3097 }
3098 }
3099 return 1;
3100 }
3101
3109 public function getChildrenOfLine($id, $includealltree = 0)
3110 {
3111 $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3112 if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3113 $fieldposition = 'position';
3114 }
3115
3116 $rows = array();
3117
3118 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3119 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3120 $sql .= ' AND fk_parent_line = '.((int) $id);
3121 $sql .= " ORDER BY " . $fieldposition . " ASC";
3122
3123 dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id, LOG_DEBUG);
3124 $resql = $this->db->query($sql);
3125 if ($resql) {
3126 if ($this->db->num_rows($resql) > 0) {
3127 while ($row = $this->db->fetch_row($resql)) {
3128 $rows[] = $row[0];
3129 if (!empty($includealltree)) {
3130 $rows = array_merge($rows, $this->getChildrenOfLine($row[0], $includealltree));
3131 }
3132 }
3133 }
3134 }
3135 return $rows;
3136 }
3137
3138 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3146 public function line_up($rowid, $fk_parent_line = true)
3147 {
3148 // phpcs:enable
3149 $this->line_order(false, 'ASC', $fk_parent_line);
3150
3151 // Get rang of line
3152 $rang = $this->getRangOfLine($rowid);
3153
3154 // Update position of line
3155 $this->updateLineUp($rowid, $rang);
3156 }
3157
3158 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3166 public function line_down($rowid, $fk_parent_line = true)
3167 {
3168 // phpcs:enable
3169 $this->line_order(false, 'ASC', $fk_parent_line);
3170
3171 // Get rang of line
3172 $rang = $this->getRangOfLine($rowid);
3173
3174 // Get max value for rang
3175 $max = $this->line_max();
3176
3177 // Update position of line
3178 $this->updateLineDown($rowid, $rang, $max);
3179 }
3180
3188 public function updateRangOfLine($rowid, $rang)
3189 {
3190 global $hookmanager;
3191 $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3192 if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3193 $fieldposition = 'position';
3194 }
3195
3196 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3197 $sql .= ' WHERE rowid = '.((int) $rowid);
3198
3199 dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
3200 if (!$this->db->query($sql)) {
3201 dol_print_error($this->db);
3202 return -1;
3203 } else {
3204 $parameters=array('rowid'=>$rowid, 'rang'=>$rang, 'fieldposition' => $fieldposition);
3205 $action='';
3206 $reshook = $hookmanager->executeHooks('afterRankOfLineUpdate', $parameters, $this, $action);
3207 return 1;
3208 }
3209 }
3210
3211 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3218 public function line_ajaxorder($rows)
3219 {
3220 // phpcs:enable
3221 $num = count($rows);
3222 for ($i = 0; $i < $num; $i++) {
3223 $this->updateRangOfLine($rows[$i], ($i + 1));
3224 }
3225 }
3226
3234 public function updateLineUp($rowid, $rang)
3235 {
3236 if ($rang > 1) {
3237 $fieldposition = 'rang';
3238 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3239 $fieldposition = 'position';
3240 }
3241
3242 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3243 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3244 $sql .= " AND " . $fieldposition . " = " . ((int) ($rang - 1));
3245 if ($this->db->query($sql)) {
3246 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang - 1));
3247 $sql .= ' WHERE rowid = '.((int) $rowid);
3248 if (!$this->db->query($sql)) {
3249 dol_print_error($this->db);
3250 }
3251 } else {
3252 dol_print_error($this->db);
3253 }
3254 }
3255 }
3256
3265 public function updateLineDown($rowid, $rang, $max)
3266 {
3267 if ($rang < $max) {
3268 $fieldposition = 'rang';
3269 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3270 $fieldposition = 'position';
3271 }
3272
3273 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3274 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3275 $sql .= " AND " . $fieldposition . " = " . ((int) ($rang + 1));
3276 if ($this->db->query($sql)) {
3277 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang + 1));
3278 $sql .= ' WHERE rowid = '.((int) $rowid);
3279 if (!$this->db->query($sql)) {
3280 dol_print_error($this->db);
3281 }
3282 } else {
3283 dol_print_error($this->db);
3284 }
3285 }
3286 }
3287
3294 public function getRangOfLine($rowid)
3295 {
3296 $fieldposition = 'rang';
3297 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3298 $fieldposition = 'position';
3299 }
3300
3301 $sql = "SELECT " . $fieldposition . " FROM ".$this->db->prefix().$this->table_element_line;
3302 $sql .= " WHERE rowid = ".((int) $rowid);
3303
3304 dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
3305 $resql = $this->db->query($sql);
3306 if ($resql) {
3307 $row = $this->db->fetch_row($resql);
3308 return $row[0];
3309 }
3310
3311 return 0;
3312 }
3313
3320 public function getIdOfLine($rang)
3321 {
3322 $fieldposition = 'rang';
3323 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3324 $fieldposition = 'position';
3325 }
3326
3327 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3328 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3329 $sql .= " AND " . $fieldposition . " = ".((int) $rang);
3330 $resql = $this->db->query($sql);
3331 if ($resql) {
3332 $row = $this->db->fetch_row($resql);
3333 return $row[0];
3334 }
3335
3336 return 0;
3337 }
3338
3339 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3346 public function line_max($fk_parent_line = 0)
3347 {
3348 // phpcs:enable
3349 $positionfield = 'rang';
3350 if (in_array($this->table_element, array('bom_bom', 'product_attribute'))) {
3351 $positionfield = 'position';
3352 }
3353
3354 // Search the last rang with fk_parent_line
3355 if ($fk_parent_line) {
3356 $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
3357 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3358 $sql .= " AND fk_parent_line = ".((int) $fk_parent_line);
3359
3360 dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
3361 $resql = $this->db->query($sql);
3362 if ($resql) {
3363 $row = $this->db->fetch_row($resql);
3364 if (!empty($row[0])) {
3365 return $row[0];
3366 } else {
3367 return $this->getRangOfLine($fk_parent_line);
3368 }
3369 }
3370 } else {
3371 // If not, search the last rang of element
3372 $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
3373 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3374
3375 dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
3376 $resql = $this->db->query($sql);
3377 if ($resql) {
3378 $row = $this->db->fetch_row($resql);
3379 return $row[0];
3380 }
3381 }
3382
3383 return 0;
3384 }
3385
3386 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3393 public function update_ref_ext($ref_ext)
3394 {
3395 // phpcs:enable
3396 if (!$this->table_element) {
3397 dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
3398 return -1;
3399 }
3400
3401 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3402 $sql .= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
3403 $sql .= " WHERE ".(isset($this->table_rowid) ? $this->table_rowid : 'rowid')." = ".((int) $this->id);
3404
3405 dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
3406 if ($this->db->query($sql)) {
3407 $this->ref_ext = $ref_ext;
3408 return 1;
3409 } else {
3410 $this->error = $this->db->error();
3411 return -1;
3412 }
3413 }
3414
3415 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3424 public function update_note($note, $suffix = '', $notrigger = 0)
3425 {
3426 // phpcs:enable
3427 global $user;
3428
3429 if (!$this->table_element) {
3430 $this->error = 'update_note was called on objet with property table_element not defined';
3431 dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
3432 return -1;
3433 }
3434 if (!in_array($suffix, array('', '_public', '_private'))) {
3435 $this->error = 'update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
3436 dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
3437 return -2;
3438 }
3439
3440 $newsuffix = $suffix;
3441
3442 // Special cas
3443 if ($this->table_element == 'product' && $newsuffix == '_private') {
3444 $newsuffix = '';
3445 }
3446 if (in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
3447 $fieldusermod = "fk_user_mod";
3448 } elseif ($this->table_element == 'ecm_files') {
3449 $fieldusermod = "fk_user_m";
3450 } else {
3451 $fieldusermod = "fk_user_modif";
3452 }
3453 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3454 $sql .= " SET note".$newsuffix." = ".(!empty($note) ? ("'".$this->db->escape($note)."'") : "NULL");
3455 $sql .= ", ".$fieldusermod." = ".((int) $user->id);
3456 $sql .= " WHERE rowid = ".((int) $this->id);
3457
3458 dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
3459 if ($this->db->query($sql)) {
3460 if ($suffix == '_public') {
3461 $this->note_public = $note;
3462 } elseif ($suffix == '_private') {
3463 $this->note_private = $note;
3464 } else {
3465 $this->note = $note; // deprecated
3466 $this->note_private = $note;
3467 }
3468 if (empty($notrigger)) {
3469 switch ($this->element) {
3470 case 'societe':
3471 $trigger_name = 'COMPANY_MODIFY';
3472 break;
3473 case 'commande':
3474 $trigger_name = 'ORDER_MODIFY';
3475 break;
3476 case 'facture':
3477 $trigger_name = 'BILL_MODIFY';
3478 break;
3479 case 'invoice_supplier':
3480 $trigger_name = 'BILL_SUPPLIER_MODIFY';
3481 break;
3482 case 'facturerec':
3483 $trigger_name = 'BILLREC_MODIFIY';
3484 break;
3485 case 'expensereport':
3486 $trigger_name = 'EXPENSE_REPORT_MODIFY';
3487 break;
3488 default:
3489 $trigger_name = strtoupper($this->element) . '_MODIFY';
3490 }
3491 $ret = $this->call_trigger($trigger_name, $user);
3492 if ($ret < 0) {
3493 return -1;
3494 }
3495 }
3496 return 1;
3497 } else {
3498 $this->error = $this->db->lasterror();
3499 return -1;
3500 }
3501 }
3502
3503 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3512 public function update_note_public($note)
3513 {
3514 // phpcs:enable
3515 return $this->update_note($note, '_public');
3516 }
3517
3518 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3529 public function update_price($exclspec = 0, $roundingadjust = 'none', $nodatabaseupdate = 0, $seller = null)
3530 {
3531 // phpcs:enable
3532 global $conf, $hookmanager, $action;
3533
3534 $parameters = array('exclspec' => $exclspec, 'roundingadjust' => $roundingadjust, 'nodatabaseupdate' => $nodatabaseupdate, 'seller' => $seller);
3535 $reshook = $hookmanager->executeHooks('updateTotalPrice', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3536 if ($reshook > 0) {
3537 return 1; // replacement code
3538 } elseif ($reshook < 0) {
3539 return -1; // failure
3540 } // reshook = 0 => execute normal code
3541
3542 // Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
3543 $MODULE = "";
3544 if ($this->element == 'propal') {
3545 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
3546 } elseif ($this->element == 'commande' || $this->element == 'order') {
3547 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
3548 } elseif ($this->element == 'facture' || $this->element == 'invoice') {
3549 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
3550 } elseif ($this->element == 'facture_fourn' || $this->element == 'supplier_invoice' || $this->element == 'invoice_supplier' || $this->element == 'invoice_supplier_rec') {
3551 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
3552 } elseif ($this->element == 'order_supplier' || $this->element == 'supplier_order') {
3553 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
3554 } elseif ($this->element == 'supplier_proposal') {
3555 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
3556 }
3557
3558 if (!empty($MODULE)) {
3559 if (!empty($conf->global->$MODULE)) {
3560 $modsactivated = explode(',', $conf->global->$MODULE);
3561 foreach ($modsactivated as $mod) {
3562 if (isModEnabled($mod)) {
3563 return 1; // update was disabled by specific setup
3564 }
3565 }
3566 }
3567 }
3568
3569 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3570
3571 $forcedroundingmode = $roundingadjust;
3572 if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) {
3573 $forcedroundingmode = getDolGlobalString('MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND');
3574 } elseif ($forcedroundingmode == 'auto') {
3575 $forcedroundingmode = '0';
3576 }
3577
3578 $error = 0;
3579
3580 $multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
3581
3582 // Define constants to find lines to sum (field name int the table_element_line not into table_element)
3583 $fieldtva = 'total_tva';
3584 $fieldlocaltax1 = 'total_localtax1';
3585 $fieldlocaltax2 = 'total_localtax2';
3586 $fieldup = 'subprice';
3587 if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
3588 $fieldtva = 'tva';
3589 $fieldup = 'pu_ht';
3590 }
3591 if ($this->element == 'invoice_supplier_rec') {
3592 $fieldup = 'pu_ht';
3593 }
3594 if ($this->element == 'expensereport') {
3595 $fieldup = 'value_unit';
3596 }
3597
3598 $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,";
3599 $sql .= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
3600 if ($this->table_element_line == 'facturedet') {
3601 $sql .= ', situation_percent';
3602 }
3603 $sql .= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
3604 $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
3605 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3606 if ($exclspec) {
3607 $product_field = 'product_type';
3608 if ($this->table_element_line == 'contratdet') {
3609 $product_field = ''; // contratdet table has no product_type field
3610 }
3611 if ($product_field) {
3612 $sql .= " AND ".$product_field." <> 9";
3613 }
3614 }
3615 $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
3616
3617 dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3618
3619 $resql = $this->db->query($sql);
3620 if ($resql) {
3621 $this->total_ht = 0;
3622 $this->total_tva = 0;
3623 $this->total_localtax1 = 0;
3624 $this->total_localtax2 = 0;
3625 $this->total_ttc = 0;
3626 $total_ht_by_vats = array();
3627 $total_tva_by_vats = array();
3628 $total_ttc_by_vats = array();
3629 $this->multicurrency_total_ht = 0;
3630 $this->multicurrency_total_tva = 0;
3631 $this->multicurrency_total_ttc = 0;
3632
3633 $this->db->begin();
3634
3635 $num = $this->db->num_rows($resql);
3636 $i = 0;
3637 while ($i < $num) {
3638 $obj = $this->db->fetch_object($resql);
3639
3640 // Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
3641 $parameters = array('fk_element' => $obj->rowid);
3642 $reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3643
3644 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'
3645 // This part of code is to fix data. We should not call it too often.
3646 $localtax_array = array($obj->localtax1_type, $obj->localtax1_tx, $obj->localtax2_type, $obj->localtax2_tx);
3647 $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);
3648
3649 $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.
3650 $diff_on_current_total = price2num($obj->total_ttc - $obj->total_ht - $obj->total_tva - $obj->total_localtax1 - $obj->total_localtax2, 'MT', 1);
3651 //var_dump($obj->total_ht.' '.$obj->total_tva.' '.$obj->total_localtax1.' '.$obj->total_localtax2.' => '.$obj->total_ttc);
3652 //var_dump($diff_when_using_price_ht.' '.$diff_on_current_total);
3653
3654 if ($diff_on_current_total) {
3655 // This should not happen, we should always have in table: total_ttc = total_ht + total_vat + total_localtax1 + total_localtax2
3656 $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);
3657 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);
3658 $resqlfix = $this->db->query($sqlfix);
3659 if (!$resqlfix) {
3660 dol_print_error($this->db, 'Failed to update line');
3661 }
3662 $obj->total_tva = $tmpcal[1];
3663 $obj->total_ttc = $tmpcal[2];
3664 } elseif ($diff_when_using_price_ht && $roundingadjust == '0') {
3665 // After calculation from HT, total is consistent but we have found a difference between VAT part in calculation and into database and
3666 // we ask to force the use of rounding on line (like done on calculation) so we force update of line
3667 $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);
3668 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);
3669 $resqlfix = $this->db->query($sqlfix);
3670 if (!$resqlfix) {
3671 dol_print_error($this->db, 'Failed to update line');
3672 }
3673 $obj->total_tva = $tmpcal[1];
3674 $obj->total_ttc = $tmpcal[2];
3675 }
3676 }
3677
3678 $this->total_ht += $obj->total_ht; // The field visible at end of line detail
3679 $this->total_tva += $obj->total_tva;
3680 $this->total_localtax1 += $obj->total_localtax1;
3681 $this->total_localtax2 += $obj->total_localtax2;
3682 $this->total_ttc += $obj->total_ttc;
3683 $this->multicurrency_total_ht += $obj->multicurrency_total_ht; // The field visible at end of line detail
3684 $this->multicurrency_total_tva += $obj->multicurrency_total_tva;
3685 $this->multicurrency_total_ttc += $obj->multicurrency_total_ttc;
3686
3687 if (!isset($total_ht_by_vats[$obj->vatrate])) {
3688 $total_ht_by_vats[$obj->vatrate] = 0;
3689 }
3690 if (!isset($total_tva_by_vats[$obj->vatrate])) {
3691 $total_tva_by_vats[$obj->vatrate] = 0;
3692 }
3693 if (!isset($total_ttc_by_vats[$obj->vatrate])) {
3694 $total_ttc_by_vats[$obj->vatrate] = 0;
3695 }
3696 $total_ht_by_vats[$obj->vatrate] += $obj->total_ht;
3697 $total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
3698 $total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
3699
3700 if ($forcedroundingmode == '1') { // Check if we need adjustement onto line for vat. TODO This works on the company currency but not on foreign currency
3701 $tmpvat = price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
3702 $diff = price2num($total_tva_by_vats[$obj->vatrate] - $tmpvat, 'MT', 1);
3703 //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";
3704 if ($diff) {
3705 if (abs($diff) > (10 * pow(10, -1 * getDolGlobalInt('MAIN_MAX_DECIMALS_TOT', 0)))) {
3706 // If error is more than 10 times the accurancy of rounding. This should not happen.
3707 $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.';
3708 dol_syslog($errmsg, LOG_WARNING);
3709 $this->error = $errmsg;
3710 $error++;
3711 break;
3712 }
3713 $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);
3714 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);
3715
3716 $resqlfix = $this->db->query($sqlfix);
3717
3718 if (!$resqlfix) {
3719 dol_print_error($this->db, 'Failed to update line');
3720 }
3721
3722 $this->total_tva = (float) price2num($this->total_tva - $diff, '', 1);
3723 $this->total_ttc = (float) price2num($this->total_ttc - $diff, '', 1);
3724 $total_tva_by_vats[$obj->vatrate] = (float) price2num($total_tva_by_vats[$obj->vatrate] - $diff, '', 1);
3725 $total_ttc_by_vats[$obj->vatrate] = (float) price2num($total_ttc_by_vats[$obj->vatrate] - $diff, '', 1);
3726 }
3727 }
3728
3729 $i++;
3730 }
3731
3732 // Add revenue stamp to total
3733 $this->total_ttc += isset($this->revenuestamp) ? $this->revenuestamp : 0;
3734 $this->multicurrency_total_ttc += isset($this->revenuestamp) ? ($this->revenuestamp * $multicurrency_tx) : 0;
3735
3736 // Situations totals
3737 if (!empty($this->situation_cycle_ref) && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits') && $this->type != $this::TYPE_CREDIT_NOTE) {
3738 $prev_sits = $this->get_prev_sits();
3739
3740 foreach ($prev_sits as $sit) { // $sit is an object Facture loaded with a fetch.
3741 $this->total_ht -= $sit->total_ht;
3742 $this->total_tva -= $sit->total_tva;
3743 $this->total_localtax1 -= $sit->total_localtax1;
3744 $this->total_localtax2 -= $sit->total_localtax2;
3745 $this->total_ttc -= $sit->total_ttc;
3746 $this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
3747 $this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
3748 $this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
3749 }
3750 }
3751
3752 // Clean total
3753 $this->total_ht = (float) price2num($this->total_ht);
3754 $this->total_tva = (float) price2num($this->total_tva);
3755 $this->total_localtax1 = (float) price2num($this->total_localtax1);
3756 $this->total_localtax2 = (float) price2num($this->total_localtax2);
3757 $this->total_ttc = (float) price2num($this->total_ttc);
3758
3759 $this->db->free($resql);
3760
3761 // Now update global fields total_ht, total_ttc, total_tva, total_localtax1, total_localtax2, multicurrency_total_* of main object
3762 $fieldht = 'total_ht';
3763 $fieldtva = 'tva';
3764 $fieldlocaltax1 = 'localtax1';
3765 $fieldlocaltax2 = 'localtax2';
3766 $fieldttc = 'total_ttc';
3767 // Specific code for backward compatibility with old field names
3768 if (in_array($this->element, array('propal', 'commande', 'facture', 'facturerec', 'supplier_proposal', 'order_supplier', 'facture_fourn', 'invoice_supplier', 'invoice_supplier_rec', 'expensereport'))) {
3769 $fieldtva = 'total_tva';
3770 }
3771
3772 if (!$error && empty($nodatabaseupdate)) {
3773 $sql = "UPDATE ".$this->db->prefix().$this->table_element.' SET';
3774 $sql .= " ".$fieldht." = ".((float) price2num($this->total_ht, 'MT', 1)).",";
3775 $sql .= " ".$fieldtva." = ".((float) price2num($this->total_tva, 'MT', 1)).",";
3776 $sql .= " ".$fieldlocaltax1." = ".((float) price2num($this->total_localtax1, 'MT', 1)).",";
3777 $sql .= " ".$fieldlocaltax2." = ".((float) price2num($this->total_localtax2, 'MT', 1)).",";
3778 $sql .= " ".$fieldttc." = ".((float) price2num($this->total_ttc, 'MT', 1));
3779 $sql .= ", multicurrency_total_ht = ".((float) price2num($this->multicurrency_total_ht, 'MT', 1));
3780 $sql .= ", multicurrency_total_tva = ".((float) price2num($this->multicurrency_total_tva, 'MT', 1));
3781 $sql .= ", multicurrency_total_ttc = ".((float) price2num($this->multicurrency_total_ttc, 'MT', 1));
3782 $sql .= " WHERE rowid = ".((int) $this->id);
3783
3784 dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3785 $resql = $this->db->query($sql);
3786
3787 if (!$resql) {
3788 $error++;
3789 $this->error = $this->db->lasterror();
3790 $this->errors[] = $this->db->lasterror();
3791 }
3792 }
3793
3794 if (!$error) {
3795 $this->db->commit();
3796 return 1;
3797 } else {
3798 $this->db->rollback();
3799 return -1;
3800 }
3801 } else {
3802 dol_print_error($this->db, 'Bad request in update_price');
3803 return -1;
3804 }
3805 }
3806
3807 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3818 public function add_object_linked($origin = null, $origin_id = null, $f_user = null, $notrigger = 0)
3819 {
3820 // phpcs:enable
3821 global $user, $hookmanager, $action;
3822 $origin = (!empty($origin) ? $origin : $this->origin);
3823 $origin_id = (!empty($origin_id) ? $origin_id : $this->origin_id);
3824 $f_user = isset($f_user) ? $f_user : $user;
3825
3826 // Special case
3827 if ($origin == 'order') {
3828 $origin = 'commande';
3829 }
3830 if ($origin == 'invoice') {
3831 $origin = 'facture';
3832 }
3833 if ($origin == 'invoice_template') {
3834 $origin = 'facturerec';
3835 }
3836 if ($origin == 'supplierorder') {
3837 $origin = 'order_supplier';
3838 }
3839
3840 // 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.
3841 // 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.
3842 $coremodule = array('knowledgemanagement', 'partnership', 'workstation', 'ticket', 'recruitment', 'eventorganization', 'asset');
3843 // Add module part to target type if object has $module property and isn't in core modules.
3844 $targettype = ((!empty($this->module) && ! in_array($this->module, $coremodule)) ? $this->module.'_' : '').$this->element;
3845
3846 $parameters = array('targettype'=>$targettype);
3847 // Hook for explicitly set the targettype if it must be differtent than $this->element
3848 $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3849 if ($reshook > 0) {
3850 if (!empty($hookmanager->resArray['targettype'])) $targettype = $hookmanager->resArray['targettype'];
3851 }
3852
3853 $this->db->begin();
3854 $error = 0;
3855
3856 $sql = "INSERT INTO " . $this->db->prefix() . "element_element (";
3857 $sql .= "fk_source";
3858 $sql .= ", sourcetype";
3859 $sql .= ", fk_target";
3860 $sql .= ", targettype";
3861 $sql .= ") VALUES (";
3862 $sql .= ((int) $origin_id);
3863 $sql .= ", '" . $this->db->escape($origin) . "'";
3864 $sql .= ", " . ((int) $this->id);
3865 $sql .= ", '" . $this->db->escape($targettype) . "'";
3866 $sql .= ")";
3867
3868 dol_syslog(get_class($this) . "::add_object_linked", LOG_DEBUG);
3869 if ($this->db->query($sql)) {
3870 if (!$notrigger) {
3871 // Call trigger
3872 $this->context['link_origin'] = $origin;
3873 $this->context['link_origin_id'] = $origin_id;
3874 $result = $this->call_trigger('OBJECT_LINK_INSERT', $f_user);
3875 if ($result < 0) {
3876 $error++;
3877 }
3878 // End call triggers
3879 }
3880 } else {
3881 $this->error = $this->db->lasterror();
3882 $error++;
3883 }
3884
3885 if (!$error) {
3886 $this->db->commit();
3887 return 1;
3888 } else {
3889 $this->db->rollback();
3890 return 0;
3891 }
3892 }
3893
3916 public function fetchObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1)
3917 {
3918 global $conf, $hookmanager, $action;
3919
3920 // Important for pdf generation time reduction
3921 // This boolean is true if $this->linkedObjects has already been loaded with all objects linked without filter
3922 if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
3923 return 1;
3924 }
3925
3926 $this->linkedObjectsIds = array();
3927 $this->linkedObjects = array();
3928
3929 $justsource = false;
3930 $justtarget = false;
3931 $withtargettype = false;
3932 $withsourcetype = false;
3933
3934 $parameters = array('sourcetype'=>$sourcetype, 'sourceid'=>$sourceid, 'targettype'=>$targettype, 'targetid'=>$targetid);
3935 // Hook for explicitly set the targettype if it must be differtent than $this->element
3936 $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3937 if ($reshook > 0) {
3938 if (!empty($hookmanager->resArray['sourcetype'])) $sourcetype = $hookmanager->resArray['sourcetype'];
3939 if (!empty($hookmanager->resArray['sourceid'])) $sourceid = $hookmanager->resArray['sourceid'];
3940 if (!empty($hookmanager->resArray['targettype'])) $targettype = $hookmanager->resArray['targettype'];
3941 if (!empty($hookmanager->resArray['targetid'])) $targetid = $hookmanager->resArray['targetid'];
3942 }
3943
3944 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid)) {
3945 $justsource = true; // the source (id and type) is a search criteria
3946 if (!empty($targettype)) {
3947 $withtargettype = true;
3948 }
3949 }
3950 if (!empty($targetid) && !empty($targettype) && empty($sourceid)) {
3951 $justtarget = true; // the target (id and type) is a search criteria
3952 if (!empty($sourcetype)) {
3953 $withsourcetype = true;
3954 }
3955 }
3956
3957 $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
3958 $targetid = (!empty($targetid) ? $targetid : $this->id);
3959 $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
3960 $targettype = (!empty($targettype) ? $targettype : $this->element);
3961
3962 /*if (empty($sourceid) && empty($targetid))
3963 {
3964 dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
3965 return -1;
3966 }*/
3967
3968 // Links between objects are stored in table element_element
3969 $sql = "SELECT rowid, fk_source, sourcetype, fk_target, targettype";
3970 $sql .= " FROM ".$this->db->prefix()."element_element";
3971 $sql .= " WHERE ";
3972 if ($justsource || $justtarget) {
3973 if ($justsource) {
3974 $sql .= "fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."'";
3975 if ($withtargettype) {
3976 $sql .= " AND targettype = '".$this->db->escape($targettype)."'";
3977 }
3978 } elseif ($justtarget) {
3979 $sql .= "fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."'";
3980 if ($withsourcetype) {
3981 $sql .= " AND sourcetype = '".$this->db->escape($sourcetype)."'";
3982 }
3983 }
3984 } else {
3985 $sql .= "(fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."')";
3986 $sql .= " ".$clause." (fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."')";
3987 if ($loadalsoobjects && $this->id > 0 && $sourceid == $this->id && $sourcetype == $this->element && $targetid == $this->id && $targettype == $this->element && $clause == 'OR') {
3988 $this->linkedObjectsFullLoaded[$this->id] = true;
3989 }
3990 }
3991 $sql .= " ORDER BY ".$orderby;
3992
3993 dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
3994 $resql = $this->db->query($sql);
3995 if ($resql) {
3996 $num = $this->db->num_rows($resql);
3997 $i = 0;
3998 while ($i < $num) {
3999 $obj = $this->db->fetch_object($resql);
4000 if ($justsource || $justtarget) {
4001 if ($justsource) {
4002 $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
4003 } elseif ($justtarget) {
4004 $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
4005 }
4006 } else {
4007 if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype) {
4008 $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
4009 }
4010 if ($obj->fk_target == $targetid && $obj->targettype == $targettype) {
4011 $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
4012 }
4013 }
4014 $i++;
4015 }
4016
4017 if (!empty($this->linkedObjectsIds)) {
4018 $tmparray = $this->linkedObjectsIds;
4019 foreach ($tmparray as $objecttype => $objectids) { // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
4020 // Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
4021 $module = $element = $subelement = $objecttype;
4022 $regs = array();
4023 if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
4024 && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
4025 $module = $element = $regs[1];
4026 $subelement = $regs[2];
4027 }
4028
4029 $classpath = $element.'/class';
4030 // To work with non standard classpath or module name
4031 if ($objecttype == 'facture') {
4032 $classpath = 'compta/facture/class';
4033 } elseif ($objecttype == 'facturerec') {
4034 $classpath = 'compta/facture/class';
4035 $module = 'facture';
4036 } elseif ($objecttype == 'propal') {
4037 $classpath = 'comm/propal/class';
4038 } elseif ($objecttype == 'supplier_proposal') {
4039 $classpath = 'supplier_proposal/class';
4040 } elseif ($objecttype == 'shipping') {
4041 $classpath = 'expedition/class';
4042 $subelement = 'expedition';
4043 $module = 'expedition';
4044 } elseif ($objecttype == 'delivery') {
4045 $classpath = 'delivery/class';
4046 $subelement = 'delivery';
4047 $module = 'delivery_note';
4048 } elseif ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier') {
4049 $classpath = 'fourn/class';
4050 $module = 'fournisseur';
4051 } elseif ($objecttype == 'fichinter') {
4052 $classpath = 'fichinter/class';
4053 $subelement = 'fichinter';
4054 $module = 'ficheinter';
4055 } elseif ($objecttype == 'subscription') {
4056 $classpath = 'adherents/class';
4057 $module = 'adherent';
4058 } elseif ($objecttype == 'contact') {
4059 $module = 'societe';
4060 }
4061 // Set classfile
4062 $classfile = strtolower($subelement);
4063 $classname = ucfirst($subelement);
4064
4065 if ($objecttype == 'order') {
4066 $classfile = 'commande';
4067 $classname = 'Commande';
4068 } elseif ($objecttype == 'invoice_supplier') {
4069 $classfile = 'fournisseur.facture';
4070 $classname = 'FactureFournisseur';
4071 } elseif ($objecttype == 'order_supplier') {
4072 $classfile = 'fournisseur.commande';
4073 $classname = 'CommandeFournisseur';
4074 } elseif ($objecttype == 'supplier_proposal') {
4075 $classfile = 'supplier_proposal';
4076 $classname = 'SupplierProposal';
4077 } elseif ($objecttype == 'facturerec') {
4078 $classfile = 'facture-rec';
4079 $classname = 'FactureRec';
4080 } elseif ($objecttype == 'subscription') {
4081 $classfile = 'subscription';
4082 $classname = 'Subscription';
4083 } elseif ($objecttype == 'project' || $objecttype == 'projet') {
4084 $classpath = 'projet/class';
4085 $classfile = 'project';
4086 $classname = 'Project';
4087 } elseif ($objecttype == 'conferenceorboothattendee') {
4088 $classpath = 'eventorganization/class';
4089 $classfile = 'conferenceorboothattendee';
4090 $classname = 'ConferenceOrBoothAttendee';
4091 $module = 'eventorganization';
4092 } elseif ($objecttype == 'conferenceorbooth') {
4093 $classpath = 'eventorganization/class';
4094 $classfile = 'conferenceorbooth';
4095 $classname = 'ConferenceOrBooth';
4096 $module = 'eventorganization';
4097 } elseif ($objecttype == 'mo') {
4098 $classpath = 'mrp/class';
4099 $classfile = 'mo';
4100 $classname = 'Mo';
4101 $module = 'mrp';
4102 }
4103
4104 // Here $module, $classfile and $classname are set, we can use them.
4105 if (isModEnabled($module) && (($element != $this->element) || $alsosametype)) {
4106 if ($loadalsoobjects && (is_numeric($loadalsoobjects) || ($loadalsoobjects === $objecttype))) {
4107 dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
4108 //print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
4109 if (class_exists($classname)) {
4110 foreach ($objectids as $i => $objectid) { // $i is rowid into llx_element_element
4111 $object = new $classname($this->db);
4112 $ret = $object->fetch($objectid);
4113 if ($ret >= 0) {
4114 $this->linkedObjects[$objecttype][$i] = $object;
4115 }
4116 }
4117 }
4118 }
4119 } else {
4120 unset($this->linkedObjectsIds[$objecttype]);
4121 }
4122 }
4123 }
4124 return 1;
4125 } else {
4126 dol_print_error($this->db);
4127 return -1;
4128 }
4129 }
4130
4137 public function clearObjectLinkedCache()
4138 {
4139 if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
4140 unset($this->linkedObjectsFullLoaded[$this->id]);
4141 }
4142
4143 return 1;
4144 }
4145
4158 public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $f_user = null, $notrigger = 0)
4159 {
4160 global $user;
4161 $updatesource = false;
4162 $updatetarget = false;
4163 $f_user = isset($f_user) ? $f_user : $user;
4164
4165 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4166 $updatesource = true;
4167 } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4168 $updatetarget = true;
4169 }
4170
4171 $this->db->begin();
4172 $error = 0;
4173
4174 $sql = "UPDATE " . $this->db->prefix() . "element_element SET ";
4175 if ($updatesource) {
4176 $sql .= "fk_source = " . ((int) $sourceid);
4177 $sql .= ", sourcetype = '" . $this->db->escape($sourcetype) . "'";
4178 $sql .= " WHERE fk_target = " . ((int) $this->id);
4179 $sql .= " AND targettype = '" . $this->db->escape($this->element) . "'";
4180 } elseif ($updatetarget) {
4181 $sql .= "fk_target = " . ((int) $targetid);
4182 $sql .= ", targettype = '" . $this->db->escape($targettype) . "'";
4183 $sql .= " WHERE fk_source = " . ((int) $this->id);
4184 $sql .= " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4185 }
4186
4187 dol_syslog(get_class($this) . "::updateObjectLinked", LOG_DEBUG);
4188 if ($this->db->query($sql)) {
4189 if (!$notrigger) {
4190 // Call trigger
4191 $this->context['link_source_id'] = $sourceid;
4192 $this->context['link_source_type'] = $sourcetype;
4193 $this->context['link_target_id'] = $targetid;
4194 $this->context['link_target_type'] = $targettype;
4195 $result = $this->call_trigger('OBJECT_LINK_MODIFY', $f_user);
4196 if ($result < 0) {
4197 $error++;
4198 }
4199 // End call triggers
4200 }
4201 } else {
4202 $this->error = $this->db->lasterror();
4203 $error++;
4204 }
4205
4206 if (!$error) {
4207 $this->db->commit();
4208 return 1;
4209 } else {
4210 $this->db->rollback();
4211 return -1;
4212 }
4213 }
4214
4228 public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = '', $f_user = null, $notrigger = 0)
4229 {
4230 global $user;
4231 $deletesource = false;
4232 $deletetarget = false;
4233 $f_user = isset($f_user) ? $f_user : $user;
4234
4235 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4236 $deletesource = true;
4237 } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4238 $deletetarget = true;
4239 }
4240
4241 $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
4242 $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
4243 $targetid = (!empty($targetid) ? $targetid : $this->id);
4244 $targettype = (!empty($targettype) ? $targettype : $this->element);
4245 $this->db->begin();
4246 $error = 0;
4247
4248 if (!$notrigger) {
4249 // Call trigger
4250 $this->context['link_id'] = $rowid;
4251 $this->context['link_source_id'] = $sourceid;
4252 $this->context['link_source_type'] = $sourcetype;
4253 $this->context['link_target_id'] = $targetid;
4254 $this->context['link_target_type'] = $targettype;
4255 $result = $this->call_trigger('OBJECT_LINK_DELETE', $f_user);
4256 if ($result < 0) {
4257 $error++;
4258 }
4259 // End call triggers
4260 }
4261
4262 if (!$error) {
4263 $sql = "DELETE FROM " . $this->db->prefix() . "element_element";
4264 $sql .= " WHERE";
4265 if ($rowid > 0) {
4266 $sql .= " rowid = " . ((int) $rowid);
4267 } else {
4268 if ($deletesource) {
4269 $sql .= " fk_source = " . ((int) $sourceid) . " AND sourcetype = '" . $this->db->escape($sourcetype) . "'";
4270 $sql .= " AND fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "'";
4271 } elseif ($deletetarget) {
4272 $sql .= " fk_target = " . ((int) $targetid) . " AND targettype = '" . $this->db->escape($targettype) . "'";
4273 $sql .= " AND fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4274 } else {
4275 $sql .= " (fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "')";
4276 $sql .= " OR";
4277 $sql .= " (fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "')";
4278 }
4279 }
4280
4281 dol_syslog(get_class($this) . "::deleteObjectLinked", LOG_DEBUG);
4282 if (!$this->db->query($sql)) {
4283 $this->error = $this->db->lasterror();
4284 $this->errors[] = $this->error;
4285 $error++;
4286 }
4287 }
4288
4289 if (!$error) {
4290 $this->db->commit();
4291 return 1;
4292 } else {
4293 $this->db->rollback();
4294 return 0;
4295 }
4296 }
4297
4307 public static function getAllItemsLinkedByObjectID($fk_object_where, $field_select, $field_where, $table_element)
4308 {
4309 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4310 return -1;
4311 }
4312
4313 global $db;
4314
4315 $sql = "SELECT ".$field_select." FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4316 $resql = $db->query($sql);
4317
4318 $TRes = array();
4319 if (!empty($resql)) {
4320 while ($res = $db->fetch_object($resql)) {
4321 $TRes[] = $res->{$field_select};
4322 }
4323 }
4324
4325 return $TRes;
4326 }
4327
4336 public static function deleteAllItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
4337 {
4338 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4339 return -1;
4340 }
4341
4342 global $db;
4343
4344 $sql = "DELETE FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4345 $resql = $db->query($sql);
4346
4347 if (empty($resql)) {
4348 return 0;
4349 }
4350
4351 return 1;
4352 }
4353
4364 public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '', $fieldstatus = 'fk_statut')
4365 {
4366 global $user, $langs, $conf;
4367
4368 $savElementId = $elementId; // To be used later to know if we were using the method using the id of this or not.
4369
4370 $elementId = (!empty($elementId) ? $elementId : $this->id);
4371 $elementTable = (!empty($elementType) ? $elementType : $this->table_element);
4372
4373 $this->db->begin();
4374
4375 if ($elementTable == 'facture_rec') {
4376 $fieldstatus = "suspended";
4377 }
4378 if ($elementTable == 'mailing') {
4379 $fieldstatus = "statut";
4380 }
4381 if ($elementTable == 'cronjob') {
4382 $fieldstatus = "status";
4383 }
4384 if ($elementTable == 'user') {
4385 $fieldstatus = "statut";
4386 }
4387 if ($elementTable == 'expensereport') {
4388 $fieldstatus = "fk_statut";
4389 }
4390 if ($elementTable == 'commande_fournisseur_dispatch') {
4391 $fieldstatus = "status";
4392 }
4393 if (isset($this->fields) && is_array($this->fields) && array_key_exists('status', $this->fields)) {
4394 $fieldstatus = 'status';
4395 }
4396
4397 $sql = "UPDATE ".$this->db->prefix().$elementTable;
4398 $sql .= " SET ".$fieldstatus." = ".((int) $status);
4399 // If status = 1 = validated, update also fk_user_valid
4400 // TODO Replace the test on $elementTable by doing a test on existence of the field in $this->fields
4401 if ($status == 1 && in_array($elementTable, array('expensereport', 'inventory'))) {
4402 $sql .= ", fk_user_valid = ".((int) $user->id);
4403 }
4404 if ($status == 1 && in_array($elementTable, array('expensereport'))) {
4405 $sql .= ", date_valid = '".$this->db->idate(dol_now())."'";
4406 }
4407 if ($status == 1 && in_array($elementTable, array('inventory'))) {
4408 $sql .= ", date_validation = '".$this->db->idate(dol_now())."'";
4409 }
4410 $sql .= " WHERE rowid = ".((int) $elementId);
4411 $sql .= " AND ".$fieldstatus." <> ".((int) $status); // We avoid update if status already correct
4412
4413 dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
4414 $resql = $this->db->query($sql);
4415 if ($resql) {
4416 $error = 0;
4417
4418 $nb_rows_affected = $this->db->affected_rows($resql); // should be 1 or 0 if status was already correct
4419
4420 if ($nb_rows_affected > 0) {
4421 if (empty($trigkey)) {
4422 // Try to guess trigkey (for backward compatibility, now we should have trigkey defined into the call of setStatus)
4423 if ($this->element == 'supplier_proposal' && $status == 2) {
4424 $trigkey = 'SUPPLIER_PROPOSAL_SIGN'; // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
4425 }
4426 if ($this->element == 'supplier_proposal' && $status == 3) {
4427 $trigkey = 'SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
4428 }
4429 if ($this->element == 'supplier_proposal' && $status == 4) {
4430 $trigkey = 'SUPPLIER_PROPOSAL_CLOSE'; // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
4431 }
4432 if ($this->element == 'fichinter' && $status == 3) {
4433 $trigkey = 'FICHINTER_CLASSIFY_DONE';
4434 }
4435 if ($this->element == 'fichinter' && $status == 2) {
4436 $trigkey = 'FICHINTER_CLASSIFY_BILLED';
4437 }
4438 if ($this->element == 'fichinter' && $status == 1) {
4439 $trigkey = 'FICHINTER_CLASSIFY_UNBILLED';
4440 }
4441 }
4442
4443 if ($trigkey) {
4444 // Call trigger
4445 $result = $this->call_trigger($trigkey, $user);
4446 if ($result < 0) {
4447 $error++;
4448 }
4449 // End call triggers
4450 }
4451 } else {
4452 // The status was probably already good. We do nothing more, no triggers.
4453 }
4454
4455 if (!$error) {
4456 $this->db->commit();
4457
4458 if (empty($savElementId)) {
4459 // If the element we update is $this (so $elementId was provided as null)
4460 if ($fieldstatus == 'tosell') {
4461 $this->status = $status;
4462 } elseif ($fieldstatus == 'tobuy') {
4463 $this->status_buy = $status;
4464 } else {
4465 $this->statut = $status;
4466 $this->status = $status;
4467 }
4468 }
4469
4470 return 1;
4471 } else {
4472 $this->db->rollback();
4473 dol_syslog(get_class($this)."::setStatut ".$this->error, LOG_ERR);
4474 return -1;
4475 }
4476 } else {
4477 $this->error = $this->db->lasterror();
4478 $this->db->rollback();
4479 return -1;
4480 }
4481 }
4482
4483
4491 public function getCanvas($id = 0, $ref = '')
4492 {
4493 global $conf;
4494
4495 if (empty($id) && empty($ref)) {
4496 return 0;
4497 }
4498 if (!empty($conf->global->MAIN_DISABLE_CANVAS)) {
4499 return 0; // To increase speed. Not enabled by default.
4500 }
4501
4502 // Clean parameters
4503 $ref = trim($ref);
4504
4505 $sql = "SELECT rowid, canvas";
4506 $sql .= " FROM ".$this->db->prefix().$this->table_element;
4507 $sql .= " WHERE entity IN (".getEntity($this->element).")";
4508 if (!empty($id)) {
4509 $sql .= " AND rowid = ".((int) $id);
4510 }
4511 if (!empty($ref)) {
4512 $sql .= " AND ref = '".$this->db->escape($ref)."'";
4513 }
4514
4515 $resql = $this->db->query($sql);
4516 if ($resql) {
4517 $obj = $this->db->fetch_object($resql);
4518 if ($obj) {
4519 $this->canvas = $obj->canvas;
4520 return 1;
4521 } else {
4522 return 0;
4523 }
4524 } else {
4525 dol_print_error($this->db);
4526 return -1;
4527 }
4528 }
4529
4530
4537 public function getSpecialCode($lineid)
4538 {
4539 $sql = "SELECT special_code FROM ".$this->db->prefix().$this->table_element_line;
4540 $sql .= " WHERE rowid = ".((int) $lineid);
4541 $resql = $this->db->query($sql);
4542 if ($resql) {
4543 $row = $this->db->fetch_row($resql);
4544 return (!empty($row[0]) ? $row[0] : 0);
4545 }
4546
4547 return 0;
4548 }
4549
4558 public function isObjectUsed($id = 0, $entity = 0)
4559 {
4560 global $langs;
4561
4562 if (empty($id)) {
4563 $id = $this->id;
4564 }
4565
4566 // Check parameters
4567 if (!isset($this->childtables) || !is_array($this->childtables) || count($this->childtables) == 0) {
4568 dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
4569 return -1;
4570 }
4571
4572 $arraytoscan = $this->childtables; // array('tablename'=>array('fk_element'=>'parentfield'), ...) or array('tablename'=>array('parent'=>table_parent, 'parentkey'=>'nameoffieldforparentfkkey'), ...)
4573 // For backward compatibility, we check if array is old format array('tablename1', 'tablename2', ...)
4574 $tmparray = array_keys($this->childtables);
4575 if (is_numeric($tmparray[0])) {
4576 $arraytoscan = array_flip($this->childtables);
4577 }
4578
4579 // Test if child exists
4580 $haschild = 0;
4581 foreach ($arraytoscan as $table => $element) {
4582 //print $id.'-'.$table.'-'.$elementname.'<br>';
4583 // Check if element can be deleted
4584 $sql = "SELECT COUNT(*) as nb";
4585 $sql.= " FROM ".$this->db->prefix().$table." as c";
4586 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4587 $sql.= ", ".$this->db->prefix().$element['parent']." as p";
4588 }
4589 if (!empty($element['fk_element'])) {
4590 $sql.= " WHERE c.".$element['fk_element']." = ".((int) $id);
4591 } else {
4592 $sql.= " WHERE c.".$this->fk_element." = ".((int) $id);
4593 }
4594 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4595 $sql.= " AND c.".$element['parentkey']." = p.rowid";
4596 }
4597 if (!empty($element['parent']) && !empty($element['parenttypefield']) && !empty($element['parenttypevalue'])) {
4598 $sql.= " AND c.".$element['parenttypefield']." = '".$this->db->escape($element['parenttypevalue'])."'";
4599 }
4600 if (!empty($entity)) {
4601 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4602 $sql.= " AND p.entity = ".((int) $entity);
4603 } else {
4604 $sql.= " AND c.entity = ".((int) $entity);
4605 }
4606 }
4607
4608 $resql = $this->db->query($sql);
4609 if ($resql) {
4610 $obj = $this->db->fetch_object($resql);
4611 if ($obj->nb > 0) {
4612 $langs->load("errors");
4613 //print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
4614 $haschild += $obj->nb;
4615 if (is_numeric($element)) { // very old usage array('table1', 'table2', ...)
4616 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $table);
4617 } elseif (is_string($element)) { // old usage array('table1' => 'TranslateKey1', 'table2' => 'TranslateKey2', ...)
4618 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element));
4619 } else { // new usage: $element['name']=Translation key
4620 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element['name']));
4621 }
4622 break; // We found at least one, we stop here
4623 }
4624 } else {
4625 $this->errors[] = $this->db->lasterror();
4626 return -1;
4627 }
4628 }
4629 if ($haschild > 0) {
4630 $this->errors[] = "ErrorRecordHasChildren";
4631 return $haschild;
4632 } else {
4633 return 0;
4634 }
4635 }
4636
4643 public function hasProductsOrServices($predefined = -1)
4644 {
4645 $nb = 0;
4646
4647 foreach ($this->lines as $key => $val) {
4648 $qualified = 0;
4649 if ($predefined == -1) {
4650 $qualified = 1;
4651 }
4652 if ($predefined == 1 && $val->fk_product > 0) {
4653 $qualified = 1;
4654 }
4655 if ($predefined == 0 && $val->fk_product <= 0) {
4656 $qualified = 1;
4657 }
4658 if ($predefined == 2 && $val->fk_product > 0 && $val->product_type == 0) {
4659 $qualified = 1;
4660 }
4661 if ($predefined == 3 && $val->fk_product > 0 && $val->product_type == 1) {
4662 $qualified = 1;
4663 }
4664 if ($qualified) {
4665 $nb++;
4666 }
4667 }
4668 dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
4669 return $nb;
4670 }
4671
4677 public function getTotalDiscount()
4678 {
4679 if (!empty($this->table_element_line) ) {
4680 $total_discount = 0.00;
4681
4682 $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
4683 $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
4684 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
4685
4686 dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
4687 $resql = $this->db->query($sql);
4688 if ($resql) {
4689 $num = $this->db->num_rows($resql);
4690 $i = 0;
4691 while ($i < $num) {
4692 $obj = $this->db->fetch_object($resql);
4693
4694 $pu_ht = $obj->pu_ht;
4695 $qty = $obj->qty;
4696 $total_ht = $obj->total_ht;
4697
4698 $total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
4699 $total_discount += $total_discount_line;
4700
4701 $i++;
4702 }
4703 }
4704
4705 //print $total_discount; exit;
4706 return price2num($total_discount);
4707 }
4708
4709 return null;
4710 }
4711
4712
4719 public function getTotalWeightVolume()
4720 {
4721 $totalWeight = 0;
4722 $totalVolume = 0;
4723 // defined for shipment only
4724 $totalOrdered = '';
4725 // defined for shipment only
4726 $totalToShip = '';
4727
4728 foreach ($this->lines as $line) {
4729 if (isset($line->qty_asked)) {
4730 if (empty($totalOrdered)) {
4731 $totalOrdered = 0; // Avoid warning because $totalOrdered is ''
4732 }
4733 $totalOrdered += $line->qty_asked; // defined for shipment only
4734 }
4735 if (isset($line->qty_shipped)) {
4736 if (empty($totalToShip)) {
4737 $totalToShip = 0; // Avoid warning because $totalToShip is ''
4738 }
4739 $totalToShip += $line->qty_shipped; // defined for shipment only
4740 } elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty)) {
4741 if (empty($totalToShip)) {
4742 $totalToShip = 0;
4743 }
4744 $totalToShip += $line->qty; // defined for reception only
4745 }
4746
4747 // Define qty, weight, volume, weight_units, volume_units
4748 if ($this->element == 'shipping') {
4749 // for shipments
4750 $qty = $line->qty_shipped ? $line->qty_shipped : 0;
4751 } else {
4752 $qty = $line->qty ? $line->qty : 0;
4753 }
4754
4755 $weight = !empty($line->weight) ? $line->weight : 0;
4756 ($weight == 0 && !empty($line->product->weight)) ? $weight = $line->product->weight : 0;
4757 $volume = !empty($line->volume) ? $line->volume : 0;
4758 ($volume == 0 && !empty($line->product->volume)) ? $volume = $line->product->volume : 0;
4759
4760 $weight_units = !empty($line->weight_units) ? $line->weight_units : 0;
4761 ($weight_units == 0 && !empty($line->product->weight_units)) ? $weight_units = $line->product->weight_units : 0;
4762 $volume_units = !empty($line->volume_units) ? $line->volume_units : 0;
4763 ($volume_units == 0 && !empty($line->product->volume_units)) ? $volume_units = $line->product->volume_units : 0;
4764
4765 $weightUnit = 0;
4766 $volumeUnit = 0;
4767 if (!empty($weight_units)) {
4768 $weightUnit = $weight_units;
4769 }
4770 if (!empty($volume_units)) {
4771 $volumeUnit = $volume_units;
4772 }
4773
4774 if (empty($totalWeight)) {
4775 $totalWeight = 0; // Avoid warning because $totalWeight is ''
4776 }
4777 if (empty($totalVolume)) {
4778 $totalVolume = 0; // Avoid warning because $totalVolume is ''
4779 }
4780
4781 //var_dump($line->volume_units);
4782 if ($weight_units < 50) { // < 50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
4783 $trueWeightUnit = pow(10, $weightUnit);
4784 $totalWeight += $weight * $qty * $trueWeightUnit;
4785 } else {
4786 if ($weight_units == 99) {
4787 // conversion 1 Pound = 0.45359237 KG
4788 $trueWeightUnit = 0.45359237;
4789 $totalWeight += $weight * $qty * $trueWeightUnit;
4790 } elseif ($weight_units == 98) {
4791 // conversion 1 Ounce = 0.0283495 KG
4792 $trueWeightUnit = 0.0283495;
4793 $totalWeight += $weight * $qty * $trueWeightUnit;
4794 } else {
4795 $totalWeight += $weight * $qty; // This may be wrong if we mix different units
4796 }
4797 }
4798 if ($volume_units < 50) { // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
4799 //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
4800 $trueVolumeUnit = pow(10, $volumeUnit);
4801 //print $line->volume;
4802 $totalVolume += $volume * $qty * $trueVolumeUnit;
4803 } else {
4804 $totalVolume += $volume * $qty; // This may be wrong if we mix different units
4805 }
4806 }
4807
4808 return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
4809 }
4810
4811
4817 public function setExtraParameters()
4818 {
4819 $this->db->begin();
4820
4821 $extraparams = (!empty($this->extraparams) ? json_encode($this->extraparams) : null);
4822
4823 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
4824 $sql .= " SET extraparams = ".(!empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
4825 $sql .= " WHERE rowid = ".((int) $this->id);
4826
4827 dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
4828 $resql = $this->db->query($sql);
4829 if (!$resql) {
4830 $this->error = $this->db->lasterror();
4831 $this->db->rollback();
4832 return -1;
4833 } else {
4834 $this->db->commit();
4835 return 1;
4836 }
4837 }
4838
4839
4840 // --------------------
4841 // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
4842 // --------------------
4843
4844 /* This is to show add lines */
4845
4855 public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl')
4856 {
4857 global $conf, $user, $langs, $object, $hookmanager, $extrafields;
4858 global $form;
4859
4860 // Line extrafield
4861 if (!is_object($extrafields)) {
4862 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4863 $extrafields = new ExtraFields($this->db);
4864 }
4865 $extrafields->fetch_name_optionals_label($this->table_element_line);
4866
4867 // Output template part (modules that overwrite templates must declare this into descriptor)
4868 // Use global variables + $dateSelector + $seller and $buyer
4869 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'.
4870 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4871 foreach ($dirtpls as $module => $reldir) {
4872 if (!empty($module)) {
4873 $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
4874 } else {
4875 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php';
4876 }
4877
4878 if (empty($conf->file->strict_mode)) {
4879 $res = @include $tpl;
4880 } else {
4881 $res = include $tpl; // for debug
4882 }
4883 if ($res) {
4884 break;
4885 }
4886 }
4887 }
4888
4889
4890
4891 /* This is to show array of line of details */
4892
4893
4908 public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl')
4909 {
4910 global $conf, $hookmanager, $langs, $user, $form, $extrafields, $object;
4911 // TODO We should not use global var for this
4912 global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
4913
4914 // Define usemargins
4915 $usemargins = 0;
4916 if (isModEnabled('margin') && !empty($this->element) && in_array($this->element, array('facture', 'facturerec', 'propal', 'commande'))) {
4917 $usemargins = 1;
4918 }
4919
4920 $num = count($this->lines);
4921
4922 // Line extrafield
4923 if (!is_object($extrafields)) {
4924 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4925 $extrafields = new ExtraFields($this->db);
4926 }
4927 $extrafields->fetch_name_optionals_label($this->table_element_line);
4928
4929 $parameters = array('num'=>$num, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$this->table_element_line);
4930 $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4931 if (empty($reshook)) {
4932 // Output template part (modules that overwrite templates must declare this into descriptor)
4933 // Use global variables + $dateSelector + $seller and $buyer
4934 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook.
4935 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4936 foreach ($dirtpls as $module => $reldir) {
4937 $res = 0;
4938 if (!empty($module)) {
4939 $tpl = dol_buildpath($reldir.'/objectline_title.tpl.php');
4940 } else {
4941 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php';
4942 }
4943 if (file_exists($tpl)) {
4944 if (empty($conf->file->strict_mode)) {
4945 $res = @include $tpl;
4946 } else {
4947 $res = include $tpl; // for debug
4948 }
4949 }
4950 if ($res) {
4951 break;
4952 }
4953 }
4954 }
4955
4956 $i = 0;
4957
4958 print "<!-- begin printObjectLines() --><tbody>\n";
4959 foreach ($this->lines as $line) {
4960 //Line extrafield
4961 $line->fetch_optionals();
4962
4963 //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line)))
4964 if (is_object($hookmanager)) { // Old code is commented on preceding line.
4965 if (empty($line->fk_parent_line)) {
4966 $parameters = array('line'=>$line, 'num'=>$num, 'i'=>$i, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$line->table_element, 'defaulttpldir'=>$defaulttpldir);
4967 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4968 } else {
4969 $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);
4970 $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4971 }
4972 }
4973 if (empty($reshook)) {
4974 $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafields, $defaulttpldir);
4975 }
4976
4977 $i++;
4978 }
4979 print "</tbody><!-- end printObjectLines() -->\n";
4980 }
4981
4999 public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/core/tpl')
5000 {
5001 global $conf, $langs, $user, $object, $hookmanager;
5002 global $form;
5003 global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
5004
5005 $object_rights = $this->getRights();
5006
5007 $text = '';
5008 $description = '';
5009
5010 // Line in view mode
5011 if ($action != 'editline' || $selected != $line->id) {
5012 // Product
5013 if (!empty($line->fk_product) && $line->fk_product > 0) {
5014 $product_static = new Product($this->db);
5015 $product_static->fetch($line->fk_product);
5016
5017 $product_static->ref = $line->ref; //can change ref in hook
5018 $product_static->label = !empty($line->label) ? $line->label : ""; //can change label in hook
5019
5020 $text = $product_static->getNomUrl(1);
5021
5022 // Define output language and label
5023 if (getDolGlobalInt('MAIN_MULTILANGS')) {
5024 if (property_exists($this, 'socid') && !is_object($this->thirdparty)) {
5025 dol_print_error('', 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
5026 return;
5027 }
5028
5029 $prod = new Product($this->db);
5030 $prod->fetch($line->fk_product);
5031
5032 $outputlangs = $langs;
5033 $newlang = '';
5034 if (empty($newlang) && GETPOST('lang_id', 'aZ09')) {
5035 $newlang = GETPOST('lang_id', 'aZ09');
5036 }
5037 if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang) && is_object($this->thirdparty)) {
5038 $newlang = $this->thirdparty->default_lang; // To use language of customer
5039 }
5040 if (!empty($newlang)) {
5041 $outputlangs = new Translate("", $conf);
5042 $outputlangs->setDefaultLang($newlang);
5043 }
5044
5045 $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
5046 } else {
5047 $label = $line->product_label;
5048 }
5049
5050 $text .= ' - '.(!empty($line->label) ? $line->label : $label);
5051 $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.
5052 }
5053
5054 $line->pu_ttc = price2num((!empty($line->subprice) ? $line->subprice : 0) * (1 + ((!empty($line->tva_tx) ? $line->tva_tx : 0) / 100)), 'MU');
5055
5056 // Output template part (modules that overwrite templates must declare this into descriptor)
5057 // Use global variables + $dateSelector + $seller and $buyer
5058 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5059 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5060 foreach ($dirtpls as $module => $reldir) {
5061 $res = 0;
5062 if (!empty($module)) {
5063 $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
5064 } else {
5065 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php';
5066 }
5067 if (file_exists($tpl)) {
5068 if (empty($conf->file->strict_mode)) {
5069 $res = @include $tpl;
5070 } else {
5071 $res = include $tpl; // for debug
5072 }
5073 }
5074 if ($res) {
5075 break;
5076 }
5077 }
5078 }
5079
5080 // Line in update mode
5081 if ($this->statut == 0 && $action == 'editline' && $selected == $line->id) {
5082 $label = (!empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
5083
5084 $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU');
5085
5086 // Output template part (modules that overwrite templates must declare this into descriptor)
5087 // Use global variables + $dateSelector + $seller and $buyer
5088 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5089 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5090 foreach ($dirtpls as $module => $reldir) {
5091 if (!empty($module)) {
5092 $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
5093 } else {
5094 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php';
5095 }
5096
5097 if (empty($conf->file->strict_mode)) {
5098 $res = @include $tpl;
5099 } else {
5100 $res = include $tpl; // for debug
5101 }
5102 if ($res) {
5103 break;
5104 }
5105 }
5106 }
5107 }
5108
5109
5110 /* This is to show array of line of details of source object */
5111
5112
5123 public function printOriginLinesList($restrictlist = '', $selectedLines = array())
5124 {
5125 global $langs, $hookmanager, $conf, $form, $action;
5126
5127 print '<tr class="liste_titre">';
5128 print '<td class="linecolref">'.$langs->trans('Ref').'</td>';
5129 print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
5130 print '<td class="linecolvat right">'.$langs->trans('VATRate').'</td>';
5131 print '<td class="linecoluht right">'.$langs->trans('PriceUHT').'</td>';
5132 if (isModEnabled("multicurrency")) {
5133 print '<td class="linecoluht_currency right">'.$langs->trans('PriceUHTCurrency').'</td>';
5134 }
5135 print '<td class="linecolqty right">'.$langs->trans('Qty').'</td>';
5136 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5137 print '<td class="linecoluseunit left">'.$langs->trans('Unit').'</td>';
5138 }
5139 print '<td class="linecoldiscount right">'.$langs->trans('ReductionShort').'</td>';
5140 print '<td class="linecolht right">'.$langs->trans('TotalHT').'</td>';
5141 print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
5142 print '</tr>';
5143 $i = 0;
5144
5145 if (!empty($this->lines)) {
5146 foreach ($this->lines as $line) {
5147 $reshook = 0;
5148 //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line))) {
5149 if (is_object($hookmanager)) { // Old code is commented on preceding line.
5150 $parameters = array('line'=>$line, 'i'=>$i, 'restrictlist'=>$restrictlist, 'selectedLines'=> $selectedLines);
5151 if (!empty($line->fk_parent_line)) { $parameters['fk_parent_line'] = $line->fk_parent_line; }
5152 $reshook = $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5153 }
5154 if (empty($reshook)) {
5155 $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines);
5156 }
5157
5158 $i++;
5159 }
5160 }
5161 }
5162
5176 public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
5177 {
5178 global $langs, $conf;
5179
5180 //var_dump($line);
5181 if (!empty($line->date_start)) {
5182 $date_start = $line->date_start;
5183 } else {
5184 $date_start = $line->date_debut_prevue;
5185 if ($line->date_debut_reel) {
5186 $date_start = $line->date_debut_reel;
5187 }
5188 }
5189 if (!empty($line->date_end)) {
5190 $date_end = $line->date_end;
5191 } else {
5192 $date_end = $line->date_fin_prevue;
5193 if ($line->date_fin_reel) {
5194 $date_end = $line->date_fin_reel;
5195 }
5196 }
5197
5198 $this->tpl['id'] = $line->id;
5199
5200 $this->tpl['label'] = '';
5201 if (!empty($line->fk_parent_line)) {
5202 $this->tpl['label'] .= img_picto('', 'rightarrow');
5203 }
5204
5205 if (($line->info_bits & 2) == 2) { // TODO Not sure this is used for source object
5206 $discount = new DiscountAbsolute($this->db);
5207 $discount->fk_soc = $this->socid;
5208 $this->tpl['label'] .= $discount->getNomUrl(0, 'discount');
5209 } elseif (!empty($line->fk_product)) {
5210 $productstatic = new Product($this->db);
5211 $productstatic->id = $line->fk_product;
5212 $productstatic->ref = $line->ref;
5213 $productstatic->type = $line->fk_product_type;
5214 if (empty($productstatic->ref)) {
5215 $line->fetch_product();
5216 $productstatic = $line->product;
5217 }
5218
5219 $this->tpl['label'] .= $productstatic->getNomUrl(1);
5220 $this->tpl['label'] .= ' - '.(!empty($line->label) ? $line->label : $line->product_label);
5221 // Dates
5222 if ($line->product_type == 1 && ($date_start || $date_end)) {
5223 $this->tpl['label'] .= get_date_range($date_start, $date_end);
5224 }
5225 } else {
5226 $this->tpl['label'] .= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
5227 if (!empty($line->desc)) {
5228 $this->tpl['label'] .= $line->desc;
5229 } else {
5230 $this->tpl['label'] .= ($line->label ? '&nbsp;'.$line->label : '');
5231 }
5232
5233 // Dates
5234 if ($line->product_type == 1 && ($date_start || $date_end)) {
5235 $this->tpl['label'] .= get_date_range($date_start, $date_end);
5236 }
5237 }
5238
5239 if (!empty($line->desc)) {
5240 if ($line->desc == '(CREDIT_NOTE)') { // TODO Not sure this is used for source object
5241 $discount = new DiscountAbsolute($this->db);
5242 $discount->fetch($line->fk_remise_except);
5243 $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
5244 } elseif ($line->desc == '(DEPOSIT)') { // TODO Not sure this is used for source object
5245 $discount = new DiscountAbsolute($this->db);
5246 $discount->fetch($line->fk_remise_except);
5247 $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
5248 } elseif ($line->desc == '(EXCESS RECEIVED)') {
5249 $discount = new DiscountAbsolute($this->db);
5250 $discount->fetch($line->fk_remise_except);
5251 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
5252 } elseif ($line->desc == '(EXCESS PAID)') {
5253 $discount = new DiscountAbsolute($this->db);
5254 $discount->fetch($line->fk_remise_except);
5255 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
5256 } else {
5257 $this->tpl['description'] = dol_trunc($line->desc, 60);
5258 }
5259 } else {
5260 $this->tpl['description'] = '&nbsp;';
5261 }
5262
5263 // VAT Rate
5264 $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
5265 $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
5266 if (!empty($line->vat_src_code) && !preg_match('/\‍(/', $this->tpl['vat_rate'])) {
5267 $this->tpl['vat_rate'] .= ' ('.$line->vat_src_code.')';
5268 }
5269
5270 $this->tpl['price'] = price($line->subprice);
5271 $this->tpl['total_ht'] = price($line->total_ht);
5272 $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
5273 $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
5274 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5275 $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
5276 }
5277 $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
5278
5279 // Is the line strike or not
5280 $this->tpl['strike'] = 0;
5281 if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) {
5282 $this->tpl['strike'] = 1;
5283 }
5284
5285 // Output template part (modules that overwrite templates must declare this into descriptor)
5286 // Use global variables + $dateSelector + $seller and $buyer
5287 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5288 foreach ($dirtpls as $module => $reldir) {
5289 if (!empty($module)) {
5290 $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
5291 } else {
5292 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php';
5293 }
5294
5295 if (empty($conf->file->strict_mode)) {
5296 $res = @include $tpl;
5297 } else {
5298 $res = include $tpl; // for debug
5299 }
5300 if ($res) {
5301 break;
5302 }
5303 }
5304 }
5305
5306
5307 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5318 public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
5319 {
5320 // phpcs:enable
5321 $this->db->begin();
5322
5323 $sql = "INSERT INTO ".$this->db->prefix()."element_resources (";
5324 $sql .= "resource_id";
5325 $sql .= ", resource_type";
5326 $sql .= ", element_id";
5327 $sql .= ", element_type";
5328 $sql .= ", busy";
5329 $sql .= ", mandatory";
5330 $sql .= ") VALUES (";
5331 $sql .= ((int) $resource_id);
5332 $sql .= ", '".$this->db->escape($resource_type)."'";
5333 $sql .= ", '".$this->db->escape($this->id)."'";
5334 $sql .= ", '".$this->db->escape($this->element)."'";
5335 $sql .= ", '".$this->db->escape($busy)."'";
5336 $sql .= ", '".$this->db->escape($mandatory)."'";
5337 $sql .= ")";
5338
5339 dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
5340 if ($this->db->query($sql)) {
5341 $this->db->commit();
5342 return 1;
5343 } else {
5344 $this->error = $this->db->lasterror();
5345 $this->db->rollback();
5346 return 0;
5347 }
5348 }
5349
5350 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5359 public function delete_resource($rowid, $element, $notrigger = 0)
5360 {
5361 // phpcs:enable
5362 global $user;
5363
5364 $this->db->begin();
5365
5366 $sql = "DELETE FROM ".$this->db->prefix()."element_resources";
5367 $sql .= " WHERE rowid = ".((int) $rowid);
5368
5369 dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
5370
5371 $resql = $this->db->query($sql);
5372 if (!$resql) {
5373 $this->error = $this->db->lasterror();
5374 $this->db->rollback();
5375 return -1;
5376 } else {
5377 if (!$notrigger) {
5378 $result = $this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
5379 if ($result < 0) {
5380 $this->db->rollback();
5381 return -1;
5382 }
5383 }
5384 $this->db->commit();
5385 return 1;
5386 }
5387 }
5388
5389
5395 public function __clone()
5396 {
5397 // Force a copy of this->lines, otherwise it will point to same object.
5398 if (isset($this->lines) && is_array($this->lines)) {
5399 $nboflines = count($this->lines);
5400 for ($i = 0; $i < $nboflines; $i++) {
5401 if (is_object($this->lines[$i])) {
5402 $this->lines[$i] = clone $this->lines[$i];
5403 }
5404 }
5405 }
5406 }
5407
5421 protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
5422 {
5423 global $conf, $langs, $user, $hookmanager, $action;
5424
5425 $srctemplatepath = '';
5426
5427 $parameters = array('modelspath'=>$modelspath, 'modele'=>$modele, 'outputlangs'=>$outputlangs, 'hidedetails'=>$hidedetails, 'hidedesc'=>$hidedesc, 'hideref'=>$hideref, 'moreparams'=>$moreparams);
5428 $reshook = $hookmanager->executeHooks('commonGenerateDocument', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5429
5430 if (!empty($reshook)) {
5431 return $reshook;
5432 }
5433
5434 dol_syslog("commonGenerateDocument modele=".$modele." outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
5435
5436 if (empty($modele)) {
5437 $this->error = 'BadValueForParameterModele';
5438 return -1;
5439 }
5440
5441 // Increase limit for PDF build
5442 $err = error_reporting();
5443 error_reporting(0);
5444 @set_time_limit(120);
5445 error_reporting($err);
5446
5447 // If selected model is a filename template (then $modele="modelname" or "modelname:filename")
5448 $tmp = explode(':', $modele, 2);
5449 if (!empty($tmp[1])) {
5450 $modele = $tmp[0];
5451 $srctemplatepath = $tmp[1];
5452 }
5453
5454 // Search template files
5455 $file = '';
5456 $classname = '';
5457 $filefound = '';
5458 $dirmodels = array('/');
5459 if (is_array($conf->modules_parts['models'])) {
5460 $dirmodels = array_merge($dirmodels, $conf->modules_parts['models']);
5461 }
5462 foreach ($dirmodels as $reldir) {
5463 foreach (array('doc', 'pdf') as $prefix) {
5464 if (in_array(get_class($this), array('Adherent'))) {
5465 // Member module use prefix_modele.class.php
5466 $file = $prefix."_".$modele.".class.php";
5467 } else {
5468 // Other module use prefix_modele.modules.php
5469 $file = $prefix."_".$modele.".modules.php";
5470 }
5471
5472 // On verifie l'emplacement du modele
5473 $file = dol_buildpath($reldir.$modelspath.$file, 0);
5474 if (file_exists($file)) {
5475 $filefound = $file;
5476 $classname = $prefix.'_'.$modele;
5477 break;
5478 }
5479 }
5480 if ($filefound) {
5481 break;
5482 }
5483 }
5484
5485 if (!$filefound) {
5486 $this->error = $langs->trans("Error").' Failed to load doc generator with modelpaths='.$modelspath.' - modele='.$modele;
5487 $this->errors[] = $this->error;
5488 dol_syslog($this->error, LOG_ERR);
5489 return -1;
5490 }
5491
5492 // If generator was found
5493 global $db; // Required to solve a conception default making an include of code using $db instead of $this->db just after.
5494
5495 require_once $file;
5496
5497 $obj = new $classname($this->db);
5498
5499 // If generator is ODT, we must have srctemplatepath defined, if not we set it.
5500 if ($obj->type == 'odt' && empty($srctemplatepath)) {
5501 $varfortemplatedir = $obj->scandir;
5502 if ($varfortemplatedir && !empty($conf->global->$varfortemplatedir)) {
5503 $dirtoscan = $conf->global->$varfortemplatedir;
5504
5505 $listoffiles = array();
5506
5507 // Now we add first model found in directories scanned
5508 $listofdir = explode(',', $dirtoscan);
5509 foreach ($listofdir as $key => $tmpdir) {
5510 $tmpdir = trim($tmpdir);
5511 $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
5512 if (!$tmpdir) {
5513 unset($listofdir[$key]);
5514 continue;
5515 }
5516 if (is_dir($tmpdir)) {
5517 $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
5518 if (count($tmpfiles)) {
5519 $listoffiles = array_merge($listoffiles, $tmpfiles);
5520 }
5521 }
5522 }
5523
5524 if (count($listoffiles)) {
5525 foreach ($listoffiles as $record) {
5526 $srctemplatepath = $record['fullname'];
5527 break;
5528 }
5529 }
5530 }
5531
5532 if (empty($srctemplatepath)) {
5533 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
5534 return -1;
5535 }
5536 }
5537
5538 if ($obj->type == 'odt' && !empty($srctemplatepath)) {
5539 if (!dol_is_file($srctemplatepath)) {
5540 dol_syslog("Failed to locate template file ".$srctemplatepath, LOG_WARNING);
5541 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
5542 return -1;
5543 }
5544 }
5545
5546 // We save charset_output to restore it because write_file can change it if needed for
5547 // output format that does not support UTF8.
5548 $sav_charset_output = empty($outputlangs->charset_output) ? '' : $outputlangs->charset_output;
5549
5550 if (in_array(get_class($this), array('Adherent'))) {
5551 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, 'tmp_cards', $moreparams);
5552 } else {
5553 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
5554 }
5555 // After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
5556
5557 if ($resultwritefile > 0) {
5558 $outputlangs->charset_output = $sav_charset_output;
5559
5560 // We delete old preview
5561 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5562 dol_delete_preview($this);
5563
5564 // Index file in database
5565 if (!empty($obj->result['fullpath'])) {
5566 $destfull = $obj->result['fullpath'];
5567
5568 // Update the last_main_doc field into main object (if document generator has property ->update_main_doc_field set)
5569 $update_main_doc_field = 0;
5570 if (!empty($obj->update_main_doc_field)) {
5571 $update_main_doc_field = 1;
5572 }
5573
5574 // Check that the file exists, before indexing it.
5575 // Hint: It does not exist, if we create a PDF and auto delete the ODT File
5576 if (dol_is_file($destfull)) {
5577 $this->indexFile($destfull, $update_main_doc_field);
5578 }
5579 } else {
5580 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);
5581 }
5582
5583 // Success in building document. We build meta file.
5584 dol_meta_create($this);
5585
5586 return 1;
5587 } else {
5588 $outputlangs->charset_output = $sav_charset_output;
5589 $this->error = $obj->error;
5590 $this->errors = $obj->errors;
5591 dol_syslog("Error generating document for ".__CLASS__.". Error: ".$obj->error, LOG_ERR);
5592 return -1;
5593 }
5594 }
5595
5605 public function indexFile($destfull, $update_main_doc_field)
5606 {
5607 global $conf, $user;
5608
5609 $upload_dir = dirname($destfull);
5610 $destfile = basename($destfull);
5611 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
5612
5613 if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a tmp dir
5614 $filename = basename($destfile);
5615 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
5616 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
5617
5618 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
5619 $ecmfile = new EcmFiles($this->db);
5620 $result = $ecmfile->fetch(0, '', ($rel_dir ? $rel_dir.'/' : '').$filename);
5621
5622 // Set the public "share" key
5623 $setsharekey = false;
5624 if ($this->element == 'propal' || $this->element == 'proposal') {
5625 if (!isset($conf->global->PROPOSAL_ALLOW_ONLINESIGN) || !empty($conf->global->PROPOSAL_ALLOW_ONLINESIGN)) {
5626 $setsharekey = true; // feature to make online signature is not set or set to on (default)
5627 }
5628 if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) {
5629 $setsharekey = true;
5630 }
5631 }
5632 if ($this->element == 'commande' && !empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD)) {
5633 $setsharekey = true;
5634 }
5635 if ($this->element == 'facture' && !empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD)) {
5636 $setsharekey = true;
5637 }
5638 if ($this->element == 'bank_account' && !empty($conf->global->BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD)) {
5639 $setsharekey = true;
5640 }
5641 if ($this->element == 'product' && !empty($conf->global->PRODUCT_ALLOW_EXTERNAL_DOWNLOAD)) {
5642 $setsharekey = true;
5643 }
5644 if ($this->element == 'contrat' && !empty($conf->global->CONTRACT_ALLOW_EXTERNAL_DOWNLOAD)) {
5645 $setsharekey = true;
5646 }
5647 if ($this->element == 'fichinter' && !empty($conf->global->FICHINTER_ALLOW_EXTERNAL_DOWNLOAD)) {
5648 $setsharekey = true;
5649 }
5650 if ($this->element == 'supplier_proposal' && !empty($conf->global->SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) {
5651 $setsharekey = true;
5652 }
5653
5654 if ($setsharekey) {
5655 if (empty($ecmfile->share)) { // Because object not found or share not set yet
5656 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
5657 $ecmfile->share = getRandomPassword(true);
5658 }
5659 }
5660
5661 if ($result > 0) {
5662 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5663 $ecmfile->fullpath_orig = '';
5664 $ecmfile->gen_or_uploaded = 'generated';
5665 $ecmfile->description = ''; // indexed content
5666 $ecmfile->keywords = ''; // keyword content
5667 $result = $ecmfile->update($user);
5668 if ($result < 0) {
5669 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5670 return -1;
5671 }
5672 } else {
5673 $ecmfile->entity = $conf->entity;
5674 $ecmfile->filepath = $rel_dir;
5675 $ecmfile->filename = $filename;
5676 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5677 $ecmfile->fullpath_orig = '';
5678 $ecmfile->gen_or_uploaded = 'generated';
5679 $ecmfile->description = ''; // indexed content
5680 $ecmfile->keywords = ''; // keyword content
5681 $ecmfile->src_object_type = $this->table_element; // $this->table_name is 'myobject' or 'mymodule_myobject'.
5682 $ecmfile->src_object_id = $this->id;
5683
5684 $result = $ecmfile->create($user);
5685 if ($result < 0) {
5686 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5687 return -1;
5688 }
5689 }
5690
5691 /*$this->result['fullname']=$destfull;
5692 $this->result['filepath']=$ecmfile->filepath;
5693 $this->result['filename']=$ecmfile->filename;*/
5694 //var_dump($obj->update_main_doc_field);exit;
5695
5696 if ($update_main_doc_field && !empty($this->table_element)) {
5697 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath."/".$ecmfile->filename)."'";
5698 $sql .= " WHERE rowid = ".((int) $this->id);
5699
5700 $resql = $this->db->query($sql);
5701 if (!$resql) {
5702 dol_print_error($this->db);
5703 return -1;
5704 } else {
5705 $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
5706 }
5707 }
5708 }
5709
5710 return 1;
5711 }
5712
5720 public function addThumbs($file)
5721 {
5722 $file_osencoded = dol_osencode($file);
5723
5724 if (file_exists($file_osencoded)) {
5725 require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
5726
5727 $tmparraysize = getDefaultImageSizes();
5728 $maxwidthsmall = $tmparraysize['maxwidthsmall'];
5729 $maxheightsmall = $tmparraysize['maxheightsmall'];
5730 $maxwidthmini = $tmparraysize['maxwidthmini'];
5731 $maxheightmini = $tmparraysize['maxheightmini'];
5732 //$quality = $tmparraysize['quality'];
5733 $quality = 50; // For thumbs, we force quality to 50
5734
5735 // Create small thumbs for company (Ratio is near 16/9)
5736 // Used on logon for example
5737 vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
5738
5739 // Create mini thumbs for company (Ratio is near 16/9)
5740 // Used on menu or for setup page for example
5741 vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
5742 }
5743 }
5744
5752 public function delThumbs($file)
5753 {
5754 $imgThumbName = getImageFileNameForSize($file, '_small'); // Full path of thumb file
5755 dol_delete_file($imgThumbName);
5756 $imgThumbName = getImageFileNameForSize($file, '_mini'); // Full path of thumb file
5757 dol_delete_file($imgThumbName);
5758 }
5759
5760
5761 /* Functions common to commonobject and commonobjectline */
5762
5763 /* For default values */
5764
5778 public function getDefaultCreateValueFor($fieldname, $alternatevalue = null, $type = 'alphanohtml')
5779 {
5780 global $conf, $_POST;
5781
5782 // If param here has been posted, we use this value first.
5783 if (GETPOSTISSET($fieldname)) {
5784 return GETPOST($fieldname, $type, 3);
5785 }
5786
5787 if (isset($alternatevalue)) {
5788 return $alternatevalue;
5789 }
5790
5791 $newelement = $this->element;
5792 if ($newelement == 'facture') {
5793 $newelement = 'invoice';
5794 }
5795 if ($newelement == 'commande') {
5796 $newelement = 'order';
5797 }
5798 if (empty($newelement)) {
5799 dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
5800 return '';
5801 }
5802
5803 $keyforfieldname = strtoupper($newelement.'_DEFAULT_'.$fieldname);
5804 //var_dump($keyforfieldname);
5805 if (isset($conf->global->$keyforfieldname)) {
5806 return $conf->global->$keyforfieldname;
5807 }
5808
5809 // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
5810 // store content into $conf->cache['overwrite_default']
5811
5812 return '';
5813 }
5814
5815
5816 /* For triggers */
5817
5818
5819 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5830 public function call_trigger($triggerName, $user)
5831 {
5832 // phpcs:enable
5833 global $langs, $conf;
5834 if (!empty(self::TRIGGER_PREFIX) && strpos($triggerName, self::TRIGGER_PREFIX . '_') !== 0) {
5835 dol_print_error('', 'The trigger "' . $triggerName . '" does not start with "' . self::TRIGGER_PREFIX . '_" as required.');
5836 exit;
5837 }
5838 if (!is_object($langs)) { // If lang was not defined, we set it. It is required by run_triggers.
5839 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5840 $langs = new Translate('', $conf);
5841 }
5842
5843 include_once DOL_DOCUMENT_ROOT.'/core/class/interfaces.class.php';
5844 $interface = new Interfaces($this->db);
5845 $result = $interface->run_triggers($triggerName, $this, $user, $langs, $conf);
5846
5847 if ($result < 0) {
5848 if (!empty($this->errors)) {
5849 $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.
5850 } else {
5851 $this->errors = $interface->errors;
5852 }
5853 }
5854 return $result;
5855 }
5856
5857
5858 /* Functions for data in other language */
5859
5860
5869 {
5870 // To avoid SQL errors. Probably not the better solution though
5871 if (!$this->element) {
5872 return 0;
5873 }
5874 if (!($this->id > 0)) {
5875 return 0;
5876 }
5877 if (is_array($this->array_languages)) {
5878 return 1;
5879 }
5880
5881 $this->array_languages = array();
5882
5883 $element = $this->element;
5884 if ($element == 'categorie') {
5885 $element = 'categories'; // For compatibility
5886 }
5887
5888 // Request to get translation values for object
5889 $sql = "SELECT rowid, property, lang , value";
5890 $sql .= " FROM ".$this->db->prefix()."object_lang";
5891 $sql .= " WHERE type_object = '".$this->db->escape($element)."'";
5892 $sql .= " AND fk_object = ".((int) $this->id);
5893
5894 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
5895 $resql = $this->db->query($sql);
5896 if ($resql) {
5897 $numrows = $this->db->num_rows($resql);
5898 if ($numrows) {
5899 $i = 0;
5900 while ($i < $numrows) {
5901 $obj = $this->db->fetch_object($resql);
5902 $key = $obj->property;
5903 $value = $obj->value;
5904 $codelang = $obj->lang;
5905 $type = $this->fields[$key]['type'];
5906
5907 // we can add this attribute to object
5908 if (preg_match('/date/', $type)) {
5909 $this->array_languages[$key][$codelang] = $this->db->jdate($value);
5910 } else {
5911 $this->array_languages[$key][$codelang] = $value;
5912 }
5913
5914 $i++;
5915 }
5916 }
5917
5918 $this->db->free($resql);
5919
5920 if ($numrows) {
5921 return $numrows;
5922 } else {
5923 return 0;
5924 }
5925 } else {
5926 dol_print_error($this->db);
5927 return -1;
5928 }
5929 }
5930
5937 public function setValuesForExtraLanguages($onlykey = '')
5938 {
5939 global $_POST, $langs;
5940
5941 // Get extra fields
5942 foreach ($_POST as $postfieldkey => $postfieldvalue) {
5943 $tmparray = explode('-', $postfieldkey);
5944 if ($tmparray[0] != 'field') {
5945 continue;
5946 }
5947
5948 $element = $tmparray[1];
5949 $key = $tmparray[2];
5950 $codelang = $tmparray[3];
5951 //var_dump("postfieldkey=".$postfieldkey." element=".$element." key=".$key." codelang=".$codelang);
5952
5953 if (!empty($onlykey) && $key != $onlykey) {
5954 continue;
5955 }
5956 if ($element != $this->element) {
5957 continue;
5958 }
5959
5960 $key_type = $this->fields[$key]['type'];
5961
5962 $enabled = 1;
5963 if (isset($this->fields[$key]['enabled'])) {
5964 $enabled = dol_eval($this->fields[$key]['enabled'], 1, 1, '1');
5965 }
5966 /*$perms = 1;
5967 if (isset($this->fields[$key]['perms']))
5968 {
5969 $perms = dol_eval($this->fields[$key]['perms'], 1, 1, '1');
5970 }*/
5971 if (empty($enabled)) {
5972 continue;
5973 }
5974 //if (empty($perms)) continue;
5975
5976 if (in_array($key_type, array('date'))) {
5977 // Clean parameters
5978 // TODO GMT date in memory must be GMT so we should add gm=true in parameters
5979 $value_key = dol_mktime(0, 0, 0, GETPOST($postfieldkey."month", 'int'), GETPOST($postfieldkey."day", 'int'), GETPOST($postfieldkey."year", 'int'));
5980 } elseif (in_array($key_type, array('datetime'))) {
5981 // Clean parameters
5982 // TODO GMT date in memory must be GMT so we should add gm=true in parameters
5983 $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'));
5984 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
5985 $value_arr = GETPOST($postfieldkey, 'array'); // check if an array
5986 if (!empty($value_arr)) {
5987 $value_key = implode(',', $value_arr);
5988 } else {
5989 $value_key = '';
5990 }
5991 } elseif (in_array($key_type, array('price', 'double'))) {
5992 $value_arr = GETPOST($postfieldkey, 'alpha');
5993 $value_key = price2num($value_arr);
5994 } else {
5995 $value_key = GETPOST($postfieldkey);
5996 if (in_array($key_type, array('link')) && $value_key == '-1') {
5997 $value_key = '';
5998 }
5999 }
6000
6001 $this->array_languages[$key][$codelang] = $value_key;
6002
6003 /*if ($nofillrequired) {
6004 $langs->load('errors');
6005 setEventMessages($langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required), null, 'errors');
6006 return -1;
6007 }*/
6008 }
6009
6010 return 1;
6011 }
6012
6013
6014 /* Functions for extrafields */
6015
6022 public function fetchNoCompute($id)
6023 {
6024 global $conf;
6025
6026 $savDisableCompute = $conf->disable_compute;
6027 $conf->disable_compute = 1;
6028
6029 $ret = $this->fetch($id);
6030
6031 $conf->disable_compute = $savDisableCompute;
6032
6033 return $ret;
6034 }
6035
6036 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6046 public function fetch_optionals($rowid = null, $optionsArray = null)
6047 {
6048 // phpcs:enable
6049 global $conf, $extrafields;
6050
6051 if (empty($rowid)) {
6052 $rowid = $this->id;
6053 }
6054 if (empty($rowid) && isset($this->rowid)) {
6055 $rowid = $this->rowid; // deprecated
6056 }
6057
6058 // To avoid SQL errors. Probably not the better solution though
6059 if (!$this->table_element) {
6060 return 0;
6061 }
6062
6063 $this->array_options = array();
6064
6065 if (!is_array($optionsArray)) {
6066 // If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
6067 if (!isset($extrafields) || !is_object($extrafields)) {
6068 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6069 $extrafields = new ExtraFields($this->db);
6070 }
6071
6072 // Load array of extrafields for elementype = $this->table_element
6073 if (empty($extrafields->attributes[$this->table_element]['loaded'])) {
6074 $extrafields->fetch_name_optionals_label($this->table_element);
6075 }
6076 $optionsArray = (!empty($extrafields->attributes[$this->table_element]['label']) ? $extrafields->attributes[$this->table_element]['label'] : null);
6077 } else {
6078 global $extrafields;
6079 dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
6080 }
6081
6082 $table_element = $this->table_element;
6083 if ($table_element == 'categorie') {
6084 $table_element = 'categories'; // For compatibility
6085 }
6086
6087 // Request to get complementary values
6088 if (is_array($optionsArray) && count($optionsArray) > 0) {
6089 $sql = "SELECT rowid";
6090 foreach ($optionsArray as $name => $label) {
6091 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate') {
6092 $sql .= ", ".$name;
6093 }
6094 }
6095 $sql .= " FROM ".$this->db->prefix().$table_element."_extrafields";
6096 $sql .= " WHERE fk_object = ".((int) $rowid);
6097
6098 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6099 $resql = $this->db->query($sql);
6100 if ($resql) {
6101 $numrows = $this->db->num_rows($resql);
6102 if ($numrows) {
6103 $tab = $this->db->fetch_array($resql);
6104
6105 foreach ($tab as $key => $value) {
6106 // 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)
6107 if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && !is_int($key)) {
6108 // we can add this attribute to object
6109 if (!empty($extrafields->attributes[$this->table_element]) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date', 'datetime'))) {
6110 //var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
6111 $this->array_options["options_".$key] = $this->db->jdate($value);
6112 } else {
6113 $this->array_options["options_".$key] = $value;
6114 }
6115
6116 //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
6117 }
6118 }
6119 }
6120
6121 // If field is a computed field, value must become result of compute (regardless of whether a row exists
6122 // in the element's extrafields table)
6123 if (is_array($extrafields->attributes[$this->table_element]['label'])) {
6124 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
6125 if (!empty($extrafields->attributes[$this->table_element]) && !empty($extrafields->attributes[$this->table_element]['computed'][$key])) {
6126 //var_dump($conf->disable_compute);
6127 if (empty($conf->disable_compute)) {
6128 global $objectoffield; // We set a global variable to $objectoffield so
6129 $objectoffield = $this; // we can use it inside computed formula
6130 $this->array_options['options_' . $key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0, '');
6131 }
6132 }
6133 }
6134 }
6135
6136 $this->db->free($resql);
6137
6138 if ($numrows) {
6139 return $numrows;
6140 } else {
6141 return 0;
6142 }
6143 } else {
6144 $this->errors[]=$this->db->lasterror;
6145 return -1;
6146 }
6147 }
6148 return 0;
6149 }
6150
6157 public function deleteExtraFields()
6158 {
6159 global $conf;
6160
6161 if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) {
6162 return 0;
6163 }
6164
6165 $this->db->begin();
6166
6167 $table_element = $this->table_element;
6168 if ($table_element == 'categorie') {
6169 $table_element = 'categories'; // For compatibility
6170 }
6171
6172 dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
6173
6174 $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6175
6176 $resql = $this->db->query($sql_del);
6177 if (!$resql) {
6178 $this->error = $this->db->lasterror();
6179 $this->db->rollback();
6180 return -1;
6181 } else {
6182 $this->db->commit();
6183 return 1;
6184 }
6185 }
6186
6197 public function insertExtraFields($trigger = '', $userused = null)
6198 {
6199 global $conf, $langs, $user;
6200
6201 if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) {
6202 return 0;
6203 }
6204
6205 if (empty($userused)) {
6206 $userused = $user;
6207 }
6208
6209 $error = 0;
6210
6211 if (!empty($this->array_options)) {
6212 // Check parameters
6213 $langs->load('admin');
6214 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6215 $extrafields = new ExtraFields($this->db);
6216 $target_extrafields = $extrafields->fetch_name_optionals_label($this->table_element);
6217
6218 // Eliminate copied source object extra fields that do not exist in target object
6219 $new_array_options = array();
6220 foreach ($this->array_options as $key => $value) {
6221 if (in_array(substr($key, 8), array_keys($target_extrafields))) { // We remove the 'options_' from $key for test
6222 $new_array_options[$key] = $value;
6223 } elseif (in_array($key, array_keys($target_extrafields))) { // We test on $key that does not contain the 'options_' prefix
6224 $new_array_options['options_'.$key] = $value;
6225 }
6226 }
6227
6228 foreach ($new_array_options as $key => $value) {
6229 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6230 $attributeType = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
6231 $attributeLabel = $langs->transnoentities($extrafields->attributes[$this->table_element]['label'][$attributeKey]);
6232 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
6233 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
6234 $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
6235 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$attributeKey];
6236
6237 // If we clone, we have to clean unique extrafields to prevent duplicates.
6238 // This behaviour can be prevented by external code by changing $this->context['createfromclone'] value in createFrom hook
6239 if (!empty($this->context['createfromclone']) && $this->context['createfromclone'] == 'createfromclone' && !empty($attributeUnique)) {
6240 $new_array_options[$key] = null;
6241 }
6242
6243 // Similar code than into insertExtraFields
6244 if ($attributeRequired) {
6245 $mandatorypb = false;
6246 if ($attributeType == 'link' && $this->array_options[$key] == '-1') {
6247 $mandatorypb = true;
6248 }
6249 if ($this->array_options[$key] === '') {
6250 $mandatorypb = true;
6251 }
6252 if ($attributeType == 'sellist' && $this->array_options[$key] == '0') {
6253 $mandatorypb = true;
6254 }
6255 if ($mandatorypb) {
6256 $langs->load("errors");
6257 dol_syslog("Mandatory field '".$key."' is empty during create and set to required into definition of extrafields");
6258 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6259 return -1;
6260 }
6261 }
6262
6263 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6264 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6265
6266 if (!empty($attrfieldcomputed)) {
6267 if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS)) {
6268 $value = dol_eval($attrfieldcomputed, 1, 0, '');
6269 dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
6270 $new_array_options[$key] = $value;
6271 } else {
6272 $new_array_options[$key] = null;
6273 }
6274 }
6275
6276 switch ($attributeType) {
6277 case 'int':
6278 if (!is_numeric($value) && $value != '') {
6279 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6280 return -1;
6281 } elseif ($value == '') {
6282 $new_array_options[$key] = null;
6283 }
6284 break;
6285 case 'price':
6286 case 'double':
6287 $value = price2num($value);
6288 if (!is_numeric($value) && $value != '') {
6289 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." for ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6290 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6291 return -1;
6292 } elseif ($value == '') {
6293 $value = null;
6294 }
6295 //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6296 $new_array_options[$key] = $value;
6297 break;
6298 /*case 'select': // Not required, we chosed value='0' for undefined values
6299 if ($value=='-1')
6300 {
6301 $this->array_options[$key] = null;
6302 }
6303 break;*/
6304 case 'password':
6305 $algo = '';
6306 if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
6307 // If there is an encryption choice, we use it to crypt data before insert
6308 $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
6309 $algo = reset($tmparrays);
6310 if ($algo != '') {
6311 //global $action; // $action may be 'create', 'update', 'update_extras'...
6312 //var_dump($action);
6313 //var_dump($this->oldcopy);exit;
6314 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
6315 //var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]);
6316 if (isset($this->oldcopy->array_options[$key]) && $this->array_options[$key] == $this->oldcopy->array_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.
6317 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6318 } else {
6319 // var_dump($algo);
6320 $newvalue = dol_hash($this->array_options[$key], $algo);
6321 $new_array_options[$key] = $newvalue;
6322 }
6323 } else {
6324 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6325 }
6326 }
6327 } else // Common usage
6328 {
6329 $new_array_options[$key] = $this->array_options[$key];
6330 }
6331 break;
6332 case 'date':
6333 case 'datetime':
6334 // If data is a string instead of a timestamp, we convert it
6335 if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6336 $this->array_options[$key] = strtotime($this->array_options[$key]);
6337 }
6338 $new_array_options[$key] = $this->db->idate($this->array_options[$key]);
6339 break;
6340 case 'datetimegmt':
6341 // If data is a string instead of a timestamp, we convert it
6342 if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6343 $this->array_options[$key] = strtotime($this->array_options[$key]);
6344 }
6345 $new_array_options[$key] = $this->db->idate($this->array_options[$key], 'gmt');
6346 break;
6347 case 'link':
6348 $param_list = array_keys($attributeParam['options']);
6349 // 0 : ObjectName
6350 // 1 : classPath
6351 $InfoFieldList = explode(":", $param_list[0]);
6352 dol_include_once($InfoFieldList[1]);
6353 if ($InfoFieldList[0] && class_exists($InfoFieldList[0])) {
6354 if ($value == '-1') { // -1 is key for no defined in combo list of objects
6355 $new_array_options[$key] = '';
6356 } elseif ($value) {
6357 $object = new $InfoFieldList[0]($this->db);
6358 if (is_numeric($value)) {
6359 $res = $object->fetch($value); // Common case
6360 } else {
6361 $res = $object->fetch('', $value); // For compatibility
6362 }
6363
6364 if ($res > 0) {
6365 $new_array_options[$key] = $object->id;
6366 } else {
6367 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
6368 return -1;
6369 }
6370 }
6371 } else {
6372 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6373 }
6374 break;
6375 case 'checkbox':
6376 case 'chkbxlst':
6377 if (is_array($this->array_options[$key])) {
6378 $new_array_options[$key] = implode(',', $this->array_options[$key]);
6379 } else {
6380 $new_array_options[$key] = $this->array_options[$key];
6381 }
6382 break;
6383 }
6384 }
6385
6386 $this->db->begin();
6387
6388 $table_element = $this->table_element;
6389 if ($table_element == 'categorie') {
6390 $table_element = 'categories'; // For compatibility
6391 }
6392
6393 dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG);
6394
6395 $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6396 $this->db->query($sql_del);
6397
6398 $sql = "INSERT INTO ".$this->db->prefix().$table_element."_extrafields (fk_object";
6399 foreach ($new_array_options as $key => $value) {
6400 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6401 // Add field of attribut
6402 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator
6403 $sql .= ",".$attributeKey;
6404 }
6405 }
6406 // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6407 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6408 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6409 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6410 $sql .= ",".$tmpkey;
6411 }
6412 }
6413 }
6414 $sql .= ") VALUES (".$this->id;
6415
6416 foreach ($new_array_options as $key => $value) {
6417 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6418 // Add field of attribute
6419 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator)
6420 if ($new_array_options[$key] != '' || $new_array_options[$key] == '0') {
6421 $sql .= ",'".$this->db->escape($new_array_options[$key])."'";
6422 } else {
6423 $sql .= ",null";
6424 }
6425 }
6426 }
6427 // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6428 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6429 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6430 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6431 if (in_array($tmpval, array('int', 'double', 'price'))) {
6432 $sql .= ", 0";
6433 } else {
6434 $sql .= ", ''";
6435 }
6436 }
6437 }
6438 }
6439
6440 $sql .= ")";
6441
6442 $resql = $this->db->query($sql);
6443 if (!$resql) {
6444 $this->error = $this->db->lasterror();
6445 $error++;
6446 }
6447
6448 if (!$error && $trigger) {
6449 // Call trigger
6450 $this->context = array('extrafieldaddupdate'=>1);
6451 $result = $this->call_trigger($trigger, $userused);
6452 if ($result < 0) {
6453 $error++;
6454 }
6455 // End call trigger
6456 }
6457
6458 if ($error) {
6459 $this->db->rollback();
6460 return -1;
6461 } else {
6462 $this->db->commit();
6463 return 1;
6464 }
6465 } else {
6466 return 0;
6467 }
6468 }
6469
6480 public function insertExtraLanguages($trigger = '', $userused = null)
6481 {
6482 global $conf, $langs, $user;
6483
6484 if (empty($userused)) {
6485 $userused = $user;
6486 }
6487
6488 $error = 0;
6489
6490 if (!empty($conf->global->MAIN_EXTRALANGUAGES_DISABLED)) {
6491 return 0; // For avoid conflicts if trigger used
6492 }
6493
6494 if (is_array($this->array_languages)) {
6495 $new_array_languages = $this->array_languages;
6496
6497 foreach ($new_array_languages as $key => $value) {
6498 $attributeKey = $key;
6499 $attributeType = $this->fields[$attributeKey]['type'];
6500 $attributeLabel = $this->fields[$attributeKey]['label'];
6501
6502 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6503 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6504
6505 switch ($attributeType) {
6506 case 'int':
6507 if (!is_numeric($value) && $value != '') {
6508 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6509 return -1;
6510 } elseif ($value == '') {
6511 $new_array_languages[$key] = null;
6512 }
6513 break;
6514 case 'double':
6515 $value = price2num($value);
6516 if (!is_numeric($value) && $value != '') {
6517 dol_syslog($langs->trans("ExtraLanguageHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6518 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6519 return -1;
6520 } elseif ($value == '') {
6521 $new_array_languages[$key] = null;
6522 } else {
6523 $new_array_languages[$key] = $value;
6524 }
6525 break;
6526 /*case 'select': // Not required, we chosed value='0' for undefined values
6527 if ($value=='-1')
6528 {
6529 $this->array_options[$key] = null;
6530 }
6531 break;*/
6532 }
6533 }
6534
6535 $this->db->begin();
6536
6537 $table_element = $this->table_element;
6538 if ($table_element == 'categorie') {
6539 $table_element = 'categories'; // For compatibility
6540 }
6541
6542 dol_syslog(get_class($this)."::insertExtraLanguages delete then insert", LOG_DEBUG);
6543
6544 foreach ($new_array_languages as $key => $langcodearray) { // $key = 'name', 'town', ...
6545 foreach ($langcodearray as $langcode => $value) {
6546 $sql_del = "DELETE FROM ".$this->db->prefix()."object_lang";
6547 $sql_del .= " WHERE fk_object = ".((int) $this->id)." AND property = '".$this->db->escape($key)."' AND type_object = '".$this->db->escape($table_element)."'";
6548 $sql_del .= " AND lang = '".$this->db->escape($langcode)."'";
6549 $this->db->query($sql_del);
6550
6551 if ($value !== '') {
6552 $sql = "INSERT INTO ".$this->db->prefix()."object_lang (fk_object, property, type_object, lang, value";
6553 $sql .= ") VALUES (".$this->id.", '".$this->db->escape($key)."', '".$this->db->escape($table_element)."', '".$this->db->escape($langcode)."', '".$this->db->escape($value)."'";
6554 $sql .= ")";
6555
6556 $resql = $this->db->query($sql);
6557 if (!$resql) {
6558 $this->error = $this->db->lasterror();
6559 $error++;
6560 break;
6561 }
6562 }
6563 }
6564 }
6565
6566 if (!$error && $trigger) {
6567 // Call trigger
6568 $this->context = array('extralanguagesaddupdate'=>1);
6569 $result = $this->call_trigger($trigger, $userused);
6570 if ($result < 0) {
6571 $error++;
6572 }
6573 // End call trigger
6574 }
6575
6576 if ($error) {
6577 $this->db->rollback();
6578 return -1;
6579 } else {
6580 $this->db->commit();
6581 return 1;
6582 }
6583 } else {
6584 return 0;
6585 }
6586 }
6587
6598 public function updateExtraField($key, $trigger = null, $userused = null)
6599 {
6600 global $conf, $langs, $user;
6601
6602 if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) {
6603 return 0;
6604 }
6605
6606 if (empty($userused)) {
6607 $userused = $user;
6608 }
6609
6610 $error = 0;
6611
6612 if (!empty($this->array_options) && isset($this->array_options["options_".$key])) {
6613 // Check parameters
6614 $langs->load('admin');
6615 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6616 $extrafields = new ExtraFields($this->db);
6617 $extrafields->fetch_name_optionals_label($this->table_element);
6618
6619 $value = $this->array_options["options_".$key];
6620
6621 $attributeType = $extrafields->attributes[$this->table_element]['type'][$key];
6622 $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key];
6623 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key];
6624 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
6625 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$key];
6626
6627 // Similar code than into insertExtraFields
6628 if ($attributeRequired) {
6629 $mandatorypb = false;
6630 if ($attributeType == 'link' && $this->array_options["options_".$key] == '-1') {
6631 $mandatorypb = true;
6632 }
6633 if ($this->array_options["options_".$key] === '') {
6634 $mandatorypb = true;
6635 }
6636 if ($mandatorypb) {
6637 $langs->load("errors");
6638 dol_syslog("Mandatory field 'options_".$key."' is empty during update and set to required into definition of extrafields");
6639 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6640 return -1;
6641 }
6642 }
6643
6644 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6645 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6646
6647 if (!empty($attrfieldcomputed)) {
6648 if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS)) {
6649 $value = dol_eval($attrfieldcomputed, 1, 0, '');
6650 dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
6651 $this->array_options["options_".$key] = $value;
6652 } else {
6653 $this->array_options["options_".$key] = null;
6654 }
6655 }
6656
6657 switch ($attributeType) {
6658 case 'int':
6659 if (!is_numeric($value) && $value != '') {
6660 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6661 return -1;
6662 } elseif ($value === '') {
6663 $this->array_options["options_".$key] = null;
6664 }
6665 break;
6666 case 'price':
6667 case 'double':
6668 $value = price2num($value);
6669 if (!is_numeric($value) && $value != '') {
6670 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6671 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6672 return -1;
6673 } elseif ($value === '') {
6674 $value = null;
6675 }
6676 //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6677 $this->array_options["options_".$key] = $value;
6678 break;
6679 /*case 'select': // Not required, we chosed value='0' for undefined values
6680 if ($value=='-1')
6681 {
6682 $this->array_options[$key] = null;
6683 }
6684 break;*/
6685 case 'date':
6686 case 'datetime':
6687 if (empty($this->array_options["options_".$key])) {
6688 $this->array_options["options_".$key] = null;
6689 } else {
6690 $this->array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key]);
6691 }
6692 break;
6693 case 'datetimegmt':
6694 if (empty($this->array_options["options_".$key])) {
6695 $this->array_options["options_".$key] = null;
6696 } else {
6697 $this->array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key], 'gmt');
6698 }
6699 break;
6700 case 'boolean':
6701 if (empty($this->array_options["options_".$key])) {
6702 $this->array_options["options_".$key] = null;
6703 }
6704 break;
6705 case 'link':
6706 if ($this->array_options["options_".$key] === '') {
6707 $this->array_options["options_".$key] = null;
6708 }
6709 break;
6710 /*
6711 case 'link':
6712 $param_list = array_keys($attributeParam['options']);
6713 // 0 : ObjectName
6714 // 1 : classPath
6715 $InfoFieldList = explode(":", $param_list[0]);
6716 dol_include_once($InfoFieldList[1]);
6717 if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
6718 {
6719 if ($value == '-1') // -1 is key for no defined in combo list of objects
6720 {
6721 $new_array_options[$key] = '';
6722 } elseif ($value) {
6723 $object = new $InfoFieldList[0]($this->db);
6724 if (is_numeric($value)) $res = $object->fetch($value); // Common case
6725 else $res = $object->fetch('', $value); // For compatibility
6726
6727 if ($res > 0) $new_array_options[$key] = $object->id;
6728 else {
6729 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
6730 $this->db->rollback();
6731 return -1;
6732 }
6733 }
6734 } else {
6735 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6736 }
6737 break;
6738 */
6739 case 'checkbox':
6740 case 'chkbxlst':
6741 if (is_array($this->array_options[$key])) {
6742 $new_array_options[$key] = implode(',', $this->array_options[$key]);
6743 } else {
6744 $new_array_options[$key] = $this->array_options[$key];
6745 }
6746 break;
6747 }
6748
6749 $this->db->begin();
6750
6751 $linealreadyfound = 0;
6752
6753 // 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)
6754 $sql = "SELECT COUNT(rowid) as nb FROM ".$this->db->prefix().$this->table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6755 $resql = $this->db->query($sql);
6756 if ($resql) {
6757 $tmpobj = $this->db->fetch_object($resql);
6758 if ($tmpobj) {
6759 $linealreadyfound = $tmpobj->nb;
6760 }
6761 }
6762
6763 if ($linealreadyfound) {
6764 if ($this->array_options["options_".$key] === null) {
6765 $sql = "UPDATE ".$this->db->prefix().$this->table_element."_extrafields SET ".$key." = null";
6766 } else {
6767 $sql = "UPDATE ".$this->db->prefix().$this->table_element."_extrafields SET ".$key." = '".$this->db->escape($this->array_options["options_".$key])."'";
6768 }
6769 $sql .= " WHERE fk_object = ".((int) $this->id);
6770 } else {
6771 $result = $this->insertExtraFields('', $user);
6772 if ($result < 0) {
6773 $error++;
6774 }
6775 }
6776
6777 $resql = $this->db->query($sql);
6778 if (!$resql) {
6779 $error++;
6780 $this->error = $this->db->lasterror();
6781 }
6782 if (!$error && $trigger) {
6783 // Call trigger
6784 $this->context = array('extrafieldupdate'=>1);
6785 $result = $this->call_trigger($trigger, $userused);
6786 if ($result < 0) {
6787 $error++;
6788 }
6789 // End call trigger
6790 }
6791
6792 if ($error) {
6793 dol_syslog(__METHOD__.$this->error, LOG_ERR);
6794 $this->db->rollback();
6795 return -1;
6796 } else {
6797 $this->db->commit();
6798 return 1;
6799 }
6800 } else {
6801 return 0;
6802 }
6803 }
6804
6815 public function updateExtraLanguages($key, $trigger = null, $userused = null)
6816 {
6817 global $conf, $langs, $user;
6818
6819 if (empty($userused)) {
6820 $userused = $user;
6821 }
6822
6823 $error = 0;
6824
6825 if (!empty($conf->global->MAIN_EXTRALANGUAGES_DISABLED)) {
6826 return 0; // For avoid conflicts if trigger used
6827 }
6828
6829 return 0;
6830 }
6831
6832
6847 public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0, $nonewbutton = 0)
6848 {
6849 global $conf, $langs, $form;
6850
6851 if (!is_object($form)) {
6852 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
6853 $form = new Form($this->db);
6854 }
6855
6856 if (!empty($this->fields)) {
6857 $val = $this->fields[$key];
6858 }
6859
6860 // Validation tests and output
6861 $fieldValidationErrorMsg = '';
6862 $validationClass = '';
6863 $fieldValidationErrorMsg = $this->getFieldError($key);
6864 if (!empty($fieldValidationErrorMsg)) {
6865 $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
6866 } else {
6867 $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
6868 }
6869
6870 $out = '';
6871 $type = '';
6872 $isDependList = 0;
6873 $param = array();
6874 $param['options'] = array();
6875 $reg = array();
6876 $size = !empty($this->fields[$key]['size']) ? $this->fields[$key]['size'] : 0;
6877 // Because we work on extrafields
6878 if (preg_match('/^(integer|link):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6879 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
6880 $type = 'link';
6881 } elseif (preg_match('/^(integer|link):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6882 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
6883 $type = 'link';
6884 } elseif (preg_match('/^(integer|link):(.*):(.*)/i', $val['type'], $reg)) {
6885 $param['options'] = array($reg[2].':'.$reg[3] => 'N');
6886 $type = 'link';
6887 } elseif (preg_match('/^(sellist):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6888 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
6889 $type = 'sellist';
6890 } elseif (preg_match('/^(sellist):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6891 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
6892 $type = 'sellist';
6893 } elseif (preg_match('/^(sellist):(.*):(.*)/i', $val['type'], $reg)) {
6894 $param['options'] = array($reg[2].':'.$reg[3] => 'N');
6895 $type = 'sellist';
6896 } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
6897 $param['options'] = array($reg[1] => 'N');
6898 $type = 'chkbxlst';
6899 } elseif (preg_match('/varchar\‍((\d+)\‍)/', $val['type'], $reg)) {
6900 $param['options'] = array();
6901 $type = 'varchar';
6902 $size = $reg[1];
6903 } elseif (preg_match('/varchar/', $val['type'])) {
6904 $param['options'] = array();
6905 $type = 'varchar';
6906 } else {
6907 $param['options'] = array();
6908 $type = $this->fields[$key]['type'];
6909 }
6910
6911 // Special case that force options and type ($type can be integer, varchar, ...)
6912 if (!empty($this->fields[$key]['arrayofkeyval']) && is_array($this->fields[$key]['arrayofkeyval'])) {
6913 $param['options'] = $this->fields[$key]['arrayofkeyval'];
6914 $type = 'select';
6915 }
6916
6917 $label = $this->fields[$key]['label'];
6918 //$elementtype=$this->fields[$key]['elementtype']; // Seems not used
6919 $default = (!empty($this->fields[$key]['default']) ? $this->fields[$key]['default'] : '');
6920 $computed = (!empty($this->fields[$key]['computed']) ? $this->fields[$key]['computed'] : '');
6921 $unique = (!empty($this->fields[$key]['unique']) ? $this->fields[$key]['unique'] : 0);
6922 $required = (!empty($this->fields[$key]['required']) ? $this->fields[$key]['required'] : 0);
6923 $autofocusoncreate = (!empty($this->fields[$key]['autofocusoncreate']) ? $this->fields[$key]['autofocusoncreate'] : 0);
6924
6925 $langfile = (!empty($this->fields[$key]['langfile']) ? $this->fields[$key]['langfile'] : '');
6926 $list = (!empty($this->fields[$key]['list']) ? $this->fields[$key]['list'] : 0);
6927 $hidden = (in_array(abs($this->fields[$key]['visible']), array(0, 2)) ? 1 : 0);
6928
6929 $objectid = $this->id;
6930
6931 if ($computed) {
6932 if (!preg_match('/^search_/', $keyprefix)) {
6933 return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
6934 } else {
6935 return '';
6936 }
6937 }
6938
6939 // Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine
6940 if (empty($morecss) && !empty($val['css'])) {
6941 $morecss = $val['css'];
6942 } elseif (empty($morecss)) {
6943 if ($type == 'date') {
6944 $morecss = 'minwidth100imp';
6945 } elseif ($type == 'datetime' || $type == 'link') { // link means an foreign key to another primary id
6946 $morecss = 'minwidth200imp';
6947 } elseif (in_array($type, array('int', 'integer', 'price')) || preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', $type)) {
6948 $morecss = 'maxwidth75';
6949 } elseif ($type == 'url') {
6950 $morecss = 'minwidth400';
6951 } elseif ($type == 'boolean') {
6952 $morecss = '';
6953 } else {
6954 if (round($size) < 12) {
6955 $morecss = 'minwidth100';
6956 } elseif (round($size) <= 48) {
6957 $morecss = 'minwidth200';
6958 } else {
6959 $morecss = 'minwidth400';
6960 }
6961 }
6962 }
6963
6964 // Add validation state class
6965 if (!empty($validationClass)) {
6966 $morecss.= $validationClass;
6967 }
6968
6969 if (in_array($type, array('date'))) {
6970 $tmp = explode(',', $size);
6971 $newsize = $tmp[0];
6972 $showtime = 0;
6973
6974 // Do not show current date when field not required (see selectDate() method)
6975 if (!$required && $value == '') {
6976 $value = '-1';
6977 }
6978
6979 // TODO Must also support $moreparam
6980 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
6981 } elseif (in_array($type, array('datetime'))) {
6982 $tmp = explode(',', $size);
6983 $newsize = $tmp[0];
6984 $showtime = 1;
6985
6986 // Do not show current date when field not required (see selectDate() method)
6987 if (!$required && $value == '') $value = '-1';
6988
6989 // TODO Must also support $moreparam
6990 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
6991 } elseif (in_array($type, array('duration'))) {
6992 $out = $form->select_duration($keyprefix.$key.$keysuffix, $value, 0, 'text', 0, 1);
6993 } elseif (in_array($type, array('int', 'integer'))) {
6994 $tmp = explode(',', $size);
6995 $newsize = $tmp[0];
6996 $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' : '').'>';
6997 } elseif (in_array($type, array('real'))) {
6998 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
6999 } elseif (preg_match('/varchar/', $type)) {
7000 $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' : '').'>';
7001 } elseif (in_array($type, array('email', 'mail', 'phone', 'url', 'ip'))) {
7002 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7003 } elseif (preg_match('/^text/', $type)) {
7004 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7005 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7006 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
7007 $out = $doleditor->Create(1);
7008 } else {
7009 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7010 }
7011 } elseif (preg_match('/^html/', $type)) {
7012 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7013 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7014 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_5, '90%');
7015 $out = $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
7016 } else {
7017 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7018 }
7019 } elseif ($type == 'boolean') {
7020 $checked = '';
7021 if (!empty($value)) {
7022 $checked = ' checked value="1" ';
7023 } else {
7024 $checked = ' value="1" ';
7025 }
7026 $out = '<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
7027 } elseif ($type == 'price') {
7028 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7029 $value = price($value);
7030 }
7031 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '.$langs->getCurrencySymbol($conf->currency);
7032 } elseif (preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', $type)) {
7033 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7034 $value = price($value);
7035 }
7036 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
7037 } elseif ($type == 'select') { // combo list
7038 $out = '';
7039 if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_EXTRAFIELDS_DISABLE_SELECT2)) {
7040 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7041 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7042 }
7043
7044 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7045 if ((!isset($this->fields[$key]['default'])) || ($this->fields[$key]['notnull'] != 1)) {
7046 $out .= '<option value="0">&nbsp;</option>';
7047 }
7048 foreach ($param['options'] as $keyb => $valb) {
7049 if ((string) $keyb == '') {
7050 continue;
7051 }
7052 if (strpos($valb, "|") !== false) {
7053 list($valb, $parent) = explode('|', $valb);
7054 }
7055 $out .= '<option value="'.$keyb.'"';
7056 $out .= (((string) $value == (string) $keyb) ? ' selected' : '');
7057 $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
7058 $out .= '>'.$valb.'</option>';
7059 }
7060 $out .= '</select>';
7061 } elseif ($type == 'sellist') {
7062 $out = '';
7063 if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_EXTRAFIELDS_DISABLE_SELECT2)) {
7064 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7065 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7066 }
7067
7068 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7069 if (is_array($param['options'])) {
7070 $param_list = array_keys($param['options']);
7071 $InfoFieldList = explode(":", $param_list[0]);
7072 $parentName = '';
7073 $parentField = '';
7074 // 0 : tableName
7075 // 1 : label field name
7076 // 2 : key fields name (if differ of rowid)
7077 // 3 : key field parent (for dependent lists)
7078 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
7079 // 5 : id category type
7080 // 6 : ids categories list separated by comma for category root
7081 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7082
7083 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7084 if (strpos($InfoFieldList[4], 'extra.') !== false) {
7085 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7086 } else {
7087 $keyList = $InfoFieldList[2].' as rowid';
7088 }
7089 }
7090 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7091 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7092 $keyList .= ', '.$parentField;
7093 }
7094
7095 $filter_categorie = false;
7096 if (count($InfoFieldList) > 5) {
7097 if ($InfoFieldList[0] == 'categorie') {
7098 $filter_categorie = true;
7099 }
7100 }
7101
7102 if ($filter_categorie === false) {
7103 $fields_label = explode('|', $InfoFieldList[1]);
7104 if (is_array($fields_label)) {
7105 $keyList .= ', ';
7106 $keyList .= implode(', ', $fields_label);
7107 }
7108
7109 $sqlwhere = '';
7110 $sql = "SELECT " . $keyList;
7111 $sql .= " FROM " . $this->db->prefix() . $InfoFieldList[0];
7112 if (!empty($InfoFieldList[4])) {
7113 // can use SELECT request
7114 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7115 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7116 }
7117
7118 // current object id can be use into filter
7119 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7120 $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
7121 } else {
7122 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7123 }
7124
7125 //We have to join on extrafield table
7126 if (strpos($InfoFieldList[4], 'extra') !== false) {
7127 $sql .= " as main, " . $this->db->prefix() . $InfoFieldList[0] . "_extrafields as extra";
7128 $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2] . " AND " . $InfoFieldList[4];
7129 } else {
7130 $sqlwhere .= " WHERE " . $InfoFieldList[4];
7131 }
7132 } else {
7133 $sqlwhere .= ' WHERE 1=1';
7134 }
7135 // Some tables may have field, some other not. For the moment we disable it.
7136 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7137 $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7138 }
7139 $sql .= $sqlwhere;
7140 //print $sql;
7141
7142 $sql .= ' ORDER BY ' . implode(', ', $fields_label);
7143
7144 dol_syslog(get_class($this) . '::showInputField type=sellist', LOG_DEBUG);
7145 $resql = $this->db->query($sql);
7146 if ($resql) {
7147 $out .= '<option value="0">&nbsp;</option>';
7148 $num = $this->db->num_rows($resql);
7149 $i = 0;
7150 while ($i < $num) {
7151 $labeltoshow = '';
7152 $obj = $this->db->fetch_object($resql);
7153
7154 // Several field into label (eq table:code|libelle:rowid)
7155 $notrans = false;
7156 $fields_label = explode('|', $InfoFieldList[1]);
7157 if (count($fields_label) > 1) {
7158 $notrans = true;
7159 foreach ($fields_label as $field_toshow) {
7160 $labeltoshow .= $obj->$field_toshow . ' ';
7161 }
7162 } else {
7163 $labeltoshow = $obj->{$InfoFieldList[1]};
7164 }
7165 $labeltoshow = dol_trunc($labeltoshow, 45);
7166
7167 if ($value == $obj->rowid) {
7168 foreach ($fields_label as $field_toshow) {
7169 $translabel = $langs->trans($obj->$field_toshow);
7170 if ($translabel != $obj->$field_toshow) {
7171 $labeltoshow = dol_trunc($translabel) . ' ';
7172 } else {
7173 $labeltoshow = dol_trunc($obj->$field_toshow) . ' ';
7174 }
7175 }
7176 $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7177 } else {
7178 if (!$notrans) {
7179 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7180 if ($translabel != $obj->{$InfoFieldList[1]}) {
7181 $labeltoshow = dol_trunc($translabel, 18);
7182 } else {
7183 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]});
7184 }
7185 }
7186 if (empty($labeltoshow)) {
7187 $labeltoshow = '(not defined)';
7188 }
7189 if ($value == $obj->rowid) {
7190 $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7191 }
7192
7193 if (!empty($InfoFieldList[3]) && $parentField) {
7194 $parent = $parentName . ':' . $obj->{$parentField};
7195 $isDependList = 1;
7196 }
7197
7198 $out .= '<option value="' . $obj->rowid . '"';
7199 $out .= ($value == $obj->rowid ? ' selected' : '');
7200 $out .= (!empty($parent) ? ' parent="' . $parent . '"' : '');
7201 $out .= '>' . $labeltoshow . '</option>';
7202 }
7203
7204 $i++;
7205 }
7206 $this->db->free($resql);
7207 } else {
7208 print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
7209 }
7210 } else {
7211 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7212 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
7213 $out .= '<option value="0">&nbsp;</option>';
7214 foreach ($data as $data_key => $data_value) {
7215 $out .= '<option value="' . $data_key . '"';
7216 $out .= ($value == $data_key ? ' selected' : '');
7217 $out .= '>' . $data_value . '</option>';
7218 }
7219 }
7220 }
7221 $out .= '</select>';
7222 } elseif ($type == 'checkbox') {
7223 $value_arr = explode(',', $value);
7224 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ?null:$param['options']), $value_arr, '', 0, $morecss, 0, '100%');
7225 } elseif ($type == 'radio') {
7226 $out = '';
7227 foreach ($param['options'] as $keyopt => $valopt) {
7228 $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
7229 $out .= ' value="'.$keyopt.'"';
7230 $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
7231 $out .= ($value == $keyopt ? 'checked' : '');
7232 $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$valopt.'</label><br>';
7233 }
7234 } elseif ($type == 'chkbxlst') {
7235 if (is_array($value)) {
7236 $value_arr = $value;
7237 } else {
7238 $value_arr = explode(',', $value);
7239 }
7240
7241 if (is_array($param['options'])) {
7242 $param_list = array_keys($param['options']);
7243 $InfoFieldList = explode(":", $param_list[0]);
7244 $parentName = '';
7245 $parentField = '';
7246 // 0 : tableName
7247 // 1 : label field name
7248 // 2 : key fields name (if differ of rowid)
7249 // 3 : key field parent (for dependent lists)
7250 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
7251 // 5 : id category type
7252 // 6 : ids categories list separated by comma for category root
7253 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7254
7255 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7256 list ($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7257 $keyList .= ', '.$parentField;
7258 }
7259 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7260 if (strpos($InfoFieldList[4], 'extra.') !== false) {
7261 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7262 } else {
7263 $keyList = $InfoFieldList[2].' as rowid';
7264 }
7265 }
7266
7267 $filter_categorie = false;
7268 if (count($InfoFieldList) > 5) {
7269 if ($InfoFieldList[0] == 'categorie') {
7270 $filter_categorie = true;
7271 }
7272 }
7273
7274 if ($filter_categorie === false) {
7275 $fields_label = explode('|', $InfoFieldList[1]);
7276 if (is_array($fields_label)) {
7277 $keyList .= ', ';
7278 $keyList .= implode(', ', $fields_label);
7279 }
7280
7281 $sqlwhere = '';
7282 $sql = "SELECT " . $keyList;
7283 $sql .= ' FROM ' . $this->db->prefix() . $InfoFieldList[0];
7284 if (!empty($InfoFieldList[4])) {
7285 // can use SELECT request
7286 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7287 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7288 }
7289
7290 // current object id can be use into filter
7291 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7292 $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
7293 } else {
7294 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7295 }
7296
7297 // We have to join on extrafield table
7298 if (strpos($InfoFieldList[4], 'extra') !== false) {
7299 $sql .= ' as main, ' . $this->db->prefix() . $InfoFieldList[0] . '_extrafields as extra';
7300 $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2] . " AND " . $InfoFieldList[4];
7301 } else {
7302 $sqlwhere .= " WHERE " . $InfoFieldList[4];
7303 }
7304 } else {
7305 $sqlwhere .= ' WHERE 1=1';
7306 }
7307 // Some tables may have field, some other not. For the moment we disable it.
7308 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7309 $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7310 }
7311 // $sql.=preg_replace('/^ AND /','',$sqlwhere);
7312 // print $sql;
7313
7314 $sql .= $sqlwhere;
7315 dol_syslog(get_class($this) . '::showInputField type=chkbxlst', LOG_DEBUG);
7316 $resql = $this->db->query($sql);
7317 if ($resql) {
7318 $num = $this->db->num_rows($resql);
7319 $i = 0;
7320
7321 $data = array();
7322
7323 while ($i < $num) {
7324 $labeltoshow = '';
7325 $obj = $this->db->fetch_object($resql);
7326
7327 $notrans = false;
7328 // Several field into label (eq table:code|libelle:rowid)
7329 $fields_label = explode('|', $InfoFieldList[1]);
7330 if (count($fields_label) > 1) {
7331 $notrans = true;
7332 foreach ($fields_label as $field_toshow) {
7333 $labeltoshow .= $obj->$field_toshow . ' ';
7334 }
7335 } else {
7336 $labeltoshow = $obj->{$InfoFieldList[1]};
7337 }
7338 $labeltoshow = dol_trunc($labeltoshow, 45);
7339
7340 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7341 foreach ($fields_label as $field_toshow) {
7342 $translabel = $langs->trans($obj->$field_toshow);
7343 if ($translabel != $obj->$field_toshow) {
7344 $labeltoshow = dol_trunc($translabel, 18) . ' ';
7345 } else {
7346 $labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
7347 }
7348 }
7349
7350 $data[$obj->rowid] = $labeltoshow;
7351 } else {
7352 if (!$notrans) {
7353 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7354 if ($translabel != $obj->{$InfoFieldList[1]}) {
7355 $labeltoshow = dol_trunc($translabel, 18);
7356 } else {
7357 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
7358 }
7359 }
7360 if (empty($labeltoshow)) {
7361 $labeltoshow = '(not defined)';
7362 }
7363
7364 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7365 $data[$obj->rowid] = $labeltoshow;
7366 }
7367
7368 if (!empty($InfoFieldList[3]) && $parentField) {
7369 $parent = $parentName . ':' . $obj->{$parentField};
7370 $isDependList = 1;
7371 }
7372
7373 $data[$obj->rowid] = $labeltoshow;
7374 }
7375
7376 $i++;
7377 }
7378 $this->db->free($resql);
7379
7380 $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, '', 0, $morecss, 0, '100%');
7381 } else {
7382 print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
7383 }
7384 } else {
7385 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7386 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
7387 $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, '', 0, $morecss, 0, '100%');
7388 }
7389 }
7390 } elseif ($type == 'link') {
7391 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
7392 $param_list_array = explode(':', $param_list[0]);
7393 $showempty = (($required && $default != '') ? 0 : 1);
7394
7395 if (!preg_match('/search_/', $keyprefix)) {
7396 if (!empty($param_list_array[2])) { // If the entry into $fields is set to add a create button
7397 if (!empty($this->fields[$key]['picto'])) {
7398 $morecss .= ' widthcentpercentminusxx';
7399 } else {
7400 $morecss .= ' widthcentpercentminusx';
7401 }
7402 } else {
7403 if (!empty($this->fields[$key]['picto'])) {
7404 $morecss .= ' widthcentpercentminusx';
7405 }
7406 }
7407 }
7408
7409 $out = $form->selectForForms($param_list[0], $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, $moreparam, 0, empty($val['disabled']) ? 0 : 1);
7410
7411 if (!empty($param_list_array[2])) { // If the entry into $fields is set, we must add a create button
7412 if ((!GETPOSTISSET('backtopage') || strpos(GETPOST('backtopage'), $_SERVER['PHP_SELF']) === 0) // // To avoid to open several times the 'Plus' button (we accept only one level)
7413 && empty($val['disabled']) && empty($nonewbutton)) { // and to avoid to show the button if the field is protected by a "disabled".
7414 list($class, $classfile) = explode(':', $param_list[0]);
7415 if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) {
7416 $url_path = dol_buildpath(dirname(dirname($classfile)).'/card.php', 1);
7417 } else {
7418 $url_path = dol_buildpath(dirname(dirname($classfile)).'/'.strtolower($class).'_card.php', 1);
7419 }
7420 $paramforthenewlink = '';
7421 $paramforthenewlink .= (GETPOSTISSET('action') ? '&action='.GETPOST('action', 'aZ09') : '');
7422 $paramforthenewlink .= (GETPOSTISSET('id') ? '&id='.GETPOST('id', 'int') : '');
7423 $paramforthenewlink .= (GETPOSTISSET('origin') ? '&origin='.GETPOST('origin', 'aZ09') : '');
7424 $paramforthenewlink .= (GETPOSTISSET('originid') ? '&originid='.GETPOST('originid', 'int') : '');
7425 $paramforthenewlink .= '&fk_'.strtolower($class).'=--IDFORBACKTOPAGE--';
7426 // TODO Add Javascript code to add input fields already filled into $paramforthenewlink so we won't loose them when going back to main page
7427 $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>';
7428 }
7429 }
7430 } elseif ($type == 'password') {
7431 // If prefix is 'search_', field is used as a filter, we use a common text field.
7432 $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
7433 } elseif ($type == 'array') {
7434 $newval = $val;
7435 $newval['type'] = 'varchar(256)';
7436
7437 $out = '';
7438 if (!empty($value)) {
7439 foreach ($value as $option) {
7440 $out .= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
7441 $out .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $morecss).'<br></span>';
7442 }
7443 }
7444 $out .= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
7445
7446 $newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
7447 $newInput .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $morecss).'<br></span>';
7448
7449 if (!empty($conf->use_javascript_ajax)) {
7450 $out .= '
7451 <script nonce="'.getNonce().'">
7452 $(document).ready(function() {
7453 $("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
7454 $("'.dol_escape_js($newInput).'").insertBefore(this);
7455 });
7456
7457 $(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
7458 $(this).parent().remove();
7459 });
7460 });
7461 </script>';
7462 }
7463 }
7464 if (!empty($hidden)) {
7465 $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
7466 }
7467
7468 if ($isDependList==1) {
7469 $out .= $this->getJSListDependancies('_common');
7470 }
7471 /* Add comments
7472 if ($type == 'date') $out.=' (YYYY-MM-DD)';
7473 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
7474 */
7475
7476 // Display error message for field
7477 if (!empty($fieldValidationErrorMsg) && function_exists('getFieldErrorIcon')) {
7478 $out .= ' '.getFieldErrorIcon($fieldValidationErrorMsg);
7479 }
7480
7481 return $out;
7482 }
7483
7497 public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '')
7498 {
7499 global $conf, $langs, $form;
7500
7501 if (!is_object($form)) {
7502 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
7503 $form = new Form($this->db);
7504 }
7505
7506 $label = empty($val['label']) ? '' : $val['label'];
7507 $type = empty($val['type']) ? '' : $val['type'];
7508 $size = empty($val['css']) ? '' : $val['css'];
7509 $reg = array();
7510
7511 // Convert var to be able to share same code than showOutputField of extrafields
7512 if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
7513 $type = 'varchar'; // convert varchar(xx) int varchar
7514 $size = $reg[1];
7515 } elseif (preg_match('/varchar/', $type)) {
7516 $type = 'varchar'; // convert varchar(xx) int varchar
7517 }
7518 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
7519 $type = 'select';
7520 }
7521 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
7522 $type = 'link';
7523 }
7524
7525 $default = empty($val['default']) ? '' : $val['default'];
7526 $computed = empty($val['computed']) ? '' : $val['computed'];
7527 $unique = empty($val['unique']) ? '' : $val['unique'];
7528 $required = empty($val['required']) ? '' : $val['required'];
7529 $param = array();
7530 $param['options'] = array();
7531
7532 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
7533 $param['options'] = $val['arrayofkeyval'];
7534 }
7535 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
7536 $type = 'link';
7537 $stringforoptions = $reg[1].':'.$reg[2];
7538 if ($reg[1] == 'User') {
7539 $stringforoptions .= ':-1';
7540 }
7541 $param['options'] = array($stringforoptions => $stringforoptions);
7542 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7543 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7544 $type = 'sellist';
7545 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
7546 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
7547 $type = 'sellist';
7548 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
7549 $param['options'] = array($reg[1].':'.$reg[2] => 'N');
7550 $type = 'sellist';
7551 } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
7552 $param['options'] = array($reg[1] => 'N');
7553 $type = 'chkbxlst';
7554 }
7555
7556 $langfile = empty($val['langfile']) ? '' : $val['langfile'];
7557 $list = (empty($val['list']) ? '' : $val['list']);
7558 $help = (empty($val['help']) ? '' : $val['help']);
7559 $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)
7560
7561 if ($hidden) {
7562 return '';
7563 }
7564
7565 // If field is a computed field, value must become result of compute
7566 if ($computed) {
7567 // Make the eval of compute string
7568 //var_dump($computed);
7569 $value = dol_eval($computed, 1, 0, '');
7570 }
7571
7572 if (empty($morecss)) {
7573 if ($type == 'date') {
7574 $morecss = 'minwidth100imp';
7575 } elseif ($type == 'datetime' || $type == 'timestamp') {
7576 $morecss = 'minwidth200imp';
7577 } elseif (in_array($type, array('int', 'double', 'price'))) {
7578 $morecss = 'maxwidth75';
7579 } elseif ($type == 'url') {
7580 $morecss = 'minwidth400';
7581 } elseif ($type == 'boolean') {
7582 $morecss = '';
7583 } else {
7584 if (is_numeric($size) && round($size) < 12) {
7585 $morecss = 'minwidth100';
7586 } elseif (is_numeric($size) && round($size) <= 48) {
7587 $morecss = 'minwidth200';
7588 } else {
7589 $morecss = 'minwidth400';
7590 }
7591 }
7592 }
7593
7594 // Format output value differently according to properties of field
7595 if (in_array($key, array('rowid', 'ref')) && method_exists($this, 'getNomUrl')) {
7596 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.
7597 $value = $this->getNomUrl(1, '', 0, '', 1);
7598 }
7599 } elseif ($key == 'status' && method_exists($this, 'getLibStatut')) {
7600 $value = $this->getLibStatut(3);
7601 } elseif ($type == 'date') {
7602 if (!empty($value)) {
7603 $value = dol_print_date($value, 'day'); // We suppose dates without time are always gmt (storage of course + output)
7604 } else {
7605 $value = '';
7606 }
7607 } elseif ($type == 'datetime' || $type == 'timestamp') {
7608 if (!empty($value)) {
7609 $value = dol_print_date($value, 'dayhour', 'tzuserrel');
7610 } else {
7611 $value = '';
7612 }
7613 } elseif ($type == 'duration') {
7614 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
7615 if (!is_null($value) && $value !== '') {
7616 $value = convertSecondToTime($value, 'allhourmin');
7617 }
7618 } elseif ($type == 'double' || $type == 'real') {
7619 if (!is_null($value) && $value !== '') {
7620 $value = price($value);
7621 }
7622 } elseif ($type == 'boolean') {
7623 $checked = '';
7624 if (!empty($value)) {
7625 $checked = ' checked ';
7626 }
7627 $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
7628 } elseif ($type == 'mail' || $type == 'email') {
7629 $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
7630 } elseif ($type == 'url') {
7631 $value = dol_print_url($value, '_blank', 32, 1);
7632 } elseif ($type == 'phone') {
7633 $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
7634 } elseif ($type == 'ip') {
7635 $value = dol_print_ip($value, 0);
7636 } elseif ($type == 'price') {
7637 if (!is_null($value) && $value !== '') {
7638 $value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
7639 }
7640 } elseif ($type == 'select') {
7641 $value = isset($param['options'][$value])?$param['options'][$value]:'';
7642 } elseif ($type == 'sellist') {
7643 $param_list = array_keys($param['options']);
7644 $InfoFieldList = explode(":", $param_list[0]);
7645
7646 $selectkey = "rowid";
7647 $keyList = 'rowid';
7648
7649 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7650 $selectkey = $InfoFieldList[2];
7651 $keyList = $InfoFieldList[2].' as rowid';
7652 }
7653
7654 $fields_label = explode('|', $InfoFieldList[1]);
7655 if (is_array($fields_label)) {
7656 $keyList .= ', ';
7657 $keyList .= implode(', ', $fields_label);
7658 }
7659
7660 $filter_categorie = false;
7661 if (count($InfoFieldList) > 5) {
7662 if ($InfoFieldList[0] == 'categorie') {
7663 $filter_categorie = true;
7664 }
7665 }
7666
7667 $sql = "SELECT ".$keyList;
7668 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
7669 if (strpos($InfoFieldList[4], 'extra') !== false) {
7670 $sql .= ' as main';
7671 }
7672 if ($selectkey == 'rowid' && empty($value)) {
7673 $sql .= " WHERE ".$selectkey." = 0";
7674 } elseif ($selectkey == 'rowid') {
7675 $sql .= " WHERE ".$selectkey." = ".((int) $value);
7676 } else {
7677 $sql .= " WHERE ".$selectkey." = '".$this->db->escape($value)."'";
7678 }
7679
7680 //$sql.= ' AND entity = '.$conf->entity;
7681
7682 dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
7683 $resql = $this->db->query($sql);
7684 if ($resql) {
7685 if ($filter_categorie === false) {
7686 $value = ''; // value was used, so now we reste it to use it to build final output
7687 $numrows = $this->db->num_rows($resql);
7688 if ($numrows) {
7689 $obj = $this->db->fetch_object($resql);
7690
7691 // Several field into label (eq table:code|libelle:rowid)
7692 $fields_label = explode('|', $InfoFieldList[1]);
7693
7694 if (is_array($fields_label) && count($fields_label) > 1) {
7695 foreach ($fields_label as $field_toshow) {
7696 $translabel = '';
7697 if (!empty($obj->$field_toshow)) {
7698 $translabel = $langs->trans($obj->$field_toshow);
7699 }
7700 if ($translabel != $field_toshow) {
7701 $value .= dol_trunc($translabel, 18) . ' ';
7702 } else {
7703 $value .= $obj->$field_toshow . ' ';
7704 }
7705 }
7706 } else {
7707 $translabel = '';
7708 if (!empty($obj->{$InfoFieldList[1]})) {
7709 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7710 }
7711 if ($translabel != $obj->{$InfoFieldList[1]}) {
7712 $value = dol_trunc($translabel, 18);
7713 } else {
7714 $value = $obj->{$InfoFieldList[1]};
7715 }
7716 }
7717 }
7718 } else {
7719 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
7720
7721 $toprint = array();
7722 $obj = $this->db->fetch_object($resql);
7723 $c = new Categorie($this->db);
7724 $c->fetch($obj->rowid);
7725 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
7726 foreach ($ways as $way) {
7727 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
7728 }
7729 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
7730 }
7731 } else {
7732 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
7733 }
7734 } elseif ($type == 'radio') {
7735 $value = $param['options'][$value];
7736 } elseif ($type == 'checkbox') {
7737 $value_arr = explode(',', $value);
7738 $value = '';
7739 if (is_array($value_arr) && count($value_arr) > 0) {
7740 $toprint = array();
7741 foreach ($value_arr as $keyval => $valueval) {
7742 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$param['options'][$valueval].'</li>';
7743 }
7744 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
7745 }
7746 } elseif ($type == 'chkbxlst') {
7747 $value_arr = explode(',', $value);
7748
7749 $param_list = array_keys($param['options']);
7750 $InfoFieldList = explode(":", $param_list[0]);
7751
7752 $selectkey = "rowid";
7753 $keyList = 'rowid';
7754
7755 if (count($InfoFieldList) >= 3) {
7756 $selectkey = $InfoFieldList[2];
7757 $keyList = $InfoFieldList[2].' as rowid';
7758 }
7759
7760 $fields_label = explode('|', $InfoFieldList[1]);
7761 if (is_array($fields_label)) {
7762 $keyList .= ', ';
7763 $keyList .= implode(', ', $fields_label);
7764 }
7765
7766 $filter_categorie = false;
7767 if (count($InfoFieldList) > 5) {
7768 if ($InfoFieldList[0] == 'categorie') {
7769 $filter_categorie = true;
7770 }
7771 }
7772
7773 $sql = "SELECT ".$keyList;
7774 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
7775 if (strpos($InfoFieldList[4], 'extra') !== false) {
7776 $sql .= ' as main';
7777 }
7778 // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
7779 // $sql.= ' AND entity = '.$conf->entity;
7780
7781 dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG);
7782 $resql = $this->db->query($sql);
7783 if ($resql) {
7784 if ($filter_categorie === false) {
7785 $value = ''; // value was used, so now we reste it to use it to build final output
7786 $toprint = array();
7787 while ($obj = $this->db->fetch_object($resql)) {
7788 // Several field into label (eq table:code|libelle:rowid)
7789 $fields_label = explode('|', $InfoFieldList[1]);
7790 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7791 if (is_array($fields_label) && count($fields_label) > 1) {
7792 foreach ($fields_label as $field_toshow) {
7793 $translabel = '';
7794 if (!empty($obj->$field_toshow)) {
7795 $translabel = $langs->trans($obj->$field_toshow);
7796 }
7797 if ($translabel != $field_toshow) {
7798 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
7799 } else {
7800 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->$field_toshow . '</li>';
7801 }
7802 }
7803 } else {
7804 $translabel = '';
7805 if (!empty($obj->{$InfoFieldList[1]})) {
7806 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7807 }
7808 if ($translabel != $obj->{$InfoFieldList[1]}) {
7809 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
7810 } else {
7811 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->{$InfoFieldList[1]} . '</li>';
7812 }
7813 }
7814 }
7815 }
7816 } else {
7817 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
7818
7819 $toprint = array();
7820 while ($obj = $this->db->fetch_object($resql)) {
7821 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7822 $c = new Categorie($this->db);
7823 $c->fetch($obj->rowid);
7824 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
7825 foreach ($ways as $way) {
7826 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
7827 }
7828 }
7829 }
7830 }
7831 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
7832 } else {
7833 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
7834 }
7835 } elseif ($type == 'link') {
7836 $out = '';
7837
7838 // only if something to display (perf)
7839 if ($value) {
7840 $param_list = array_keys($param['options']); // Example: $param_list='ObjectName:classPath:-1::customer'
7841
7842 $InfoFieldList = explode(":", $param_list[0]);
7843 $classname = $InfoFieldList[0];
7844 $classpath = $InfoFieldList[1];
7845 $getnomurlparam = (empty($InfoFieldList[2]) ? 3 : $InfoFieldList[2]);
7846 $getnomurlparam2 = (empty($InfoFieldList[4]) ? '' : $InfoFieldList[4]);
7847 if (!empty($classpath)) {
7848 dol_include_once($InfoFieldList[1]);
7849 if ($classname && class_exists($classname)) {
7850 $object = new $classname($this->db);
7851 if ($object->element === 'product') { // Special cas for product because default valut of fetch are wrong
7852 $result = $object->fetch($value, '', '', '', 0, 1, 1);
7853 } else {
7854 $result = $object->fetch($value);
7855 }
7856 if ($result > 0) {
7857 if ($object->element === 'product') {
7858 $get_name_url_param_arr = array($getnomurlparam, $getnomurlparam2, 0, -1, 0, '', 0);
7859 if (isset($val['get_name_url_params'])) {
7860 $get_name_url_params = explode(':', $val['get_name_url_params']);
7861 if (!empty($get_name_url_params)) {
7862 $param_num_max = count($get_name_url_param_arr) - 1;
7863 foreach ($get_name_url_params as $param_num => $param_value) {
7864 if ($param_num > $param_num_max) {
7865 break;
7866 }
7867 $get_name_url_param_arr[$param_num] = $param_value;
7868 }
7869 }
7870 }
7871
7875 $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]);
7876 } else {
7877 $value = $object->getNomUrl($getnomurlparam, $getnomurlparam2);
7878 }
7879 } else {
7880 $value = '';
7881 }
7882 }
7883 } else {
7884 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
7885 return 'Error bad setup of extrafield';
7886 }
7887 } else {
7888 $value = '';
7889 }
7890 } elseif ($type == 'password') {
7891 $value = preg_replace('/./i', '*', $value);
7892 } elseif ($type == 'array') {
7893 $value = implode('<br>', $value);
7894 } else { // text|html|varchar
7895 $value = dol_htmlentitiesbr($value);
7896 }
7897
7898 //print $type.'-'.$size.'-'.$value;
7899 $out = $value;
7900
7901 return $out;
7902 }
7903
7910 public function clearFieldError($fieldKey)
7911 {
7912 $this->error = '';
7913 unset($this->validateFieldsErrors[$fieldKey]);
7914 }
7915
7923 public function setFieldError($fieldKey, $msg = '')
7924 {
7925 global $langs;
7926 if (empty($msg)) {
7927 $msg = $langs->trans("UnknowError");
7928 }
7929
7930 $this->error = $this->validateFieldsErrors[$fieldKey] = $msg;
7931 }
7932
7939 public function getFieldError($fieldKey)
7940 {
7941 if (!empty($this->validateFieldsErrors[$fieldKey])) {
7942 return $this->validateFieldsErrors[$fieldKey];
7943 }
7944 return '';
7945 }
7946
7955 public function validateField($fields, $fieldKey, $fieldValue)
7956 {
7957 global $langs;
7958
7959 if (!class_exists('Validate')) {
7960 require_once DOL_DOCUMENT_ROOT . '/core/class/validate.class.php';
7961 }
7962
7963 $this->clearFieldError($fieldKey);
7964
7965 if (!isset($fields[$fieldKey])) {
7966 $this->setFieldError($fieldKey, $langs->trans('FieldNotFoundInObject'));
7967 return false;
7968 }
7969
7970 $val = $fields[$fieldKey];
7971
7972 $param = array();
7973 $param['options'] = array();
7974 $type = $val['type'];
7975
7976 $required = false;
7977 if (isset($val['notnull']) && $val['notnull'] === 1) {
7978 // 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
7979 $required = true;
7980 }
7981
7982 $maxSize = 0;
7983 $minSize = 0;
7984
7985 //
7986 // PREPARE Elements
7987 //
7988 $reg = array();
7989
7990 // Convert var to be able to share same code than showOutputField of extrafields
7991 if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
7992 $type = 'varchar'; // convert varchar(xx) int varchar
7993 $maxSize = $reg[1];
7994 } elseif (preg_match('/varchar/', $type)) {
7995 $type = 'varchar'; // convert varchar(xx) int varchar
7996 }
7997
7998 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
7999 $type = 'select';
8000 }
8001
8002 if (!empty($val['type']) && preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8003 $type = 'link';
8004 }
8005
8006 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8007 $param['options'] = $val['arrayofkeyval'];
8008 }
8009
8010 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8011 $type = 'link';
8012 $param['options'] = array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
8013 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
8014 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
8015 $type = 'sellist';
8016 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
8017 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
8018 $type = 'sellist';
8019 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
8020 $param['options'] = array($reg[1].':'.$reg[2] => 'N');
8021 $type = 'sellist';
8022 }
8023
8024 //
8025 // TEST Value
8026 //
8027
8028 // Use Validate class to allow external Modules to use data validation part instead of concentrate all test here (factoring) or just for reuse
8029 $validate = new Validate($this->db, $langs);
8030
8031
8032 // little trick : to perform tests with good performances sort tests by quick to low
8033
8034 //
8035 // COMMON TESTS
8036 //
8037
8038 // Required test and empty value
8039 if ($required && !$validate->isNotEmptyString($fieldValue)) {
8040 $this->setFieldError($fieldKey, $validate->error);
8041 return false;
8042 } elseif (!$required && !$validate->isNotEmptyString($fieldValue)) {
8043 // if no value sent and the field is not mandatory, no need to perform tests
8044 return true;
8045 }
8046
8047 // MAX Size test
8048 if (!empty($maxSize) && !$validate->isMaxLength($fieldValue, $maxSize)) {
8049 $this->setFieldError($fieldKey, $validate->error);
8050 return false;
8051 }
8052
8053 // MIN Size test
8054 if (!empty($minSize) && !$validate->isMinLength($fieldValue, $minSize)) {
8055 $this->setFieldError($fieldKey, $validate->error);
8056 return false;
8057 }
8058
8059 //
8060 // TESTS for TYPE
8061 //
8062
8063 if (in_array($type, array('date', 'datetime', 'timestamp'))) {
8064 if (!$validate->isTimestamp($fieldValue)) {
8065 $this->setFieldError($fieldKey, $validate->error);
8066 return false;
8067 } else { return true; }
8068 } elseif ($type == 'duration') {
8069 if (!$validate->isDuration($fieldValue)) {
8070 $this->setFieldError($fieldKey, $validate->error);
8071 return false;
8072 } else { return true; }
8073 } elseif (in_array($type, array('double', 'real', 'price'))) {
8074 // is numeric
8075 if (!$validate->isNumeric($fieldValue)) {
8076 $this->setFieldError($fieldKey, $validate->error);
8077 return false;
8078 } else { return true; }
8079 } elseif ($type == 'boolean') {
8080 if (!$validate->isBool($fieldValue)) {
8081 $this->setFieldError($fieldKey, $validate->error);
8082 return false;
8083 } else { return true; }
8084 } elseif ($type == 'mail') {
8085 if (!$validate->isEmail($fieldValue)) {
8086 $this->setFieldError($fieldKey, $validate->error);
8087 return false;
8088 }
8089 } elseif ($type == 'url') {
8090 if (!$validate->isUrl($fieldValue)) {
8091 $this->setFieldError($fieldKey, $validate->error);
8092 return false;
8093 } else { return true; }
8094 } elseif ($type == 'phone') {
8095 if (!$validate->isPhone($fieldValue)) {
8096 $this->setFieldError($fieldKey, $validate->error);
8097 return false;
8098 } else { return true; }
8099 } elseif ($type == 'select' || $type == 'radio') {
8100 if (!isset($param['options'][$fieldValue])) {
8101 $this->error = $langs->trans('RequireValidValue');
8102 return false;
8103 } else { return true; }
8104 } elseif ($type == 'sellist' || $type == 'chkbxlst') {
8105 $param_list = array_keys($param['options']);
8106 $InfoFieldList = explode(":", $param_list[0]);
8107 $value_arr = explode(',', $fieldValue);
8108 $value_arr = array_map(array($this->db, 'escape'), $value_arr);
8109
8110 $selectkey = "rowid";
8111 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
8112 $selectkey = $InfoFieldList[2];
8113 }
8114
8115 if (!$validate->isInDb($value_arr, $InfoFieldList[0], $selectkey)) {
8116 $this->setFieldError($fieldKey, $validate->error);
8117 return false;
8118 } else { return true; }
8119 } elseif ($type == 'link') {
8120 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
8121 $InfoFieldList = explode(":", $param_list[0]);
8122 $classname = $InfoFieldList[0];
8123 $classpath = $InfoFieldList[1];
8124 if (!$validate->isFetchable($fieldValue, $classname, $classpath)) {
8125 $this->setFieldError($fieldKey, $validate->error);
8126 return false;
8127 } else { return true; }
8128 }
8129
8130 // if no test failled all is ok
8131 return true;
8132 }
8133
8147 public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = 0, $display_type = 'card')
8148 {
8149 global $db, $conf, $langs, $action, $form, $hookmanager;
8150
8151 if (!is_object($form)) {
8152 $form = new Form($db);
8153 }
8154 if (!is_object($extrafields)) {
8155 dol_syslog('Bad parameter extrafields for showOptionals', LOG_ERR);
8156 return 'Bad parameter extrafields for showOptionals';
8157 }
8158 if (!is_array($extrafields->attributes[$this->table_element])) {
8159 dol_syslog("extrafields->attributes was not loaded with extrafields->fetch_name_optionals_label(table_element);", LOG_WARNING);
8160 }
8161
8162 $out = '';
8163
8164 $parameters = array('mode'=>$mode, 'params'=>$params, 'keysuffix'=>$keysuffix, 'keyprefix'=>$keyprefix, 'display_type'=>$display_type);
8165 $reshook = $hookmanager->executeHooks('showOptionals', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
8166
8167 if (empty($reshook)) {
8168 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) {
8169 $out .= "\n";
8170 $out .= '<!-- commonobject:showOptionals --> ';
8171 $out .= "\n";
8172
8173 $nbofextrafieldsshown = 0;
8174 $e = 0; // var to manage the modulo (odd/even)
8175
8176 $lastseparatorkeyfound = '';
8177 $extrafields_collapse_num = '';
8178 $extrafields_collapse_num_old = '';
8179 $i = 0;
8180
8181 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $label) {
8182 $i++;
8183
8184 // Show only the key field in params
8185 if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) {
8186 continue;
8187 }
8188
8189 // Test on 'enabled' ('enabled' is different than 'list' = 'visibility')
8190 $enabled = 1;
8191 if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
8192 $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
8193 }
8194 if (empty($enabled)) {
8195 continue;
8196 }
8197
8198 $visibility = 1;
8199 if ($visibility && isset($extrafields->attributes[$this->table_element]['list'][$key])) {
8200 $visibility = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
8201 }
8202
8203 $perms = 1;
8204 if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
8205 $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
8206 }
8207
8208 if (($mode == 'create') && abs($visibility) != 1 && abs($visibility) != 3) {
8209 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list
8210 } elseif (($mode == 'edit') && abs($visibility) != 1 && abs($visibility) != 3 && abs($visibility) != 4) {
8211 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list and <> 4 = not visible at the creation
8212 } elseif ($mode == 'view' && empty($visibility)) {
8213 continue;
8214 }
8215 if (empty($perms)) {
8216 continue;
8217 }
8218
8219 // Load language if required
8220 if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
8221 $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
8222 }
8223
8224 $colspan = 0;
8225 if (is_array($params) && count($params) > 0 && $display_type=='card') {
8226 if (array_key_exists('cols', $params)) {
8227 $colspan = $params['cols'];
8228 } elseif (array_key_exists('colspan', $params)) { // For backward compatibility. Use cols instead now.
8229 $reg = array();
8230 if (preg_match('/colspan="(\d+)"/', $params['colspan'], $reg)) {
8231 $colspan = $reg[1];
8232 } else {
8233 $colspan = $params['colspan'];
8234 }
8235 }
8236 }
8237 $colspan = intval($colspan);
8238
8239 switch ($mode) {
8240 case "view":
8241 $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
8242 break;
8243 case "create":
8244 case "edit":
8245 // We get the value of property found with GETPOST so it takes into account:
8246 // default values overwrite, restore back to list link, ... (but not 'default value in database' of field)
8247 $check = 'alphanohtml';
8248 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text'))) {
8249 $check = 'restricthtml';
8250 }
8251 $getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, $check, 3); // GETPOST can get value from GET, POST or setup of default values overwrite.
8252 // GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
8253 if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)) {
8254 if (is_array($getposttemp)) {
8255 // $getposttemp is an array but following code expects a comma separated string
8256 $value = implode(",", $getposttemp);
8257 } else {
8258 $value = $getposttemp;
8259 }
8260 } else {
8261 $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.
8262 }
8263 //var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
8264 break;
8265 }
8266
8267 $nbofextrafieldsshown++;
8268
8269 // Output value of the current field
8270 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
8271 $extrafields_collapse_num = $key;
8272 /*
8273 $extrafield_param = $extrafields->attributes[$this->table_element]['param'][$key];
8274 if (!empty($extrafield_param) && is_array($extrafield_param)) {
8275 $extrafield_param_list = array_keys($extrafield_param['options']);
8276
8277 if (count($extrafield_param_list) > 0) {
8278 $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
8279
8280 if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
8281 //$extrafields_collapse_num = $extrafields->attributes[$this->table_element]['pos'][$key];
8282 $extrafields_collapse_num = $key;
8283 }
8284 }
8285 }
8286 */
8287
8288 // if colspan=0 or 1, the second column is not extended, so the separator must be on 2 columns
8289 $out .= $extrafields->showSeparator($key, $this, ($colspan ? $colspan + 1 : 2), $display_type, $mode);
8290
8291 $lastseparatorkeyfound = $key;
8292 } else {
8293 $collapse_group = $extrafields_collapse_num.(!empty($this->id) ? '_'.$this->id : '');
8294
8295 $class = (!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
8296 $csstyle = '';
8297 if (is_array($params) && count($params) > 0) {
8298 if (array_key_exists('class', $params)) {
8299 $class .= $params['class'].' ';
8300 }
8301 if (array_key_exists('style', $params)) {
8302 $csstyle = $params['style'];
8303 }
8304 }
8305
8306 // add html5 elements
8307 $domData = ' data-element="extrafield"';
8308 $domData .= ' data-targetelement="'.$this->element.'"';
8309 $domData .= ' data-targetid="'.$this->id.'"';
8310
8311 $html_id = (empty($this->id) ? '' : 'extrarow-'.$this->element.'_'.$key.'_'.$this->id);
8312 if ($display_type=='card') {
8313 if (!empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0) {
8314 $colspan = 0;
8315 }
8316
8317 if ($action == 'selectlines') {
8318 $colspan++;
8319 }
8320 }
8321
8322 // Convert date into timestamp format (value in memory must be a timestamp)
8323 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date'))) {
8324 $datenotinstring = null;
8325 if (array_key_exists('options_'.$key, $this->array_options)) {
8326 $datenotinstring = $this->array_options['options_'.$key];
8327 if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
8328 $datenotinstring = $this->db->jdate($datenotinstring);
8329 }
8330 }
8331 $datekey = $keyprefix.'options_'.$key.$keysuffix;
8332 $value = (GETPOSTISSET($datekey)) ? dol_mktime(12, 0, 0, GETPOST($datekey.'month', 'int', 3), GETPOST($datekey.'day', 'int', 3), GETPOST($datekey.'year', 'int', 3)) : $datenotinstring;
8333 }
8334 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('datetime'))) {
8335 $datenotinstring = null;
8336 if (array_key_exists('options_'.$key, $this->array_options)) {
8337 $datenotinstring = $this->array_options['options_'.$key];
8338 if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
8339 $datenotinstring = $this->db->jdate($datenotinstring);
8340 }
8341 }
8342 $timekey = $keyprefix.'options_'.$key.$keysuffix;
8343 $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;
8344 }
8345 // Convert float submited string into real php numeric (value in memory must be a php numeric)
8346 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price', 'double'))) {
8347 if (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) {
8348 $value = price2num($value);
8349 } elseif (isset($this->array_options['options_'.$key])) {
8350 $value = $this->array_options['options_'.$key];
8351 }
8352 }
8353
8354 // HTML, text, select, integer and varchar: take into account default value in database if in create mode
8355 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text', 'varchar', 'select', 'radio', 'int', 'boolean'))) {
8356 if ($action == 'create') {
8357 $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) ? $value : $extrafields->attributes[$this->table_element]['default'][$key];
8358 }
8359 }
8360
8361 $labeltoshow = $langs->trans($label);
8362 $helptoshow = $langs->trans($extrafields->attributes[$this->table_element]['help'][$key]);
8363
8364 if ($display_type == 'card') {
8365 $out .= '<tr '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="field_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
8366 if (!empty($conf->global->MAIN_VIEW_LINE_NUMBER) && ($action == 'view' || $action == 'valid' || $action == 'editline' || $action == 'confirm_valid' || $action == 'confirm_cancel')) {
8367 $out .= '<td></td>';
8368 }
8369 $out .= '<td class="'.(empty($params['tdclass']) ? 'titlefieldcreate' : $params['tdclass']).' wordbreak';
8370 } elseif ($display_type == 'line') {
8371 $out .= '<div '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="fieldline_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
8372 $out .= '<div style="display: inline-block; padding-right:4px" class="wordbreak';
8373 }
8374 //$out .= "titlefield";
8375 //if (GETPOST('action', 'restricthtml') == 'create') $out.='create';
8376 // BUG #11554 : For public page, use red dot for required fields, instead of bold label
8377 $tpl_context = isset($params["tpl_context"]) ? $params["tpl_context"] : "none";
8378 if ($tpl_context != "public") { // Public page : red dot instead of fieldrequired characters
8379 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
8380 $out .= ' fieldrequired';
8381 }
8382 }
8383 $out .= '">';
8384 if ($tpl_context == "public") { // Public page : red dot instead of fieldrequired characters
8385 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
8386 $out .= $form->textwithpicto($labeltoshow, $helptoshow);
8387 } else {
8388 $out .= $labeltoshow;
8389 }
8390 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
8391 $out .= '&nbsp;<span style="color: red">*</span>';
8392 }
8393 } else {
8394 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
8395 $out .= $form->textwithpicto($labeltoshow, $helptoshow);
8396 } else {
8397 $out .= $labeltoshow;
8398 }
8399 }
8400
8401 $out .= ($display_type == 'card' ? '</td>' : '</div>');
8402
8403 $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
8404 if ($display_type == 'card') {
8405 // 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
8406 $out .= '<td '.($html_id ? 'id="'.$html_id.'" ' : '').' class="valuefieldcreate '.$this->element.'_extras_'.$key.'" '.($colspan ? ' colspan="'.$colspan.'"' : '').'>';
8407 } elseif ($display_type == 'line') {
8408 $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].'">';
8409 }
8410
8411 switch ($mode) {
8412 case "view":
8413 $out .= $extrafields->showOutputField($key, $value, '', $this->table_element);
8414 break;
8415 case "create":
8416 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
8417 break;
8418 case "edit":
8419 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
8420 break;
8421 }
8422
8423 $out .= ($display_type=='card' ? '</td>' : '</div>');
8424
8425 if (!empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) {
8426 $out .= ($display_type=='card' ? '</tr>' : '</div>');
8427 } else {
8428 $out .= ($display_type=='card' ? '</tr>' : '</div>');
8429 }
8430
8431 $e++;
8432 }
8433 }
8434 $out .= "\n";
8435 // Add code to manage list depending on others
8436 if (!empty($conf->use_javascript_ajax)) {
8437 $out .= $this->getJSListDependancies();
8438 }
8439
8440 $out .= '<!-- commonobject:showOptionals end --> '."\n";
8441
8442 if (empty($nbofextrafieldsshown)) {
8443 $out = '';
8444 }
8445 }
8446 }
8447
8448 $out .= $hookmanager->resPrint;
8449
8450 return $out;
8451 }
8452
8457 public function getJSListDependancies($type = '_extra')
8458 {
8459 $out = '
8460 <script nonce="'.getNonce().'">
8461 jQuery(document).ready(function() {
8462 function showOptions'.$type.'(child_list, parent_list, orig_select)
8463 {
8464 var val = $("select[name=\""+parent_list+"\"]").val();
8465 var parentVal = parent_list + ":" + val;
8466 if(typeof val == "string"){
8467 if(val != "") {
8468 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
8469 $("select[name=\""+child_list+"\"] option[parent]").remove();
8470 $("select[name=\""+child_list+"\"]").append(options);
8471 } else {
8472 var options = orig_select.find("option[parent]").clone();
8473 $("select[name=\""+child_list+"\"] option[parent]").remove();
8474 $("select[name=\""+child_list+"\"]").append(options);
8475 }
8476 } else if(val > 0) {
8477 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
8478 $("select[name=\""+child_list+"\"] option[parent]").remove();
8479 $("select[name=\""+child_list+"\"]").append(options);
8480 } else {
8481 var options = orig_select.find("option[parent]").clone();
8482 $("select[name=\""+child_list+"\"] option[parent]").remove();
8483 $("select[name=\""+child_list+"\"]").append(options);
8484 }
8485 }
8486 function setListDependencies'.$type.'() {
8487 jQuery("select option[parent]").parent().each(function() {
8488 var orig_select = {};
8489 var child_list = $(this).attr("name");
8490 orig_select[child_list] = $(this).clone();
8491 var parent = $(this).find("option[parent]:first").attr("parent");
8492 var infos = parent.split(":");
8493 var parent_list = infos[0];
8494
8495 //Hide daughters lists
8496 if ($("#"+child_list).val() == 0 && $("#"+parent_list).val() == 0){
8497 $("#"+child_list).hide();
8498 //Show mother lists
8499 } else if ($("#"+parent_list).val() != 0){
8500 $("#"+parent_list).show();
8501 }
8502 //Show the child list if the parent list value is selected
8503 $("select[name=\""+parent_list+"\"]").click(function() {
8504 if ($(this).val() != 0){
8505 $("#"+child_list).show()
8506 }
8507 });
8508
8509 //When we change parent list
8510 $("select[name=\""+parent_list+"\"]").change(function() {
8511 showOptions'.$type.'(child_list, parent_list, orig_select[child_list]);
8512 //Select the value 0 on child list after a change on the parent list
8513 $("#"+child_list).val(0).trigger("change");
8514 //Hide child lists if the parent value is set to 0
8515 if ($(this).val() == 0){
8516 $("#"+child_list).hide();
8517 }
8518 });
8519 });
8520 }
8521
8522 setListDependencies'.$type.'();
8523 });
8524 </script>'."\n";
8525 return $out;
8526 }
8527
8533 public function getRights()
8534 {
8535 global $user;
8536
8537 $module = empty($this->module) ? '' : $this->module;
8538 $element = $this->element;
8539
8540 if ($element == 'facturerec') {
8541 $element = 'facture';
8542 } elseif ($element == 'invoice_supplier_rec') {
8543 return empty($user->rights->fournisseur->facture) ? null : $user->rights->fournisseur->facture;
8544 } elseif ($module && !empty($user->rights->$module->$element)) {
8545 // for modules built with ModuleBuilder
8546 return $user->rights->$module->$element;
8547 }
8548
8549 return $user->rights->$element;
8550 }
8551
8564 public static function commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
8565 {
8566 foreach ($tables as $table) {
8567 $sql = 'UPDATE '.$dbs->prefix().$table.' SET fk_soc = '.((int) $dest_id).' WHERE fk_soc = '.((int) $origin_id);
8568
8569 if (!$dbs->query($sql)) {
8570 if ($ignoreerrors) {
8571 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.
8572 }
8573 //$this->errors = $db->lasterror();
8574 return false;
8575 }
8576 }
8577
8578 return true;
8579 }
8580
8593 public static function commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
8594 {
8595 foreach ($tables as $table) {
8596 $sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_product = '.((int) $dest_id).' WHERE fk_product = '.((int) $origin_id);
8597
8598 if (!$dbs->query($sql)) {
8599 if ($ignoreerrors) {
8600 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.
8601 }
8602 //$this->errors = $db->lasterror();
8603 return false;
8604 }
8605 }
8606
8607 return true;
8608 }
8609
8622 public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
8623 {
8624 global $conf;
8625
8626 $buyPrice = 0;
8627
8628 if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull > 0)) {
8629 // When ForceBuyingPriceIfNull is set
8630 $buyPrice = $unitPrice * (1 - $discountPercent / 100);
8631 } else {
8632 // Get cost price for margin calculation
8633 if (!empty($fk_product) && $fk_product > 0) {
8634 if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice') {
8635 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
8636 $product = new Product($this->db);
8637 $result = $product->fetch($fk_product);
8638 if ($result <= 0) {
8639 $this->errors[] = 'ErrorProductIdDoesNotExists';
8640 return -1;
8641 }
8642 if ($product->cost_price > 0) {
8643 $buyPrice = $product->cost_price;
8644 } elseif ($product->pmp > 0) {
8645 $buyPrice = $product->pmp;
8646 }
8647 } elseif (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp') {
8648 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
8649 $product = new Product($this->db);
8650 $result = $product->fetch($fk_product);
8651 if ($result <= 0) {
8652 $this->errors[] = 'ErrorProductIdDoesNotExists';
8653 return -1;
8654 }
8655 if ($product->pmp > 0) {
8656 $buyPrice = $product->pmp;
8657 }
8658 }
8659
8660 if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1', 'pmp', 'costprice'))) {
8661 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
8662 $productFournisseur = new ProductFournisseur($this->db);
8663 if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0) {
8664 $buyPrice = $productFournisseur->fourn_unitprice;
8665 } elseif ($result < 0) {
8666 $this->errors[] = $productFournisseur->error;
8667 return -2;
8668 }
8669 }
8670 }
8671 }
8672 return $buyPrice;
8673 }
8674
8675 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8695 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')
8696 {
8697 // phpcs:enable
8698 global $conf, $user, $langs;
8699
8700 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
8701 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
8702
8703 $sortfield = 'position_name';
8704 $sortorder = 'asc';
8705
8706 $dir = $sdir.'/';
8707 $pdir = '/';
8708
8709 $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
8710 $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
8711
8712 // For backward compatibility
8713 if ($modulepart == 'product') {
8714 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
8715 $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
8716 $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
8717 }
8718 }
8719
8720 // Defined relative dir to DOL_DATA_ROOT
8721 $relativedir = '';
8722 if ($dir) {
8723 $relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
8724 $relativedir = preg_replace('/^[\\/]/', '', $relativedir);
8725 $relativedir = preg_replace('/[\\/]$/', '', $relativedir);
8726 }
8727
8728 $dirthumb = $dir.'thumbs/';
8729 $pdirthumb = $pdir.'thumbs/';
8730
8731 $return = '<!-- Photo -->'."\n";
8732 $nbphoto = 0;
8733
8734 $filearray = dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ?SORT_DESC:SORT_ASC), 1);
8735
8736 /*if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) // For backward compatiblity, we scan also old dirs
8737 {
8738 $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
8739 $filearray=array_merge($filearray, $filearrayold);
8740 }*/
8741
8742 completeFileArrayWithDatabaseInfo($filearray, $relativedir);
8743
8744 if (count($filearray)) {
8745 if ($sortfield && $sortorder) {
8746 $filearray = dol_sort_array($filearray, $sortfield, $sortorder);
8747 }
8748
8749 foreach ($filearray as $key => $val) {
8750 $photo = '';
8751 $file = $val['name'];
8752
8753 //if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
8754 if (image_format_supported($file) >= 0) {
8755 $nbphoto++;
8756 $photo = $file;
8757 $viewfilename = $file;
8758
8759 if ($size == 1 || $size == 'small') { // Format vignette
8760 // Find name of thumb file
8761 $photo_vignette = basename(getImageFileNameForSize($dir.$file, '_small'));
8762 if (!dol_is_file($dirthumb.$photo_vignette)) {
8763 // The thumb does not exists, so we will use the original file
8764 $dirthumb = $dir;
8765 $pdirthumb = $pdir;
8766 $photo_vignette = basename($file);
8767 }
8768
8769 // Get filesize of original file
8770 $imgarray = dol_getImageSize($dir.$photo);
8771
8772 if ($nbbyrow > 0) {
8773 if ($nbphoto == 1) {
8774 $return .= '<table class="valigntop center centpercent" style="border: 0; padding: 2px; border-spacing: 2px; border-collapse: separate;">';
8775 }
8776
8777 if ($nbphoto % $nbbyrow == 1) {
8778 $return .= '<tr class="center valignmiddle" style="border: 1px">';
8779 }
8780 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%" class="photo">'."\n";
8781 } elseif ($nbbyrow < 0) {
8782 $return .= '<div class="inline-block">'."\n";
8783 }
8784
8785 $relativefile = preg_replace('/^\//', '', $pdir.$photo);
8786 if (empty($nolink)) {
8787 $urladvanced = getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
8788 if ($urladvanced) {
8789 $return .= '<a href="'.$urladvanced.'">';
8790 } else {
8791 $return .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank" rel="noopener noreferrer">';
8792 }
8793 }
8794
8795 // Show image (width height=$maxHeight)
8796 // Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
8797 $alt = $langs->transnoentitiesnoconv('File').': '.$relativefile;
8798 $alt .= ' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
8799 if ($overwritetitle) {
8800 if (is_numeric($overwritetitle)) {
8801 $alt = '';
8802 } else {
8803 $alt = $overwritetitle;
8804 }
8805 }
8806
8807 if ($usesharelink) {
8808 if ($val['share']) {
8809 if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
8810 $return .= '<!-- Show original file (thumb not yet available with shared links) -->';
8811 $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).'">';
8812 } else {
8813 $return .= '<!-- Show original file -->';
8814 $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).'">';
8815 }
8816 } else {
8817 $return .= '<!-- Show nophoto file (because file is not shared) -->';
8818 $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
8819 }
8820 } else {
8821 if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
8822 $return .= '<!-- Show thumb -->';
8823 $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).'">';
8824 } else {
8825 $return .= '<!-- Show original file -->';
8826 $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).'">';
8827 }
8828 }
8829
8830 if (empty($nolink)) {
8831 $return .= '</a>';
8832 }
8833
8834 if ($showfilename) {
8835 $return .= '<br>'.$viewfilename;
8836 }
8837 if ($showaction) {
8838 $return .= '<br>';
8839 // On propose la generation de la vignette si elle n'existe pas et si la taille est superieure aux limites
8840 if ($photo_vignette && (image_format_supported($photo) > 0) && ($this->imgWidth > $maxWidth || $this->imgHeight > $maxHeight)) {
8841 $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>';
8842 }
8843 // Special cas for product
8844 if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
8845 // Link to resize
8846 $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; ';
8847
8848 // Link to delete
8849 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
8850 $return .= img_delete().'</a>';
8851 }
8852 }
8853 $return .= "\n";
8854
8855 if ($nbbyrow > 0) {
8856 $return .= '</td>';
8857 if (($nbphoto % $nbbyrow) == 0) {
8858 $return .= '</tr>';
8859 }
8860 } elseif ($nbbyrow < 0) {
8861 $return .= '</div>'."\n";
8862 }
8863 }
8864
8865 if (empty($size)) { // Format origine
8866 $return .= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
8867
8868 if ($showfilename) {
8869 $return .= '<br>'.$viewfilename;
8870 }
8871 if ($showaction) {
8872 // Special case for product
8873 if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
8874 // Link to resize
8875 $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; ';
8876
8877 // Link to delete
8878 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
8879 $return .= img_delete().'</a>';
8880 }
8881 }
8882 }
8883
8884 // On continue ou on arrete de boucler ?
8885 if ($nbmax && $nbphoto >= $nbmax) {
8886 break;
8887 }
8888 }
8889 }
8890
8891 if ($size == 1 || $size == 'small') {
8892 if ($nbbyrow > 0) {
8893 // Ferme tableau
8894 while ($nbphoto % $nbbyrow) {
8895 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%">&nbsp;</td>';
8896 $nbphoto++;
8897 }
8898
8899 if ($nbphoto) {
8900 $return .= '</table>';
8901 }
8902 }
8903 }
8904 }
8905
8906 $this->nbphoto = $nbphoto;
8907
8908 return $return;
8909 }
8910
8911
8918 protected function isArray($info)
8919 {
8920 if (is_array($info)) {
8921 if (isset($info['type']) && $info['type'] == 'array') {
8922 return true;
8923 } else {
8924 return false;
8925 }
8926 }
8927 return false;
8928 }
8929
8936 public function isDate($info)
8937 {
8938 if (isset($info['type']) && ($info['type'] == 'date' || $info['type'] == 'datetime' || $info['type'] == 'timestamp')) {
8939 return true;
8940 }
8941 return false;
8942 }
8943
8950 public function isDuration($info)
8951 {
8952 if (is_array($info)) {
8953 if (isset($info['type']) && ($info['type'] == 'duration')) {
8954 return true;
8955 } else {
8956 return false;
8957 }
8958 } else {
8959 return false;
8960 }
8961 }
8962
8969 public function isInt($info)
8970 {
8971 if (is_array($info)) {
8972 if (isset($info['type']) && (preg_match('/(^int|int$)/i', $info['type']))) {
8973 return true;
8974 } else {
8975 return false;
8976 }
8977 } else {
8978 return false;
8979 }
8980 }
8981
8988 public function isFloat($info)
8989 {
8990 if (is_array($info)) {
8991 if (isset($info['type']) && (preg_match('/^(double|real|price)/i', $info['type']))) {
8992 return true;
8993 } else {
8994 return false;
8995 }
8996 }
8997 return false;
8998 }
8999
9006 public function isText($info)
9007 {
9008 if (is_array($info)) {
9009 if (isset($info['type']) && $info['type'] == 'text') {
9010 return true;
9011 } else {
9012 return false;
9013 }
9014 }
9015 return false;
9016 }
9017
9024 protected function canBeNull($info)
9025 {
9026 if (is_array($info)) {
9027 if (isset($info['notnull']) && $info['notnull'] != '1') {
9028 return true;
9029 } else {
9030 return false;
9031 }
9032 }
9033 return true;
9034 }
9035
9042 protected function isForcedToNullIfZero($info)
9043 {
9044 if (is_array($info)) {
9045 if (isset($info['notnull']) && $info['notnull'] == '-1') {
9046 return true;
9047 } else {
9048 return false;
9049 }
9050 }
9051 return false;
9052 }
9053
9060 protected function isIndex($info)
9061 {
9062 if (is_array($info)) {
9063 if (isset($info['index']) && $info['index'] == true) {
9064 return true;
9065 } else {
9066 return false;
9067 }
9068 }
9069 return false;
9070 }
9071
9072
9081 protected function setSaveQuery()
9082 {
9083 global $conf;
9084
9085 $queryarray = array();
9086 foreach ($this->fields as $field => $info) { // Loop on definition of fields
9087 // Depending on field type ('datetime', ...)
9088 if ($this->isDate($info)) {
9089 if (empty($this->{$field})) {
9090 $queryarray[$field] = null;
9091 } else {
9092 $queryarray[$field] = $this->db->idate($this->{$field});
9093 }
9094 } elseif ($this->isDuration($info)) {
9095 // $this->{$field} may be null, '', 0, '0', 123, '123'
9096 if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
9097 if (!isset($this->{$field})) {
9098 if (!empty($info['default'])) {
9099 $queryarray[$field] = $info['default'];
9100 } else {
9101 $queryarray[$field] = 0;
9102 }
9103 } else {
9104 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9105 }
9106 } else {
9107 $queryarray[$field] = null;
9108 }
9109 } elseif ($this->isInt($info) || $this->isFloat($info)) {
9110 if ($field == 'entity' && is_null($this->{$field})) {
9111 $queryarray[$field] = ((int) $conf->entity);
9112 } else {
9113 // $this->{$field} may be null, '', 0, '0', 123, '123'
9114 if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
9115 if (!isset($this->{$field})) {
9116 $queryarray[$field] = 0;
9117 } elseif ($this->isInt($info)) {
9118 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9119 } elseif ($this->isFloat($info)) {
9120 $queryarray[$field] = (double) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9121 }
9122 } else {
9123 $queryarray[$field] = null;
9124 }
9125 }
9126 } else {
9127 // Note: If $this->{$field} is not defined, it means there is a bug into definition of ->fields or a missing declaration of property
9128 // We should keep the warning generated by this because it is a bug somewhere else in code, not here.
9129 $queryarray[$field] = $this->{$field};
9130 }
9131
9132 if ($info['type'] == 'timestamp' && empty($queryarray[$field])) {
9133 unset($queryarray[$field]);
9134 }
9135 if (!empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) {
9136 $queryarray[$field] = null; // May force 0 to null
9137 }
9138 }
9139
9140 return $queryarray;
9141 }
9142
9149 public function setVarsFromFetchObj(&$obj)
9150 {
9151 global $db;
9152
9153 foreach ($this->fields as $field => $info) {
9154 if ($this->isDate($info)) {
9155 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') {
9156 $this->$field = '';
9157 } else {
9158 $this->$field = $db->jdate($obj->$field);
9159 }
9160 } elseif ($this->isInt($info)) {
9161 if ($field == 'rowid') {
9162 $this->id = (int) $obj->$field;
9163 } else {
9164 if ($this->isForcedToNullIfZero($info)) {
9165 if (empty($obj->$field)) {
9166 $this->$field = null;
9167 } else {
9168 $this->$field = (double) $obj->$field;
9169 }
9170 } else {
9171 if (isset($obj->$field) && (!is_null($obj->$field) || (isset($info['notnull']) && $info['notnull'] == 1))) {
9172 $this->$field = (int) $obj->$field;
9173 } else {
9174 $this->$field = null;
9175 }
9176 }
9177 }
9178 } elseif ($this->isFloat($info)) {
9179 if ($this->isForcedToNullIfZero($info)) {
9180 if (empty($obj->$field)) {
9181 $this->$field = null;
9182 } else {
9183 $this->$field = (double) $obj->$field;
9184 }
9185 } else {
9186 if (isset($obj->$field) && (!is_null($obj->$field) || (isset($info['notnull']) && $info['notnull'] == 1))) {
9187 $this->$field = (double) $obj->$field;
9188 } else {
9189 $this->$field = null;
9190 }
9191 }
9192 } else {
9193 $this->$field = isset($obj->$field) ? $obj->$field : null;
9194 }
9195 }
9196
9197 // If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
9198 if (!isset($this->fields['ref']) && isset($this->id)) {
9199 $this->ref = $this->id;
9200 }
9201 }
9202
9210 public function getFieldList($alias = '', $excludefields = array())
9211 {
9212 $keys = array_keys($this->fields);
9213 if (!empty($alias)) {
9214 $keys_with_alias = array();
9215 foreach ($keys as $fieldname) {
9216 if (!empty($excludefields)) {
9217 if (in_array($fieldname, $excludefields)) { // The field is excluded and must not be in output
9218 continue;
9219 }
9220 }
9221 $keys_with_alias[] = $alias . '.' . $fieldname;
9222 }
9223 return implode(',', $keys_with_alias);
9224 } else {
9225 return implode(',', $keys);
9226 }
9227 }
9228
9236 protected function quote($value, $fieldsentry)
9237 {
9238 if (is_null($value)) {
9239 return 'NULL';
9240 } elseif (preg_match('/^(int|double|real|price)/i', $fieldsentry['type'])) {
9241 return price2num("$value");
9242 } elseif (preg_match('/int$/i', $fieldsentry['type'])) {
9243 return (int) $value;
9244 } elseif ($fieldsentry['type'] == 'boolean') {
9245 if ($value) {
9246 return 'true';
9247 } else {
9248 return 'false';
9249 }
9250 } else {
9251 return "'".$this->db->escape($value)."'";
9252 }
9253 }
9254
9255
9263 public function createCommon(User $user, $notrigger = false)
9264 {
9265 global $langs;
9266 dol_syslog(get_class($this)."::createCommon create", LOG_DEBUG);
9267
9268 $error = 0;
9269
9270 $now = dol_now();
9271
9272 $fieldvalues = $this->setSaveQuery();
9273
9274 if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) {
9275 $fieldvalues['date_creation'] = $this->db->idate($now);
9276 }
9277 if (array_key_exists('fk_user_creat', $fieldvalues) && !($fieldvalues['fk_user_creat'] > 0)) {
9278 $fieldvalues['fk_user_creat'] = $user->id;
9279 $this->fk_user_creat = $user->id;
9280 }
9281 if (array_key_exists('user_modification_id', $fieldvalues) && !($fieldvalues['user_modification_id'] > 0)) {
9282 $fieldvalues['user_modification_id'] = $user->id;
9283 $this->user_modification_id = $user->id;
9284 }
9285 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
9286 if (array_key_exists('ref', $fieldvalues)) {
9287 $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
9288 }
9289
9290 $keys = array();
9291 $values = array(); // Array to store string forged for SQL syntax
9292 foreach ($fieldvalues as $k => $v) {
9293 $keys[$k] = $k;
9294 $value = $this->fields[$k];
9295 $values[$k] = $this->quote($v, $value); // May return string 'NULL' if $value is null
9296 }
9297
9298 // Clean and check mandatory
9299 foreach ($keys as $key) {
9300 // If field is an implicit foreign key field (so type = 'integer:...')
9301 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
9302 $values[$key] = '';
9303 }
9304 if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
9305 $values[$key] = '';
9306 }
9307
9308 if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && is_null($this->fields[$key]['default'])) {
9309 $error++;
9310 $langs->load("errors");
9311 dol_syslog("Mandatory field '".$key."' is empty and required into ->fields definition of class");
9312 $this->errors[] = $langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
9313 }
9314
9315 // If value is null and there is a default value for field
9316 if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && !is_null($this->fields[$key]['default'])) {
9317 $values[$key] = $this->quote($this->fields[$key]['default'], $this->fields[$key]);
9318 }
9319
9320 // If field is an implicit foreign key field (so type = 'integer:...')
9321 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) {
9322 if (isset($this->fields[$key]['default'])) {
9323 $values[$key] = ((int) $this->fields[$key]['default']);
9324 } else {
9325 $values[$key] = 'null';
9326 }
9327 }
9328 if (!empty($this->fields[$key]['foreignkey']) && empty($values[$key])) {
9329 $values[$key] = 'null';
9330 }
9331 }
9332
9333 if ($error) {
9334 return -1;
9335 }
9336
9337 $this->db->begin();
9338
9339 if (!$error) {
9340 $sql = "INSERT INTO ".$this->db->prefix().$this->table_element;
9341 $sql .= " (".implode(", ", $keys).')';
9342 $sql .= " VALUES (".implode(", ", $values).")"; // $values can contains 'abc' or 123
9343
9344 $res = $this->db->query($sql);
9345 if (!$res) {
9346 $error++;
9347 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
9348 $this->errors[] = "ErrorRefAlreadyExists";
9349 } else {
9350 $this->errors[] = $this->db->lasterror();
9351 }
9352 }
9353 }
9354
9355 if (!$error) {
9356 $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
9357 }
9358
9359 // If we have a field ref with a default value of (PROV)
9360 if (!$error) {
9361 if (key_exists('ref', $this->fields) && $this->fields['ref']['notnull'] > 0 && key_exists('default', $this->fields['ref']) && $this->fields['ref']['default'] == '(PROV)') {
9362 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ref = '(PROV".((int) $this->id).")' WHERE (ref = '(PROV)' OR ref = '') AND rowid = ".((int) $this->id);
9363 $resqlupdate = $this->db->query($sql);
9364
9365 if ($resqlupdate === false) {
9366 $error++;
9367 $this->errors[] = $this->db->lasterror();
9368 } else {
9369 $this->ref = '(PROV'.$this->id.')';
9370 }
9371 }
9372 }
9373
9374 // Create extrafields
9375 if (!$error) {
9376 $result = $this->insertExtraFields();
9377 if ($result < 0) {
9378 $error++;
9379 }
9380 }
9381
9382 // Create lines
9383 if (!empty($this->table_element_line) && !empty($this->fk_element)) {
9384 $num = (is_array($this->lines) ? count($this->lines) : 0);
9385 for ($i = 0; $i < $num; $i++) {
9386 $line = $this->lines[$i];
9387
9388 $keyforparent = $this->fk_element;
9389 $line->$keyforparent = $this->id;
9390
9391 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
9392 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
9393 if (!is_object($line)) {
9394 $line = (object) $line;
9395 }
9396
9397 $result = 0;
9398 if (method_exists($line, 'insert')) {
9399 $result = $line->insert($user, 1);
9400 } elseif (method_exists($line, 'create')) {
9401 $result = $line->create($user, 1);
9402 }
9403 if ($result < 0) {
9404 $this->error = $line->error;
9405 $this->db->rollback();
9406 return -1;
9407 }
9408 }
9409 }
9410
9411 // Triggers
9412 if (!$error && !$notrigger) {
9413 // Call triggers
9414 $result = $this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user);
9415 if ($result < 0) {
9416 $error++;
9417 }
9418 // End call triggers
9419 }
9420
9421 // Commit or rollback
9422 if ($error) {
9423 $this->db->rollback();
9424 return -1;
9425 } else {
9426 $this->db->commit();
9427 return $this->id;
9428 }
9429 }
9430
9431
9440 public function fetchCommon($id, $ref = null, $morewhere = '')
9441 {
9442 if (empty($id) && empty($ref) && empty($morewhere)) {
9443 return -1;
9444 }
9445
9446 $fieldlist = $this->getFieldList('t');
9447 if (empty($fieldlist)) {
9448 return 0;
9449 }
9450
9451 $sql = "SELECT ".$fieldlist;
9452 $sql .= " FROM ".$this->db->prefix().$this->table_element.' as t';
9453
9454 if (!empty($id)) {
9455 $sql .= ' WHERE t.rowid = '.((int) $id);
9456 } elseif (!empty($ref)) {
9457 $sql .= " WHERE t.ref = '".$this->db->escape($ref)."'";
9458 } else {
9459 $sql .= ' WHERE 1 = 1'; // usage with empty id and empty ref is very rare
9460 }
9461 if (empty($id) && isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
9462 $sql .= ' AND t.entity IN ('.getEntity($this->element).')';
9463 }
9464 if ($morewhere) {
9465 $sql .= $morewhere;
9466 }
9467 $sql .= ' LIMIT 1'; // This is a fetch, to be sure to get only one record
9468
9469 $res = $this->db->query($sql);
9470 if ($res) {
9471 $obj = $this->db->fetch_object($res);
9472 if ($obj) {
9473 $this->setVarsFromFetchObj($obj);
9474
9475 // Retrieve all extrafield
9476 // fetch optionals attributes and labels
9477 $this->fetch_optionals();
9478
9479 return $this->id;
9480 } else {
9481 return 0;
9482 }
9483 } else {
9484 $this->error = $this->db->lasterror();
9485 $this->errors[] = $this->error;
9486 return -1;
9487 }
9488 }
9489
9496 public function fetchLinesCommon($morewhere = '')
9497 {
9498 $objectlineclassname = get_class($this).'Line';
9499 if (!class_exists($objectlineclassname)) {
9500 $this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
9501 return -1;
9502 }
9503
9504 $objectline = new $objectlineclassname($this->db);
9505
9506 $sql = "SELECT ".$objectline->getFieldList('l');
9507 $sql .= " FROM ".$this->db->prefix().$objectline->table_element." as l";
9508 $sql .= " WHERE l.fk_".$this->db->escape($this->element)." = ".((int) $this->id);
9509 if ($morewhere) {
9510 $sql .= $morewhere;
9511 }
9512 if (isset($objectline->fields['position'])) {
9513 $sql .= $this->db->order('position', 'ASC');
9514 }
9515
9516 $resql = $this->db->query($sql);
9517 if ($resql) {
9518 $num_rows = $this->db->num_rows($resql);
9519 $i = 0;
9520 $this->lines = array();
9521 while ($i < $num_rows) {
9522 $obj = $this->db->fetch_object($resql);
9523 if ($obj) {
9524 $newline = new $objectlineclassname($this->db);
9525 $newline->setVarsFromFetchObj($obj);
9526
9527 $this->lines[] = $newline;
9528 }
9529 $i++;
9530 }
9531
9532 return 1;
9533 } else {
9534 $this->error = $this->db->lasterror();
9535 $this->errors[] = $this->error;
9536 return -1;
9537 }
9538 }
9539
9547 public function updateCommon(User $user, $notrigger = false)
9548 {
9549 global $conf, $langs;
9550 dol_syslog(get_class($this)."::updateCommon update", LOG_DEBUG);
9551
9552 $error = 0;
9553
9554 $now = dol_now();
9555
9556 // $this->oldcopy should have been set by the caller of update
9557 //if (empty($this->oldcopy)) {
9558 // $this->oldcopy = dol_clone($this);
9559 //}
9560
9561 $fieldvalues = $this->setSaveQuery();
9562
9563 if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) {
9564 $fieldvalues['date_modification'] = $this->db->idate($now);
9565 }
9566 if (array_key_exists('fk_user_modif', $fieldvalues) && !($fieldvalues['fk_user_modif'] > 0)) {
9567 $fieldvalues['fk_user_modif'] = $user->id;
9568 }
9569 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
9570 if (array_key_exists('ref', $fieldvalues)) {
9571 $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
9572 }
9573
9574 // Add quotes and escape on fields with type string
9575 $keys = array();
9576 $values = array();
9577 $tmp = array();
9578 foreach ($fieldvalues as $k => $v) {
9579 $keys[$k] = $k;
9580 $value = $this->fields[$k];
9581 $values[$k] = $this->quote($v, $value);
9582 $tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
9583 }
9584
9585 // Clean and check mandatory fields
9586 foreach ($keys as $key) {
9587 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
9588 $values[$key] = ''; // This is an implicit foreign key field
9589 }
9590 if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
9591 $values[$key] = ''; // This is an explicit foreign key field
9592 }
9593
9594 //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
9595 /*
9596 if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
9597 {
9598 $error++;
9599 $this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
9600 }*/
9601 }
9602
9603 $sql = 'UPDATE '.$this->db->prefix().$this->table_element.' SET '.implode(', ', $tmp).' WHERE rowid='.((int) $this->id);
9604
9605 $this->db->begin();
9606
9607 if (!$error) {
9608 $res = $this->db->query($sql);
9609 if (!$res) {
9610 $error++;
9611 $this->errors[] = $this->db->lasterror();
9612 }
9613 }
9614
9615 // Update extrafield
9616 if (!$error) {
9617 $result = $this->insertExtraFields();
9618 if ($result < 0) {
9619 $error++;
9620 }
9621 }
9622
9623 // Triggers
9624 if (!$error && !$notrigger) {
9625 // Call triggers
9626 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
9627 if ($result < 0) {
9628 $error++;
9629 } //Do also here what you must do to rollback action if trigger fail
9630 // End call triggers
9631 }
9632
9633 // Commit or rollback
9634 if ($error) {
9635 $this->db->rollback();
9636 return -1;
9637 } else {
9638 $this->db->commit();
9639 return $this->id;
9640 }
9641 }
9642
9651 public function deleteCommon(User $user, $notrigger = false, $forcechilddeletion = 0)
9652 {
9653 dol_syslog(get_class($this)."::deleteCommon delete", LOG_DEBUG);
9654
9655 $error = 0;
9656
9657 $this->db->begin();
9658
9659 if ($forcechilddeletion) { // Force also delete of childtables that should lock deletion in standard case when option force is off
9660 foreach ($this->childtables as $table) {
9661 $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
9662 $resql = $this->db->query($sql);
9663 if (!$resql) {
9664 $this->error = $this->db->lasterror();
9665 $this->errors[] = $this->error;
9666 $this->db->rollback();
9667 return -1;
9668 }
9669 }
9670 } elseif (!empty($this->childtables)) { // If object has childs linked with a foreign key field, we check all child tables.
9671 $objectisused = $this->isObjectUsed($this->id);
9672 if (!empty($objectisused)) {
9673 dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
9674 $this->error = 'ErrorRecordHasChildren';
9675 $this->errors[] = $this->error;
9676 $this->db->rollback();
9677 return 0;
9678 }
9679 }
9680
9681 // Delete cascade first
9682 if (is_array($this->childtablesoncascade) && !empty($this->childtablesoncascade)) {
9683 foreach ($this->childtablesoncascade as $table) {
9684 $deleteFromObject = explode(':', $table);
9685 if (count($deleteFromObject) >= 2) {
9686 $className = str_replace('@', '', $deleteFromObject[0]);
9687 $filePath = $deleteFromObject[1];
9688 $columnName = $deleteFromObject[2];
9689 $TMoreSQL = array();
9690 $more_sql = $deleteFromObject[3];
9691 if (!empty($more_sql)) {
9692 $TMoreSQL['customsql'] = $more_sql;
9693 }
9694 if (dol_include_once($filePath)) {
9695 $childObject = new $className($this->db);
9696 if (method_exists($childObject, 'deleteByParentField')) {
9697 $result = $childObject->deleteByParentField($this->id, $columnName, $TMoreSQL);
9698 if ($result < 0) {
9699 $error++;
9700 $this->errors[] = $childObject->error;
9701 break;
9702 }
9703 } else {
9704 $error++;
9705 $this->errors[] = "You defined a cascade delete on an object $childObject but there is no method deleteByParentField for it";
9706 break;
9707 }
9708 } else {
9709 $error++;
9710 $this->errors[] = 'Cannot include child class file '.$filePath;
9711 break;
9712 }
9713 } else {
9714 // Delete record in child table
9715 $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
9716
9717 $resql = $this->db->query($sql);
9718 if (!$resql) {
9719 $error++;
9720 $this->error = $this->db->lasterror();
9721 $this->errors[] = $this->error;
9722 break;
9723 }
9724 }
9725 }
9726 }
9727
9728 if (!$error) {
9729 if (!$notrigger) {
9730 // Call triggers
9731 $result = $this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
9732 if ($result < 0) {
9733 $error++;
9734 } // Do also here what you must do to rollback action if trigger fail
9735 // End call triggers
9736 }
9737 }
9738
9739 // Delete llx_ecm_files
9740 if (!$error) {
9741 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
9742 if (!$res) {
9743 $error++;
9744 }
9745 }
9746
9747 // Delete linked object
9748 $res = $this->deleteObjectLinked();
9749 if ($res < 0) {
9750 $error++;
9751 }
9752
9753 if (!$error && !empty($this->isextrafieldmanaged)) {
9754 $result = $this->deleteExtraFields();
9755 if ($result < 0) {
9756 $error++;
9757 }
9758 }
9759
9760 if (!$error) {
9761 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.' WHERE rowid='.((int) $this->id);
9762
9763 $resql = $this->db->query($sql);
9764 if (!$resql) {
9765 $error++;
9766 $this->errors[] = $this->db->lasterror();
9767 }
9768 }
9769
9770 // Commit or rollback
9771 if ($error) {
9772 $this->db->rollback();
9773 return -1;
9774 } else {
9775 $this->db->commit();
9776 return 1;
9777 }
9778 }
9779
9790 public function deleteByParentField($parentId = 0, $parentField = '', $filter = array(), $filtermode = "AND")
9791 {
9792 global $user;
9793
9794 $error = 0;
9795 $deleted = 0;
9796
9797 if (!empty($parentId) && !empty($parentField)) {
9798 $this->db->begin();
9799
9800 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
9801 $sql .= " WHERE ".$parentField." = ".(int) $parentId;
9802
9803 // Manage filters
9804 $sqlwhere = array();
9805 if (count($filter) > 0) {
9806 foreach ($filter as $key => $value) {
9807 if ($key == 'customsql') {
9808 $sqlwhere[] = $value;
9809 } elseif (strpos($value, '%') === false) {
9810 $sqlwhere[] = $key." IN (".$this->db->sanitize($this->db->escape($value)).")";
9811 } else {
9812 $sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'";
9813 }
9814 }
9815 }
9816 if (count($sqlwhere) > 0) {
9817 $sql .= " AND (".implode(" ".$filtermode." ", $sqlwhere).")";
9818 }
9819
9820 $resql = $this->db->query($sql);
9821 if (!$resql) {
9822 $this->errors[] = $this->db->lasterror();
9823 $error++;
9824 } else {
9825 while ($obj = $this->db->fetch_object($resql)) {
9826 $result = $this->fetch($obj->rowid);
9827 if ($result < 0) {
9828 $error++;
9829 $this->errors[] = $this->error;
9830 } else {
9831 if (get_class($this) == 'Contact') { // TODO special code because delete() for contact has not been standardized like other delete.
9832 $result = $this->delete();
9833 } else {
9834 $result = $this->delete($user);
9835 }
9836 if ($result < 0) {
9837 $error++;
9838 $this->errors[] = $this->error;
9839 } else {
9840 $deleted++;
9841 }
9842 }
9843 }
9844 }
9845
9846 if (empty($error)) {
9847 $this->db->commit();
9848 return $deleted;
9849 } else {
9850 $this->error = implode(', ', $this->errors);
9851 $this->db->rollback();
9852 return $error * -1;
9853 }
9854 }
9855
9856 return $deleted;
9857 }
9858
9867 public function deleteLineCommon(User $user, $idline, $notrigger = false)
9868 {
9869 global $conf;
9870
9871 $error = 0;
9872
9873 $tmpforobjectclass = get_class($this);
9874 $tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line';
9875
9876 $this->db->begin();
9877
9878 // Call trigger
9879 $result = $this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user);
9880 if ($result < 0) {
9881 $error++;
9882 }
9883 // End call triggers
9884
9885 if (empty($error)) {
9886 $sql = "DELETE FROM ".$this->db->prefix().$this->table_element_line;
9887 $sql .= " WHERE rowid = ".((int) $idline);
9888
9889 $resql = $this->db->query($sql);
9890 if (!$resql) {
9891 $this->error = "Error ".$this->db->lasterror();
9892 $error++;
9893 }
9894 }
9895
9896 if (empty($error)) {
9897 // Remove extrafields
9898 $tmpobjectline = new $tmpforobjectlineclass($this->db);
9899 if (!isset($tmpobjectline->isextrafieldmanaged) || !empty($tmpobjectline->isextrafieldmanaged)) {
9900 $tmpobjectline->id = $idline;
9901 $result = $tmpobjectline->deleteExtraFields();
9902 if ($result < 0) {
9903 $error++;
9904 $this->error = "Error ".get_class($this)."::deleteLineCommon deleteExtraFields error -4 ".$tmpobjectline->error;
9905 }
9906 }
9907 }
9908
9909 if (empty($error)) {
9910 $this->db->commit();
9911 return 1;
9912 } else {
9913 dol_syslog(get_class($this)."::deleteLineCommon ERROR:".$this->error, LOG_ERR);
9914 $this->db->rollback();
9915 return -1;
9916 }
9917 }
9918
9919
9929 public function setStatusCommon($user, $status, $notrigger = 0, $triggercode = '')
9930 {
9931 $error = 0;
9932
9933 $this->db->begin();
9934
9935 $statusfield = 'status';
9936 if (in_array($this->element, array('don', 'donation', 'shipping'))) {
9937 $statusfield = 'fk_statut';
9938 }
9939
9940 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
9941 $sql .= " SET ".$statusfield." = ".((int) $status);
9942 $sql .= " WHERE rowid = ".((int) $this->id);
9943
9944 if ($this->db->query($sql)) {
9945 if (!$error) {
9946 $this->oldcopy = clone $this;
9947 }
9948
9949 if (!$error && !$notrigger) {
9950 // Call trigger
9951 $result = $this->call_trigger($triggercode, $user);
9952 if ($result < 0) {
9953 $error++;
9954 }
9955 }
9956
9957 if (!$error) {
9958 $this->status = $status;
9959 $this->db->commit();
9960 return 1;
9961 } else {
9962 $this->db->rollback();
9963 return -1;
9964 }
9965 } else {
9966 $this->error = $this->db->error();
9967 $this->db->rollback();
9968 return -1;
9969 }
9970 }
9971
9972
9979 public function initAsSpecimenCommon()
9980 {
9981 global $user;
9982
9983 $this->id = 0;
9984 $this->specimen = 1;
9985 $fields = array(
9986 'label' => 'This is label',
9987 'ref' => 'ABCD1234',
9988 'description' => 'This is a description',
9989 'qty' => 123.12,
9990 'note_public' => 'Public note',
9991 'note_private' => 'Private note',
9992 'date_creation' => (dol_now() - 3600 * 48),
9993 'date_modification' => (dol_now() - 3600 * 24),
9994 'fk_user_creat' => $user->id,
9995 'fk_user_modif' => $user->id,
9996 'date' => dol_now(),
9997 );
9998 foreach ($fields as $key => $value) {
9999 if (array_key_exists($key, $this->fields)) {
10000 $this->{$key} = $value;
10001 }
10002 }
10003
10004 // Force values to default values when known
10005 if (property_exists($this, 'fields')) {
10006 foreach ($this->fields as $key => $value) {
10007 // If fields are already set, do nothing
10008 if (array_key_exists($key, $fields)) {
10009 continue;
10010 }
10011
10012 if (!empty($value['default'])) {
10013 $this->$key = $value['default'];
10014 }
10015 }
10016 }
10017
10018 return 1;
10019 }
10020
10021
10022 /* Part for comments */
10023
10028 public function fetchComments()
10029 {
10030 require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
10031
10032 $comment = new Comment($this->db);
10033 $result = $comment->fetchAllFor($this->element, $this->id);
10034 if ($result < 0) {
10035 $this->errors = array_merge($this->errors, $comment->errors);
10036 return -1;
10037 } else {
10038 $this->comments = $comment->comments;
10039 }
10040 return count($this->comments);
10041 }
10042
10048 public function getNbComments()
10049 {
10050 return count($this->comments);
10051 }
10052
10059 public function trimParameters($parameters)
10060 {
10061 if (!is_array($parameters)) {
10062 return;
10063 }
10064 foreach ($parameters as $parameter) {
10065 if (isset($this->$parameter)) {
10066 $this->$parameter = trim($this->$parameter);
10067 }
10068 }
10069 }
10070
10071 /* Part for categories/tags */
10072
10083 public function getCategoriesCommon($type_categ)
10084 {
10085 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10086
10087 // Get current categories
10088 $c = new Categorie($this->db);
10089 $existing = $c->containing($this->id, $type_categ, 'id');
10090
10091 return $existing;
10092 }
10093
10106 public function setCategoriesCommon($categories, $type_categ = '', $remove_existing = true)
10107 {
10108 // Handle single category
10109 if (!is_array($categories)) {
10110 $categories = array($categories);
10111 }
10112
10113 dol_syslog(get_class($this)."::setCategoriesCommon Oject Id:".$this->id.' type_categ:'.$type_categ.' nb tag add:'.count($categories), LOG_DEBUG);
10114
10115 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10116
10117 if (empty($type_categ)) {
10118 dol_syslog(__METHOD__.': Type '.$type_categ.'is an unknown category type. Done nothing.', LOG_ERR);
10119 return -1;
10120 }
10121
10122 // Get current categories
10123 $c = new Categorie($this->db);
10124 $existing = $c->containing($this->id, $type_categ, 'id');
10125 if ($remove_existing) {
10126 // Diff
10127 if (is_array($existing)) {
10128 $to_del = array_diff($existing, $categories);
10129 $to_add = array_diff($categories, $existing);
10130 } else {
10131 $to_del = array(); // Nothing to delete
10132 $to_add = $categories;
10133 }
10134 } else {
10135 $to_del = array(); // Nothing to delete
10136 $to_add = array_diff($categories, $existing);
10137 }
10138
10139 $error = 0;
10140 $ok = 0;
10141
10142 // Process
10143 foreach ($to_del as $del) {
10144 if ($c->fetch($del) > 0) {
10145 $result=$c->del_type($this, $type_categ);
10146 if ($result < 0) {
10147 $error++;
10148 $this->error = $c->error;
10149 $this->errors = $c->errors;
10150 break;
10151 } else {
10152 $ok += $result;
10153 }
10154 }
10155 }
10156 foreach ($to_add as $add) {
10157 if ($c->fetch($add) > 0) {
10158 $result = $c->add_type($this, $type_categ);
10159 if ($result < 0) {
10160 $error++;
10161 $this->error = $c->error;
10162 $this->errors = $c->errors;
10163 break;
10164 } else {
10165 $ok += $result;
10166 }
10167 }
10168 }
10169
10170 return $error ? (-1 * $error) : $ok;
10171 }
10172
10181 public function cloneCategories($fromId, $toId, $type = '')
10182 {
10183 $this->db->begin();
10184
10185 if (empty($type)) {
10186 $type = $this->table_element;
10187 }
10188
10189 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10190 $categorystatic = new Categorie($this->db);
10191
10192 $sql = "INSERT INTO ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type])." (fk_categorie, fk_product)";
10193 $sql .= " SELECT fk_categorie, $toId FROM ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type]);
10194 $sql .= " WHERE fk_product = ".((int) $fromId);
10195
10196 if (!$this->db->query($sql)) {
10197 $this->error = $this->db->lasterror();
10198 $this->db->rollback();
10199 return -1;
10200 }
10201
10202 $this->db->commit();
10203 return 1;
10204 }
10205
10212 public function deleteEcmFiles($mode = 0)
10213 {
10214 global $conf;
10215
10216 $this->db->begin();
10217
10218 // Delete in database with mode 0
10219 if ($mode == 0) {
10220 switch ($this->element) {
10221 case 'propal':
10222 $element = 'propale';
10223 break;
10224 case 'product':
10225 $element = 'produit';
10226 break;
10227 case 'order_supplier':
10228 $element = 'fournisseur/commande';
10229 break;
10230 case 'invoice_supplier':
10231 $element = 'fournisseur/facture/'.get_exdir($this->id, 2, 0, 1, $this, 'invoice_supplier');
10232 break;
10233 case 'shipping':
10234 $element = 'expedition/sending';
10235 break;
10236 default:
10237 $element = $this->element;
10238 }
10239
10240 // Delete ecm_files extrafields
10241 $sql = "DELETE FROM ".$this->db->prefix()."ecm_files_extrafields WHERE fk_object IN (";
10242 $sql .= " SELECT rowid FROM ".$this->db->prefix()."ecm_files WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
10243 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
10244 $sql .= ")";
10245
10246 if (!$this->db->query($sql)) {
10247 $this->error = $this->db->lasterror();
10248 $this->db->rollback();
10249 return false;
10250 }
10251
10252 // Delete ecm_files
10253 $sql = "DELETE FROM ".$this->db->prefix()."ecm_files";
10254 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
10255 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
10256
10257 if (!$this->db->query($sql)) {
10258 $this->error = $this->db->lasterror();
10259 $this->db->rollback();
10260 return false;
10261 }
10262 }
10263
10264 // Delete in database with mode 1
10265 if ($mode == 1) {
10266 $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files_extrafields";
10267 $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).")";
10268 $resql = $this->db->query($sql);
10269 if (!$resql) {
10270 $this->error = $this->db->lasterror();
10271 $this->db->rollback();
10272 return false;
10273 }
10274
10275 $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files";
10276 $sql .= " WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? "" : "@".$this->module))."' AND src_object_id = ".((int) $this->id);
10277 $resql = $this->db->query($sql);
10278 if (!$resql) {
10279 $this->error = $this->db->lasterror();
10280 $this->db->rollback();
10281 return false;
10282 }
10283 }
10284
10285 $this->db->commit();
10286 return true;
10287 }
10288}
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:464
$object ref
Definition info.php:78
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
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...
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.
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.
fetchCommon($id, $ref=null, $morewhere='')
Load object in memory from the database.
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...
showOptionals($extrafields, $mode='view', $params=null, $keysuffix='', $keyprefix='', $onetrtd=0, $display_type='card')
Function to show lines of extrafields with output datas.
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)
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='', $f_user=null, $notrigger=0)
Delete all links between an object $this.
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.
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 ...
fetchLinesCommon($morewhere='')
Load object in memory from the database.
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.
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...
getCanvas($id=0, $ref='')
Load type of canvas of an object if it exists.
line_up($rowid, $fk_parent_line=true)
Update a line to have a lower rank.
isDuration($info)
Function test if type is duration.
canBeNull($info)
Function test if field can be null.
listeTypeContacts($source='internal', $option=0, $activeonly=0, $code='', $element='', $excludeelement='')
Return array with list of possible values for type of contacts.
call_trigger($triggerName, $user)
Call trigger based on this instance.
getRights()
Returns the rights used for this class.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
cloneCategories($fromId, $toId, $type='')
Copy related categories to another object.
fetch_barcode()
Load data for barcode into properties ->barcode_type* Properties ->barcode_type that is id of barcode...
Class to manage contact/addresses.
Class to manage absolute discounts.
Class to manage a WYSIWYG editor.
Class to manage Dolibarr database access.
Class to manage ECM files.
Class to manage standard extra fields.
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.
print $langs trans("Ref").' m m m statut
Definition index.php:152
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:241
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.
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).
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 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.
dol_print_url($url, $target='_blank', $max=32, $withpicto=0, $morecss='float')
Show Url link.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
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.
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).
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:1632
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:124
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:120
getRandomPassword($generic=false, $replaceambiguouschars=null, $length=32)
Return a generated password using default module.
dol_hash($chain, $type='0')
Returns a hash (non reversible encryption) of a string.