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