dolibarr 18.0.6
commonobject.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2006-2015 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2005-2013 Regis Houssin <regis.houssin@inodbox.com>
4 * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
5 * Copyright (C) 2012-2013 Christophe Battarel <christophe.battarel@altairis.fr>
6 * Copyright (C) 2011-2022 Philippe Grand <philippe.grand@atoo-net.com>
7 * Copyright (C) 2012-2015 Marcos García <marcosgdf@gmail.com>
8 * Copyright (C) 2012-2015 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
9 * Copyright (C) 2012 Cedric Salvador <csalvador@gpcsolutions.fr>
10 * Copyright (C) 2015-2022 Alexandre Spangaro <aspangaro@open-dsi.fr>
11 * Copyright (C) 2016 Bahfir abbes <dolipar@dolipar.org>
12 * Copyright (C) 2017 ATM Consulting <support@atm-consulting.fr>
13 * Copyright (C) 2017-2019 Nicolas ZABOURI <info@inovea-conseil.com>
14 * Copyright (C) 2017 Rui Strecht <rui.strecht@aliartalentos.com>
15 * Copyright (C) 2018-2023 Frédéric France <frederic.france@netlogic.fr>
16 * Copyright (C) 2018 Josep Lluís Amador <joseplluis@lliuretic.cat>
17 * Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
18 * Copyright (C) 2021 Grégory Blémand <gregory.blemand@atm-consulting.fr>
19 * Copyright (C) 2023 Lenin Rivas <lenin.rivas777@gmail.com>
20 *
21 * This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 3 of the License, or
24 * (at your option) any later version.
25 *
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
30 *
31 * You should have received a copy of the GNU General Public License
32 * along with this program. If not, see <https://www.gnu.org/licenses/>.
33 */
34
45abstract class CommonObject
46{
47 const TRIGGER_PREFIX = ''; // to be overriden in child class implementations, i.e. 'BILL', 'TASK', 'PROPAL', etc.
48
52 public $module;
53
57 public $db;
58
62 public $id;
63
67 public $entity;
68
73 public $error;
74
78 public $errorhidden;
79
83 public $errors = array();
84
88 private $validateFieldsErrors = array();
89
93 public $element;
94
98 public $fk_element;
99
104 public $element_for_permission;
105
109 public $table_element;
110
114 public $table_element_line = '';
115
119 public $ismultientitymanaged;
120
124 public $import_key;
125
129 public $array_options = array();
130
134 public $fields = array();
135
139 public $array_languages = null; // Value is array() when load already tried
140
144 public $contacts_ids;
145
149 public $linked_objects;
150
154 public $linkedObjectsIds;
155
159 public $linkedObjects;
160
164 private $linkedObjectsFullLoaded = array();
165
169 public $oldcopy;
173 public $oldref;
174
178 protected $table_ref_field = '';
179
183 public $restrictiononfksoc = 0;
184
185
186 // Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them.
187
191 public $context = array();
192
196 public $canvas;
197
202 public $project;
203
208 public $fk_project;
209
215 public $projet;
216
222
227 public $contact;
228
233 public $contact_id;
234
239 public $thirdparty;
240
245 public $user;
246
251 public $origin;
252
257 public $origin_id;
258
262 public $ref;
263
267 public $ref_ext;
268
272 public $ref_previous;
273
277 public $ref_next;
278
282 public $newref;
283
288 public $statut;
289
294 public $status;
295
296
301 public $country;
302
307 public $country_id;
308
313 public $country_code;
314
319 public $state;
320
325 public $state_id;
326
331 public $state_code;
332
337 public $region_id;
338
343 public $region_code;
344
349 public $region;
350
351
356 public $barcode_type;
357
362 public $barcode_type_code;
363
368 public $barcode_type_label;
369
374 public $barcode_type_coder;
375
380 public $mode_reglement_id;
381
386 public $cond_reglement_id;
387
391 public $demand_reason_id;
392
397 public $transport_mode_id;
398
404 public $cond_reglement;
405
411 public $fk_delivery_address;
412
417 public $shipping_method_id;
418
423 public $shipping_method;
424
428 public $multicurrency_code;
429
433 public $multicurrency_tx;
434
439 public $model_pdf;
440
446 public $modelpdf;
447
452 public $last_main_doc;
453
459 public $fk_bank;
460
465 public $fk_account;
466
470 public $openid;
471
476 public $note_public;
477
482 public $note_private;
483
488 public $note;
489
494 public $total_ht;
495
500 public $total_tva;
501
506 public $total_localtax1;
507
512 public $total_localtax2;
513
518 public $total_ttc;
519
523 public $lines;
524
529 public $comments = array();
530
534 public $name;
535
539 public $lastname;
540
544 public $firstname;
545
549 public $civility_id;
550
551 // Dates
555 public $date_creation;
556
560 public $date_validation; // Date validation
561
565 public $date_modification; // Date last change (tms field)
570 public $date_update;
571
575 public $date_cloture; // Date closing (tms field)
576
581 public $user_author;
586 public $user_creation;
590 public $user_creation_id;
591
596 public $user_valid;
601 public $user_validation;
605 public $user_validation_id;
609 public $user_closing_id;
610
615 public $user_modification;
619 public $user_modification_id;
620
621
626 public $fk_user_creat;
627
632 public $fk_user_modif;
633
634
635 public $next_prev_filter;
636
640 public $specimen = 0;
641
645 public $sendtoid;
646
650 public $alreadypaid;
651
652
653 public $labelStatus;
654 protected $labelStatusShort;
655
659 public $showphoto_on_popup;
660
664 public $nb = array();
665
669 public $output;
670
674 public $extraparams = array();
675
679 protected $childtables = array();
680
686 protected $childtablesoncascade = array();
687
688
689 // No constructor as it is an abstract class
690
691
702 public static function isExistingObject($element, $id, $ref = '', $ref_ext = '')
703 {
704 global $db, $conf;
705
706 $sql = "SELECT rowid, ref, ref_ext";
707 $sql .= " FROM ".$db->prefix().$element;
708 $sql .= " WHERE entity IN (".getEntity($element).")";
709
710 if ($id > 0) {
711 $sql .= " AND rowid = ".((int) $id);
712 } elseif ($ref) {
713 $sql .= " AND ref = '".$db->escape($ref)."'";
714 } elseif ($ref_ext) {
715 $sql .= " AND ref_ext = '".$db->escape($ref_ext)."'";
716 } else {
717 $error = 'ErrorWrongParameters';
718 dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
719 return -1;
720 }
721 if ($ref || $ref_ext) { // Because the same ref can exists in 2 different entities, we force the current one in priority
722 $sql .= " AND entity = ".((int) $conf->entity);
723 }
724
725 dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
726 $resql = $db->query($sql);
727 if ($resql) {
728 $num = $db->num_rows($resql);
729 if ($num > 0) {
730 return 1;
731 } else {
732 return 0;
733 }
734 }
735 return -1;
736 }
737
744 public function setErrorsFromObject($object)
745 {
746 if (!empty($object->error)) {
747 $this->error = $object->error;
748 }
749 if (!empty($object->errors)) {
750 $this->errors = array_merge($this->errors, $object->errors);
751 }
752 }
753
761 public function getTooltipContentArray($params)
762 {
763 return [];
764 }
765
773 public function getTooltipContent($params)
774 {
775 global $action, $extrafields, $langs, $hookmanager;
776
777 // If there is too much extrafields, we do not include them into tooltip
778 $MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP = getDolGlobalInt('MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP', 3);
779
780 $datas = $this->getTooltipContentArray($params);
781 $count = 0;
782
783 // Add extrafields
784 if (!empty($extrafields->attributes[$this->table_element]['label'])) {
785 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
786 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
787 continue;
788 }
789 if ($count >= abs($MAX_EXTRAFIELDS_TO_SHOW_IN_TOOLTIP)) {
790 $datas['more_extrafields'] = '<br>...';
791 break;
792 }
793 $enabled = 1;
794 if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
795 $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
796 }
797 if ($enabled && isset($extrafields->attributes[$this->table_element]['list'][$key])) {
798 $enabled = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
799 }
800 $perms = 1;
801 if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
802 $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
803 }
804 if (empty($enabled)) {
805 continue; // 0 = Never visible field
806 }
807 if (abs($enabled) != 1 && abs($enabled) != 3 && abs($enabled) != 5 && abs($enabled) != 4) {
808 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list <> 4 = not visible at the creation
809 }
810 if (empty($perms)) {
811 continue; // 0 = Not visible
812 }
813 if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
814 $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
815 }
816 $labelextra = $langs->trans((string) $extrafields->attributes[$this->table_element]['label'][$key]);
817 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
818 $datas[$key]= '<br><b><u>'. $labelextra . '</u></b>';
819 } else {
820 $value = (empty($this->array_options['options_' . $key]) ? '' : $this->array_options['options_' . $key]);
821 $datas[$key]= '<br><b>'. $labelextra . ':</b> ' . $extrafields->showOutputField($key, $value, '', $this->table_element);
822 $count++;
823 }
824 }
825 }
826
827 $hookmanager->initHooks(array($this->element . 'dao'));
828 $parameters = array(
829 'tooltipcontentarray' => &$datas,
830 'params' => $params,
831 );
832 // Note that $action and $object may have been modified by some hooks
833 $hookmanager->executeHooks('getTooltipContent', $parameters, $this, $action);
834
835 //var_dump($datas);
836 $label = implode($datas);
837
838 return $label;
839 }
840
841
847 public function errorsToString()
848 {
849 return $this->error.(is_array($this->errors) ? (($this->error != '' ? ', ' : '').join(', ', $this->errors)) : '');
850 }
851
852
859 public function getFormatedCustomerRef($objref)
860 {
861 global $hookmanager;
862
863 $parameters = array('objref'=>$objref);
864 $action = '';
865 $reshook = $hookmanager->executeHooks('getFormatedCustomerRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
866 if ($reshook > 0) {
867 return $hookmanager->resArray['objref'];
868 }
869 return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
870 }
871
878 public function getFormatedSupplierRef($objref)
879 {
880 global $hookmanager;
881
882 $parameters = array('objref'=>$objref);
883 $action = '';
884 $reshook = $hookmanager->executeHooks('getFormatedSupplierRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
885 if ($reshook > 0) {
886 return $hookmanager->resArray['objref'];
887 }
888 return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
889 }
890
900 public function getFullAddress($withcountry = 0, $sep = "\n", $withregion = 0, $extralangcode = '')
901 {
902 if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country))) {
903 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
904 $tmparray = getCountry($this->country_id, 'all');
905 $this->country_code = $tmparray['code'];
906 $this->country = $tmparray['label'];
907 }
908
909 if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_code))) {
910 require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
911 $tmparray = getState($this->state_id, 'all', 0, 1);
912 $this->state_code = $tmparray['code'];
913 $this->state = $tmparray['label'];
914 $this->region_code = $tmparray['region_code'];
915 $this->region = $tmparray['region'];
916 }
917
918 return dol_format_address($this, $withcountry, $sep, '', 0, $extralangcode);
919 }
920
921
930 public function getLastMainDocLink($modulepart, $initsharekey = 0, $relativelink = 0)
931 {
932 global $user, $dolibarr_main_url_root;
933
934 if (empty($this->last_main_doc)) {
935 return ''; // No way to known which document name to use
936 }
937
938 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
939 $ecmfile = new EcmFiles($this->db);
940 $result = $ecmfile->fetch(0, '', $this->last_main_doc);
941 if ($result < 0) {
942 $this->error = $ecmfile->error;
943 $this->errors = $ecmfile->errors;
944 return -1;
945 }
946
947 if (empty($ecmfile->id)) {
948 // Add entry into index
949 if ($initsharekey) {
950 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
951
952 // TODO We can't, we dont' have full path of file, only last_main_doc and ->element, so we must first rebuild full path $destfull
953 /*
954 $ecmfile->filepath = $rel_dir;
955 $ecmfile->filename = $filename;
956 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
957 $ecmfile->fullpath_orig = '';
958 $ecmfile->gen_or_uploaded = 'generated';
959 $ecmfile->description = ''; // indexed content
960 $ecmfile->keywords = ''; // keyword content
961 $ecmfile->share = getRandomPassword(true);
962 $result = $ecmfile->create($user);
963 if ($result < 0)
964 {
965 $this->error = $ecmfile->error;
966 $this->errors = $ecmfile->errors;
967 }
968 */
969 } else {
970 return '';
971 }
972 } elseif (empty($ecmfile->share)) {
973 // Add entry into index
974 if ($initsharekey) {
975 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
976 $ecmfile->share = getRandomPassword(true);
977 $ecmfile->update($user);
978 } else {
979 return '';
980 }
981 }
982 // Define $urlwithroot
983 $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
984 // This is to use external domain name found into config file
985 //if (DOL_URL_ROOT && ! preg_match('/\/$/', $urlwithouturlroot) && ! preg_match('/^\//', DOL_URL_ROOT)) $urlwithroot=$urlwithouturlroot.'/'.DOL_URL_ROOT;
986 //else
987 $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT;
988 //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
989
990 $forcedownload = 0;
991
992 $paramlink = '';
993 //if (!empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart; // For sharing with hash (so public files), modulepart is not required.
994 //if (!empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; // For sharing with hash (so public files), entity is not required.
995 //$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath); // No need of name of file for public link, we will use the hash
996 if (!empty($ecmfile->share)) {
997 $paramlink .= ($paramlink ? '&' : '').'hashp='.$ecmfile->share; // Hash for public share
998 }
999 if ($forcedownload) {
1000 $paramlink .= ($paramlink ? '&' : '').'attachment=1';
1001 }
1002
1003 if ($relativelink) {
1004 $linktoreturn = 'document.php'.($paramlink ? '?'.$paramlink : '');
1005 } else {
1006 $linktoreturn = $urlwithroot.'/document.php'.($paramlink ? '?'.$paramlink : '');
1007 }
1008
1009 // Here $ecmfile->share is defined
1010 return $linktoreturn;
1011 }
1012
1013
1014 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1024 public function add_contact($fk_socpeople, $type_contact, $source = 'external', $notrigger = 0)
1025 {
1026 // phpcs:enable
1027 global $user, $langs;
1028
1029
1030 dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
1031
1032 // Check parameters
1033 if ($fk_socpeople <= 0) {
1034 $langs->load("errors");
1035 $this->error = $langs->trans("ErrorWrongValueForParameterX", "1");
1036 dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
1037 return -1;
1038 }
1039 if (!$type_contact) {
1040 $langs->load("errors");
1041 $this->error = $langs->trans("ErrorWrongValueForParameterX", "2");
1042 dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
1043 return -2;
1044 }
1045
1046 $id_type_contact = 0;
1047 if (is_numeric($type_contact)) {
1048 $id_type_contact = $type_contact;
1049 } else {
1050 // We look for id type_contact
1051 $sql = "SELECT tc.rowid";
1052 $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1053 $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1054 $sql .= " AND tc.source='".$this->db->escape($source)."'";
1055 $sql .= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
1056 //print $sql;
1057 $resql = $this->db->query($sql);
1058 if ($resql) {
1059 $obj = $this->db->fetch_object($resql);
1060 if ($obj) {
1061 $id_type_contact = $obj->rowid;
1062 }
1063 }
1064 }
1065
1066 if ($id_type_contact == 0) {
1067 dol_syslog("CODE_NOT_VALID_FOR_THIS_ELEMENT: Code type of contact '".$type_contact."' does not exists or is not active for element ".$this->element.", we can ignore it");
1068 return 0;
1069 }
1070
1071 $datecreate = dol_now();
1072
1073 // Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
1074 $TListeContacts = $this->liste_contact(-1, $source);
1075 $already_added = false;
1076 if (is_array($TListeContacts) && !empty($TListeContacts)) {
1077 foreach ($TListeContacts as $array_contact) {
1078 if ($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
1079 $already_added = true;
1080 break;
1081 }
1082 }
1083 }
1084
1085 if (!$already_added) {
1086 $this->db->begin();
1087
1088 // Insert into database
1089 $sql = "INSERT INTO ".$this->db->prefix()."element_contact";
1090 $sql .= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
1091 $sql .= " VALUES (".$this->id.", ".((int) $fk_socpeople)." , ";
1092 $sql .= "'".$this->db->idate($datecreate)."'";
1093 $sql .= ", 4, ".((int) $id_type_contact);
1094 $sql .= ")";
1095
1096 $resql = $this->db->query($sql);
1097 if ($resql) {
1098 if (!$notrigger) {
1099 $result = $this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
1100 if ($result < 0) {
1101 $this->db->rollback();
1102 return -1;
1103 }
1104 }
1105
1106 $this->db->commit();
1107 return 1;
1108 } else {
1109 if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1110 $this->error = $this->db->errno();
1111 $this->db->rollback();
1112 return -2;
1113 } else {
1114 $this->error = $this->db->lasterror();
1115 $this->db->rollback();
1116 return -1;
1117 }
1118 }
1119 } else {
1120 return 0;
1121 }
1122 }
1123
1124 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1132 public function copy_linked_contact($objFrom, $source = 'internal')
1133 {
1134 // phpcs:enable
1135 $contacts = $objFrom->liste_contact(-1, $source);
1136 foreach ($contacts as $contact) {
1137 if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0) {
1138 return -1;
1139 }
1140 }
1141 return 1;
1142 }
1143
1144 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1154 public function update_contact($rowid, $statut, $type_contact_id = 0, $fk_socpeople = 0)
1155 {
1156 // phpcs:enable
1157 // Insert into database
1158 $sql = "UPDATE ".$this->db->prefix()."element_contact set";
1159 $sql .= " statut = ".$statut;
1160 if ($type_contact_id) {
1161 $sql .= ", fk_c_type_contact = ".((int) $type_contact_id);
1162 }
1163 if ($fk_socpeople) {
1164 $sql .= ", fk_socpeople = ".((int) $fk_socpeople);
1165 }
1166 $sql .= " where rowid = ".((int) $rowid);
1167 $resql = $this->db->query($sql);
1168 if ($resql) {
1169 return 0;
1170 } else {
1171 $this->error = $this->db->lasterror();
1172 return -1;
1173 }
1174 }
1175
1176 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1184 public function delete_contact($rowid, $notrigger = 0)
1185 {
1186 // phpcs:enable
1187 global $user;
1188
1189 $error = 0;
1190
1191 $this->db->begin();
1192
1193 if (!$error && empty($notrigger)) {
1194 // Call trigger
1195 $this->context['contact_id'] = ((int) $rowid);
1196 $result = $this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
1197 if ($result < 0) {
1198 $error++;
1199 }
1200 // End call triggers
1201 }
1202
1203 if (!$error) {
1204 dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
1205
1206 $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
1207 $sql .= " WHERE rowid = ".((int) $rowid);
1208
1209 $result = $this->db->query($sql);
1210 if (!$result) {
1211 $error++;
1212 $this->errors[] = $this->db->lasterror();
1213 }
1214 }
1215
1216 if (!$error) {
1217 $this->db->commit();
1218 return 1;
1219 } else {
1220 $this->error = $this->db->lasterror();
1221 $this->db->rollback();
1222 return -1;
1223 }
1224 }
1225
1226 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1234 public function delete_linked_contact($source = '', $code = '')
1235 {
1236 // phpcs:enable
1237 $listId = '';
1238 $temp = array();
1239 $typeContact = $this->liste_type_contact($source, '', 0, 0, $code);
1240
1241 if (!empty($typeContact)) {
1242 foreach ($typeContact as $key => $value) {
1243 array_push($temp, $key);
1244 }
1245 $listId = implode(",", $temp);
1246 }
1247
1248 // If $listId is empty, we have not criteria on fk_c_type_contact so we will delete record on element_id for
1249 // any type or record instead of only the ones of the current object. So we do nothing in such a case.
1250 if (empty($listId)) {
1251 return 0;
1252 }
1253
1254 $sql = "DELETE FROM ".$this->db->prefix()."element_contact";
1255 $sql .= " WHERE element_id = ".((int) $this->id);
1256 $sql .= " AND fk_c_type_contact IN (".$this->db->sanitize($listId).")";
1257
1258 dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
1259 if ($this->db->query($sql)) {
1260 return 1;
1261 } else {
1262 $this->error = $this->db->lasterror();
1263 return -1;
1264 }
1265 }
1266
1267 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1279 public function liste_contact($statusoflink = -1, $source = 'external', $list = 0, $code = '', $status = -1, $arrayoftcids = array())
1280 {
1281 // phpcs:enable
1282 global $langs;
1283
1284 $tab = array();
1285
1286 $sql = "SELECT ec.rowid, ec.statut as statuslink, ec.fk_socpeople as id, ec.fk_c_type_contact"; // This field contains id of llx_socpeople or id of llx_user
1287 if ($source == 'internal') {
1288 $sql .= ", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
1289 }
1290 if ($source == 'external' || $source == 'thirdparty') {
1291 $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
1292 }
1293 $sql .= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
1294 $sql .= ", tc.source, tc.element, tc.code, tc.libelle";
1295 $sql .= " FROM ".$this->db->prefix()."c_type_contact tc,";
1296 $sql .= " ".$this->db->prefix()."element_contact ec";
1297 if ($source == 'internal') { // internal contact (user)
1298 $sql .= " LEFT JOIN ".$this->db->prefix()."user t on ec.fk_socpeople = t.rowid";
1299 }
1300 if ($source == 'external' || $source == 'thirdparty') { // external contact (socpeople)
1301 $sql .= " LEFT JOIN ".$this->db->prefix()."socpeople t on ec.fk_socpeople = t.rowid";
1302 }
1303 $sql .= " WHERE ec.element_id = ".((int) $this->id);
1304 $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1305 $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1306 if ($code) {
1307 $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1308 }
1309 if ($source == 'internal') {
1310 $sql .= " AND tc.source = 'internal'";
1311 if ($status >= 0) {
1312 $sql .= " AND t.statut = ".((int) $status);
1313 }
1314 }
1315 if ($source == 'external' || $source == 'thirdparty') {
1316 $sql .= " AND tc.source = 'external'";
1317 if ($status >= 0) {
1318 $sql .= " AND t.statut = ".((int) $status); // t is llx_socpeople
1319 }
1320 }
1321 $sql .= " AND tc.active = 1";
1322 if ($statusoflink >= 0) {
1323 $sql .= " AND ec.statut = ".((int) $statusoflink);
1324 }
1325 $sql .= " ORDER BY t.lastname ASC";
1326
1327 dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
1328 $resql = $this->db->query($sql);
1329 if ($resql) {
1330 $num = $this->db->num_rows($resql);
1331 $i = 0;
1332 while ($i < $num) {
1333 $obj = $this->db->fetch_object($resql);
1334
1335 if (!$list) {
1336 $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
1337 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1338 $tab[$i] = array(
1339 'parentId' => $this->id,
1340 'source' => $obj->source,
1341 'socid' => $obj->socid,
1342 'id' => $obj->id,
1343 'nom' => $obj->lastname, // For backward compatibility
1344 'civility' => $obj->civility,
1345 'lastname' => $obj->lastname,
1346 'firstname' => $obj->firstname,
1347 'email'=>$obj->email,
1348 'login'=> (empty($obj->login) ? '' : $obj->login),
1349 'photo' => (empty($obj->photo) ? '' : $obj->photo),
1350 'statuscontact' => $obj->statuscontact,
1351 'rowid' => $obj->rowid,
1352 'code' => $obj->code,
1353 'libelle' => $libelle_type,
1354 'status' => $obj->statuslink,
1355 'fk_c_type_contact' => $obj->fk_c_type_contact
1356 );
1357 } else {
1358 $tab[$i] = $obj->id;
1359 }
1360
1361 $i++;
1362 }
1363
1364 return $tab;
1365 } else {
1366 $this->error = $this->db->lasterror();
1367 dol_print_error($this->db);
1368 return -1;
1369 }
1370 }
1371
1372
1379 public function swapContactStatus($rowid)
1380 {
1381 $sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1382 $sql .= " tc.code, tc.libelle";
1383 $sql .= " FROM (".$this->db->prefix()."element_contact as ec, ".$this->db->prefix()."c_type_contact as tc)";
1384 $sql .= " WHERE ec.rowid =".((int) $rowid);
1385 $sql .= " AND ec.fk_c_type_contact=tc.rowid";
1386 $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1387
1388 dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1389 $resql = $this->db->query($sql);
1390 if ($resql) {
1391 $obj = $this->db->fetch_object($resql);
1392 $newstatut = ($obj->statut == 4) ? 5 : 4;
1393 $result = $this->update_contact($rowid, $newstatut);
1394 $this->db->free($resql);
1395 return $result;
1396 } else {
1397 $this->error = $this->db->error();
1398 dol_print_error($this->db);
1399 return -1;
1400 }
1401 }
1402
1403 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1414 public function liste_type_contact($source = 'internal', $order = 'position', $option = 0, $activeonly = 0, $code = '')
1415 {
1416 // phpcs:enable
1417 global $langs;
1418
1419 if (empty($order)) {
1420 $order = 'position';
1421 }
1422 if ($order == 'position') {
1423 $order .= ',code';
1424 }
1425
1426 $tab = array();
1427 $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position";
1428 $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1429 $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1430 if ($activeonly == 1) {
1431 $sql .= " AND tc.active=1"; // only the active types
1432 }
1433 if (!empty($source) && $source != 'all') {
1434 $sql .= " AND tc.source='".$this->db->escape($source)."'";
1435 }
1436 if (!empty($code)) {
1437 $sql .= " AND tc.code='".$this->db->escape($code)."'";
1438 }
1439 $sql .= $this->db->order($order, 'ASC');
1440
1441 //print "sql=".$sql;
1442 $resql = $this->db->query($sql);
1443 if ($resql) {
1444 $num = $this->db->num_rows($resql);
1445 $i = 0;
1446 while ($i < $num) {
1447 $obj = $this->db->fetch_object($resql);
1448
1449 $transkey = "TypeContact_".$this->element."_".$source."_".$obj->code;
1450 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1451 if (empty($option)) {
1452 $tab[$obj->rowid] = $libelle_type;
1453 } else {
1454 $tab[$obj->code] = $libelle_type;
1455 }
1456 $i++;
1457 }
1458 return $tab;
1459 } else {
1460 $this->error = $this->db->lasterror();
1461 //dol_print_error($this->db);
1462 return null;
1463 }
1464 }
1465
1477 public function listeTypeContacts($source = 'internal', $option = 0, $activeonly = 0, $code = '', $element = '', $excludeelement = '')
1478 {
1479 global $langs, $conf;
1480
1481 $langs->loadLangs(array('bills', 'contracts', 'interventions', 'orders', 'projects', 'propal', 'ticket', 'agenda'));
1482
1483 $tab = array();
1484
1485 $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position, tc.element";
1486 $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1487
1488 $sqlWhere = array();
1489 if (!empty($element)) {
1490 $sqlWhere[] = " tc.element='".$this->db->escape($element)."'";
1491 }
1492 if (!empty($excludeelement)) {
1493 $sqlWhere[] = " tc.element <> '".$this->db->escape($excludeelement)."'";
1494 }
1495
1496 if ($activeonly == 1) {
1497 $sqlWhere[] = " tc.active=1"; // only the active types
1498 }
1499
1500 if (!empty($source) && $source != 'all') {
1501 $sqlWhere[] = " tc.source='".$this->db->escape($source)."'";
1502 }
1503
1504 if (!empty($code)) {
1505 $sqlWhere[] = " tc.code='".$this->db->escape($code)."'";
1506 }
1507
1508 if (count($sqlWhere) > 0) {
1509 $sql .= " WHERE ".implode(' AND ', $sqlWhere);
1510 }
1511
1512 $sql .= $this->db->order('tc.element, tc.position', 'ASC');
1513
1514 dol_syslog(__METHOD__, LOG_DEBUG);
1515 $resql = $this->db->query($sql);
1516 if ($resql) {
1517 $num = $this->db->num_rows($resql);
1518 if ($num > 0) {
1519 $langs->loadLangs(array("propal", "orders", "bills", "suppliers", "contracts", "supplier_proposal"));
1520
1521 while ($obj = $this->db->fetch_object($resql)) {
1522 $modulename = $obj->element;
1523 if (strpos($obj->element, 'project') !== false) {
1524 $modulename = 'projet';
1525 } elseif ($obj->element == 'contrat') {
1526 $element = 'contract';
1527 } elseif ($obj->element == 'action') {
1528 $modulename = 'agenda';
1529 } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1530 $modulename = 'fournisseur';
1531 } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1532 $modulename = 'fournisseur';
1533 }
1534 if (!empty($conf->{$modulename}->enabled)) {
1535 $libelle_element = $langs->trans('ContactDefault_'.$obj->element);
1536 $tmpelement = $obj->element;
1537 $transkey = "TypeContact_".$tmpelement."_".$source."_".$obj->code;
1538 $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1539 if (empty($option)) {
1540 $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1541 } else {
1542 $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1543 }
1544 }
1545 }
1546 }
1547 return $tab;
1548 } else {
1549 $this->error = $this->db->lasterror();
1550 return null;
1551 }
1552 }
1553
1565 public function getIdContact($source, $code, $status = 0)
1566 {
1567 global $conf;
1568
1569 $result = array();
1570 $i = 0;
1571 //cas particulier pour les expeditions
1572 if ($this->element == 'shipping' && $this->origin_id != 0) {
1573 $id = $this->origin_id;
1574 $element = 'commande';
1575 } elseif ($this->element == 'reception' && $this->origin_id != 0) {
1576 $id = $this->origin_id;
1577 $element = 'order_supplier';
1578 } else {
1579 $id = $this->id;
1580 $element = $this->element;
1581 }
1582
1583 $sql = "SELECT ec.fk_socpeople";
1584 $sql .= " FROM ".$this->db->prefix()."element_contact as ec,";
1585 if ($source == 'internal') {
1586 $sql .= " ".$this->db->prefix()."user as c,";
1587 }
1588 if ($source == 'external') {
1589 $sql .= " ".$this->db->prefix()."socpeople as c,";
1590 }
1591 $sql .= " ".$this->db->prefix()."c_type_contact as tc";
1592 $sql .= " WHERE ec.element_id = ".((int) $id);
1593 $sql .= " AND ec.fk_socpeople = c.rowid";
1594 if ($source == 'internal') {
1595 $sql .= " AND c.entity IN (".getEntity('user').")";
1596 }
1597 if ($source == 'external') {
1598 $sql .= " AND c.entity IN (".getEntity('societe').")";
1599 }
1600 $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1601 $sql .= " AND tc.element = '".$this->db->escape($element)."'";
1602 $sql .= " AND tc.source = '".$this->db->escape($source)."'";
1603 if ($code) {
1604 $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1605 }
1606 $sql .= " AND tc.active = 1";
1607 if ($status) {
1608 $sql .= " AND ec.statut = ".((int) $status);
1609 }
1610
1611 dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1612 $resql = $this->db->query($sql);
1613 if ($resql) {
1614 while ($obj = $this->db->fetch_object($resql)) {
1615 $result[$i] = $obj->fk_socpeople;
1616 $i++;
1617 }
1618 } else {
1619 $this->error = $this->db->error();
1620 return null;
1621 }
1622
1623 return $result;
1624 }
1625
1626 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1633 public function fetch_contact($contactid = null)
1634 {
1635 // phpcs:enable
1636 if (empty($contactid)) {
1637 $contactid = $this->contact_id;
1638 }
1639
1640 if (empty($contactid)) {
1641 return 0;
1642 }
1643
1644 require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1645 $contact = new Contact($this->db);
1646 $result = $contact->fetch($contactid);
1647 $this->contact = $contact;
1648 return $result;
1649 }
1650
1651 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1658 public function fetch_thirdparty($force_thirdparty_id = 0)
1659 {
1660 // phpcs:enable
1661 global $conf;
1662
1663 if (empty($this->socid) && empty($this->fk_soc) && empty($force_thirdparty_id)) {
1664 return 0;
1665 }
1666
1667 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1668
1669 $idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : 0);
1670 if ($force_thirdparty_id) {
1671 $idtofetch = $force_thirdparty_id;
1672 }
1673
1674 if ($idtofetch) {
1675 $thirdparty = new Societe($this->db);
1676 $result = $thirdparty->fetch($idtofetch);
1677 if ($result<0) {
1678 $this->errors=array_merge($this->errors, $thirdparty->errors);
1679 }
1680 $this->thirdparty = $thirdparty;
1681
1682 // Use first price level if level not defined for third party
1683 if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
1684 $this->thirdparty->price_level = 1;
1685 }
1686
1687 return $result;
1688 } else {
1689 return -1;
1690 }
1691 }
1692
1693
1701 public function fetchOneLike($ref)
1702 {
1703 if (!$this->table_ref_field) {
1704 return 0;
1705 }
1706
1707 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element." WHERE ".$this->table_ref_field." LIKE '".$this->db->escape($ref)."' LIMIT 1";
1708
1709 $query = $this->db->query($sql);
1710
1711 if (!$this->db->num_rows($query)) {
1712 return 0;
1713 }
1714
1715 $result = $this->db->fetch_object($query);
1716
1717 return $this->fetch($result->rowid);
1718 }
1719
1720 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1728 public function fetch_barcode()
1729 {
1730 // phpcs:enable
1731 global $conf;
1732
1733 dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1734
1735 $idtype = $this->barcode_type;
1736 if (empty($idtype) && $idtype != '0') { // If type of barcode no set, we try to guess. If set to '0' it means we forced to have type remain not defined
1737 if ($this->element == 'product' && !empty($conf->global->PRODUIT_DEFAULT_BARCODE_TYPE)) {
1738 $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1739 } elseif ($this->element == 'societe') {
1740 $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1741 } else {
1742 dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1743 }
1744 }
1745
1746 if ($idtype > 0) {
1747 if (empty($this->barcode_type) || empty($this->barcode_type_code) || empty($this->barcode_type_label) || empty($this->barcode_type_coder)) { // If data not already loaded
1748 $sql = "SELECT rowid, code, libelle as label, coder";
1749 $sql .= " FROM ".$this->db->prefix()."c_barcode_type";
1750 $sql .= " WHERE rowid = ".((int) $idtype);
1751 dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1752 $resql = $this->db->query($sql);
1753 if ($resql) {
1754 $obj = $this->db->fetch_object($resql);
1755 $this->barcode_type = $obj->rowid;
1756 $this->barcode_type_code = $obj->code;
1757 $this->barcode_type_label = $obj->label;
1758 $this->barcode_type_coder = $obj->coder;
1759 return 1;
1760 } else {
1761 dol_print_error($this->db);
1762 return -1;
1763 }
1764 }
1765 }
1766 return 0;
1767 }
1768
1769 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1775 public function fetch_project()
1776 {
1777 // phpcs:enable
1778 return $this->fetch_projet();
1779 }
1780
1781 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1787 public function fetch_projet()
1788 {
1789 // phpcs:enable
1790 include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1791
1792 if (empty($this->fk_project) && !empty($this->fk_projet)) {
1793 $this->fk_project = $this->fk_projet; // For backward compatibility
1794 }
1795 if (empty($this->fk_project)) {
1796 return 0;
1797 }
1798
1799 $project = new Project($this->db);
1800 $result = $project->fetch($this->fk_project);
1801
1802 $this->projet = $project; // deprecated
1803 $this->project = $project;
1804 return $result;
1805 }
1806
1807 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1813 public function fetch_product()
1814 {
1815 // phpcs:enable
1816 include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1817
1818 if (empty($this->fk_product)) {
1819 return 0;
1820 }
1821
1822 $product = new Product($this->db);
1823 $result = $product->fetch($this->fk_product);
1824
1825 $this->product = $product;
1826 return $result;
1827 }
1828
1829 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1836 public function fetch_user($userid)
1837 {
1838 // phpcs:enable
1839 $user = new User($this->db);
1840 $result = $user->fetch($userid);
1841 $this->user = $user;
1842 return $result;
1843 }
1844
1845 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1851 public function fetch_origin()
1852 {
1853 // phpcs:enable
1854 if ($this->origin == 'shipping') {
1855 $this->origin = 'expedition';
1856 }
1857 if ($this->origin == 'delivery') {
1858 $this->origin = 'livraison';
1859 }
1860 if ($this->origin == 'order_supplier') {
1861 $this->origin = 'commandeFournisseur';
1862 }
1863
1864 $origin = $this->origin;
1865
1866 $classname = ucfirst($origin);
1867 $this->$origin = new $classname($this->db);
1868 $this->$origin->fetch($this->origin_id);
1869 }
1870
1880 public function fetchObjectFrom($table, $field, $key, $element = null)
1881 {
1882 global $conf;
1883
1884 $result = false;
1885
1886 $sql = "SELECT rowid FROM ".$this->db->prefix().$table;
1887 $sql .= " WHERE ".$field." = '".$this->db->escape($key)."'";
1888 if (!empty($element)) {
1889 $sql .= " AND entity IN (".getEntity($element).")";
1890 } else {
1891 $sql .= " AND entity = ".((int) $conf->entity);
1892 }
1893
1894 dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
1895 $resql = $this->db->query($sql);
1896 if ($resql) {
1897 $row = $this->db->fetch_row($resql);
1898 // Test for avoid error -1
1899 if ($row[0] > 0) {
1900 $result = $this->fetch($row[0]);
1901 }
1902 }
1903
1904 return $result;
1905 }
1906
1915 public function getValueFrom($table, $id, $field)
1916 {
1917 $result = false;
1918 if (!empty($id) && !empty($field) && !empty($table)) {
1919 $sql = "SELECT ".$field." FROM ".$this->db->prefix().$table;
1920 $sql .= " WHERE rowid = ".((int) $id);
1921
1922 dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
1923 $resql = $this->db->query($sql);
1924 if ($resql) {
1925 $row = $this->db->fetch_row($resql);
1926 $result = $row[0];
1927 }
1928 }
1929 return $result;
1930 }
1931
1948 public function setValueFrom($field, $value, $table = '', $id = null, $format = '', $id_field = '', $fuser = null, $trigkey = '', $fk_user_field = 'fk_user_modif')
1949 {
1950 global $user;
1951
1952 if (empty($table)) {
1953 $table = $this->table_element;
1954 }
1955 if (empty($id)) {
1956 $id = $this->id;
1957 }
1958 if (empty($format)) {
1959 $format = 'text';
1960 }
1961 if (empty($id_field)) {
1962 $id_field = 'rowid';
1963 }
1964
1965 // Special case
1966 if ($table == 'product' && $field == 'note_private') {
1967 $field = 'note';
1968 }
1969
1970 if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
1971 $fk_user_field = 'fk_user_mod';
1972 }
1973 if (in_array($table, array('prelevement_bons'))) { // TODO Add a field fk_user_modif into llx_prelevement_bons
1974 $fk_user_field = '';
1975 }
1976
1977 if ($trigkey) {
1978 $oldvalue = null;
1979
1980 $sql = "SELECT " . $field;
1981 $sql .= " FROM " . MAIN_DB_PREFIX . $table;
1982 $sql .= " WHERE " . $id_field . " = " . ((int) $id);
1983
1984 $resql = $this->db->query($sql);
1985 if ($resql) {
1986 if ($obj = $this->db->fetch_object($resql)) {
1987 if ($format == 'date') {
1988 $oldvalue = $this->db->jdate($obj->$field);
1989 } else {
1990 $oldvalue = $obj->$field;
1991 }
1992 }
1993 } else {
1994 $this->error = $this->db->lasterror();
1995 return -1;
1996 }
1997 }
1998
1999 $error = 0;
2000
2001 dol_syslog(__METHOD__, LOG_DEBUG);
2002
2003 $this->db->begin();
2004
2005 $sql = "UPDATE ".$this->db->prefix().$table." SET ";
2006
2007 if ($format == 'text') {
2008 $sql .= $field." = '".$this->db->escape($value)."'";
2009 } elseif ($format == 'int') {
2010 $sql .= $field." = ".((int) $value);
2011 } elseif ($format == 'date') {
2012 $sql .= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
2013 } elseif ($format == 'dategmt') {
2014 $sql .= $field." = ".($value ? "'".$this->db->idate($value, 'gmt')."'" : "null");
2015 }
2016
2017 if ($fk_user_field) {
2018 if (!empty($fuser) && is_object($fuser)) {
2019 $sql .= ", ".$fk_user_field." = ".((int) $fuser->id);
2020 } elseif (empty($fuser) || $fuser != 'none') {
2021 $sql .= ", ".$fk_user_field." = ".((int) $user->id);
2022 }
2023 }
2024
2025 $sql .= " WHERE ".$id_field." = ".((int) $id);
2026
2027 $resql = $this->db->query($sql);
2028 if ($resql) {
2029 if ($trigkey) {
2030 // call trigger with updated object values
2031 if (method_exists($this, 'fetch')) {
2032 $result = $this->fetch($id);
2033 } else {
2034 $result = $this->fetchCommon($id);
2035 }
2036 $this->oldcopy = clone $this;
2037 if (property_exists($this->oldcopy, $field)) {
2038 $this->oldcopy->$field = $oldvalue;
2039 }
2040
2041 if ($result >= 0) {
2042 $result = $this->call_trigger($trigkey, (!empty($fuser) && is_object($fuser)) ? $fuser : $user); // This may set this->errors
2043 }
2044 if ($result < 0) {
2045 $error++;
2046 }
2047 }
2048
2049 if (!$error) {
2050 if (property_exists($this, $field)) {
2051 $this->$field = $value;
2052 }
2053 $this->db->commit();
2054 return 1;
2055 } else {
2056 $this->db->rollback();
2057 return -2;
2058 }
2059 } else {
2060 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2061 $this->error = 'DB_ERROR_RECORD_ALREADY_EXISTS';
2062 } else {
2063 $this->error = $this->db->lasterror();
2064 }
2065 $this->db->rollback();
2066 return -1;
2067 }
2068 }
2069
2070 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2080 public function load_previous_next_ref($filter, $fieldid, $nodbprefix = 0)
2081 {
2082 // phpcs:enable
2083 global $conf, $user;
2084
2085 if (!$this->table_element) {
2086 dol_print_error('', get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
2087 return -1;
2088 }
2089 if ($fieldid == 'none') {
2090 return 1;
2091 }
2092
2093 // For backward compatibility
2094 if (in_array($this->table_element, array('facture_rec', 'facture_fourn_rec')) && $fieldid == 'title') {
2095 $fieldid = 'titre';
2096 }
2097
2098 // Security on socid
2099 $socid = 0;
2100 if ($user->socid > 0) {
2101 $socid = $user->socid;
2102 }
2103
2104 // this->ismultientitymanaged contains
2105 // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
2106 $aliastablesociete = 's';
2107 if ($this->element == 'societe') {
2108 $aliastablesociete = 'te'; // te as table_element
2109 }
2110 $restrictiononfksoc = empty($this->restrictiononfksoc) ? 0 : $this->restrictiononfksoc;
2111 $sql = "SELECT MAX(te.".$fieldid.")";
2112 $sql .= " FROM ".(empty($nodbprefix) ?$this->db->prefix():'').$this->table_element." as te";
2113 if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2114 if (empty($user->admin) || !empty($user->entity) || $conf->entity != 1) {
2115 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."usergroup_user as ug ON ug.fk_user = te.rowid";
2116 }
2117 }
2118 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2119 $tmparray = explode('@', $this->ismultientitymanaged);
2120 $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
2121 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2122 $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
2123 } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2124 $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
2125 }
2126 if ($restrictiononfksoc && empty($user->rights->societe->client->voir) && !$socid) {
2127 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
2128 }
2129 $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)
2130 if ($restrictiononfksoc == 1 && empty($user->rights->societe->client->voir) && !$socid) {
2131 $sql .= " AND sc.fk_user = ".((int) $user->id);
2132 }
2133 if ($restrictiononfksoc == 2 && empty($user->rights->societe->client->voir) && !$socid) {
2134 $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
2135 }
2136 if (!empty($filter)) {
2137 if (!preg_match('/^\s*AND/i', $filter)) {
2138 $sql .= " AND ";
2139 }
2140 $sql .= $filter;
2141 }
2142 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2143 $tmparray = explode('@', $this->ismultientitymanaged);
2144 $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
2145 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2146 $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
2147 }
2148 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2149 if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2150 if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
2151 $sql .= " AND te.entity IS NOT NULL"; // Show all users
2152 } else {
2153 $sql .= " AND ug.entity IN (".getEntity('usergroup').")";
2154 }
2155 } else {
2156 $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
2157 }
2158 }
2159 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
2160 $tmparray = explode('@', $this->ismultientitymanaged);
2161 $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
2162 }
2163 if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
2164 $sql .= ' AND te.fk_soc = '.((int) $socid);
2165 }
2166 if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
2167 $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
2168 }
2169 if ($restrictiononfksoc && $socid && $this->element == 'societe') {
2170 $sql .= ' AND te.rowid = '.((int) $socid);
2171 }
2172 //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
2173
2174 $result = $this->db->query($sql);
2175 if (!$result) {
2176 $this->error = $this->db->lasterror();
2177 return -1;
2178 }
2179 $row = $this->db->fetch_row($result);
2180 $this->ref_previous = $row[0];
2181
2182 $sql = "SELECT MIN(te.".$fieldid.")";
2183 $sql .= " FROM ".(empty($nodbprefix) ?$this->db->prefix():'').$this->table_element." as te";
2184 if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2185 if (empty($user->admin) || !empty($user->entity) || $conf->entity != 1) {
2186 $sql .= " INNER JOIN ".MAIN_DB_PREFIX."usergroup_user as ug ON ug.fk_user = te.rowid";
2187 }
2188 }
2189 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2190 $tmparray = explode('@', $this->ismultientitymanaged);
2191 $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
2192 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2193 $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
2194 } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2195 $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
2196 }
2197 if ($restrictiononfksoc && empty($user->rights->societe->client->voir) && !$socid) {
2198 $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
2199 }
2200 $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)
2201 if ($restrictiononfksoc == 1 && empty($user->rights->societe->client->voir) && !$socid) {
2202 $sql .= " AND sc.fk_user = ".((int) $user->id);
2203 }
2204 if ($restrictiononfksoc == 2 && empty($user->rights->societe->client->voir) && !$socid) {
2205 $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
2206 }
2207 if (!empty($filter)) {
2208 if (!preg_match('/^\s*AND/i', $filter)) {
2209 $sql .= " AND "; // For backward compatibility
2210 }
2211 $sql .= $filter;
2212 }
2213 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2214 $tmparray = explode('@', $this->ismultientitymanaged);
2215 $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
2216 } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2217 $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
2218 }
2219 if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2220 if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2221 if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
2222 $sql .= " AND te.entity IS NOT NULL"; // Show all users
2223 } else {
2224 $sql .= " AND ug.entity IN (".getEntity('usergroup').")";
2225 }
2226 } else {
2227 $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
2228 }
2229 }
2230 if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
2231 $tmparray = explode('@', $this->ismultientitymanaged);
2232 $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
2233 }
2234 if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
2235 $sql .= ' AND te.fk_soc = '.((int) $socid);
2236 }
2237 if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
2238 $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
2239 }
2240 if ($restrictiononfksoc && $socid && $this->element == 'societe') {
2241 $sql .= ' AND te.rowid = '.((int) $socid);
2242 }
2243 //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
2244 // 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
2245
2246 $result = $this->db->query($sql);
2247 if (!$result) {
2248 $this->error = $this->db->lasterror();
2249 return -2;
2250 }
2251 $row = $this->db->fetch_row($result);
2252 $this->ref_next = $row[0];
2253
2254 return 1;
2255 }
2256
2257
2265 public function getListContactId($source = 'external')
2266 {
2267 $contactAlreadySelected = array();
2268 $tab = $this->liste_contact(-1, $source);
2269 $num = count($tab);
2270 $i = 0;
2271 while ($i < $num) {
2272 if ($source == 'thirdparty') {
2273 $contactAlreadySelected[$i] = $tab[$i]['socid'];
2274 } else {
2275 $contactAlreadySelected[$i] = $tab[$i]['id'];
2276 }
2277 $i++;
2278 }
2279 return $contactAlreadySelected;
2280 }
2281
2282
2290 public function setProject($projectid, $notrigger = 0)
2291 {
2292 global $user;
2293 $error = 0;
2294
2295 if (!$this->table_element) {
2296 dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined", LOG_ERR);
2297 return -1;
2298 }
2299
2300 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2301 if (!empty($this->fields['fk_project'])) { // Common case
2302 if ($projectid) {
2303 $sql .= " SET fk_project = ".((int) $projectid);
2304 } else {
2305 $sql .= " SET fk_project = NULL";
2306 }
2307 $sql .= ' WHERE rowid = '.((int) $this->id);
2308 } elseif ($this->table_element == 'actioncomm') { // Special case for actioncomm
2309 if ($projectid) {
2310 $sql .= " SET fk_project = ".((int) $projectid);
2311 } else {
2312 $sql .= " SET fk_project = NULL";
2313 }
2314 $sql .= ' WHERE id = '.((int) $this->id);
2315 } else // Special case for old architecture objects
2316 {
2317 if ($projectid) {
2318 $sql .= ' SET fk_projet = '.((int) $projectid);
2319 } else {
2320 $sql .= ' SET fk_projet = NULL';
2321 }
2322 $sql .= " WHERE rowid = ".((int) $this->id);
2323 }
2324
2325 $this->db->begin();
2326
2327 dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
2328 if ($this->db->query($sql)) {
2329 $this->fk_project = ((int) $projectid);
2330 } else {
2331 dol_print_error($this->db);
2332 $error++;
2333 }
2334
2335 // Triggers
2336 if (!$error && !$notrigger) {
2337 // Call triggers
2338 $result = $this->call_trigger(strtoupper($this->element) . '_MODIFY', $user);
2339 if ($result < 0) {
2340 $error++;
2341 } //Do also here what you must do to rollback action if trigger fail
2342 // End call triggers
2343 }
2344
2345 // Commit or rollback
2346 if ($error) {
2347 $this->db->rollback();
2348 return -1;
2349 } else {
2350 $this->db->commit();
2351 return 1;
2352 }
2353 }
2354
2361 public function setPaymentMethods($id)
2362 {
2363 global $user;
2364
2365 $error = 0; $notrigger = 0;
2366
2367 dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
2368
2369 if ($this->statut >= 0 || $this->element == 'societe') {
2370 // TODO uniformize field name
2371 $fieldname = 'fk_mode_reglement';
2372 if ($this->element == 'societe') {
2373 $fieldname = 'mode_reglement';
2374 }
2375 if (get_class($this) == 'Fournisseur') {
2376 $fieldname = 'mode_reglement_supplier';
2377 }
2378 if (get_class($this) == 'Tva') {
2379 $fieldname = 'fk_typepayment';
2380 }
2381 if (get_class($this) == 'Salary') {
2382 $fieldname = 'fk_typepayment';
2383 }
2384
2385 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2386 $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2387 $sql .= ' WHERE rowid='.((int) $this->id);
2388
2389 if ($this->db->query($sql)) {
2390 $this->mode_reglement_id = $id;
2391 // for supplier
2392 if (get_class($this) == 'Fournisseur') {
2393 $this->mode_reglement_supplier_id = $id;
2394 }
2395 // Triggers
2396 if (!$error && !$notrigger) {
2397 // Call triggers
2398 if (get_class($this) == 'Commande') {
2399 $result = $this->call_trigger('ORDER_MODIFY', $user);
2400 } else {
2401 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
2402 }
2403 if ($result < 0) {
2404 $error++;
2405 }
2406 // End call triggers
2407 }
2408 return 1;
2409 } else {
2410 dol_syslog(get_class($this).'::setPaymentMethods Error '.$this->db->error());
2411 $this->error = $this->db->error();
2412 return -1;
2413 }
2414 } else {
2415 dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
2416 $this->error = 'Status of the object is incompatible '.$this->statut;
2417 return -2;
2418 }
2419 }
2420
2427 public function setMulticurrencyCode($code)
2428 {
2429 dol_syslog(get_class($this).'::setMulticurrencyCode('.$code.')');
2430 if ($this->statut >= 0 || $this->element == 'societe') {
2431 $fieldname = 'multicurrency_code';
2432
2433 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2434 $sql .= " SET ".$fieldname." = '".$this->db->escape($code)."'";
2435 $sql .= ' WHERE rowid='.((int) $this->id);
2436
2437 if ($this->db->query($sql)) {
2438 $this->multicurrency_code = $code;
2439
2440 list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
2441 if ($rate) {
2442 $this->setMulticurrencyRate($rate, 2);
2443 }
2444
2445 return 1;
2446 } else {
2447 dol_syslog(get_class($this).'::setMulticurrencyCode Error '.$sql.' - '.$this->db->error());
2448 $this->error = $this->db->error();
2449 return -1;
2450 }
2451 } else {
2452 dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
2453 $this->error = 'Status of the object is incompatible '.$this->statut;
2454 return -2;
2455 }
2456 }
2457
2465 public function setMulticurrencyRate($rate, $mode = 1)
2466 {
2467 dol_syslog(get_class($this).'::setMulticurrencyRate('.$rate.','.$mode.')');
2468 if ($this->statut >= 0 || $this->element == 'societe') {
2469 $fieldname = 'multicurrency_tx';
2470
2471 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2472 $sql .= " SET ".$fieldname." = ".((float) $rate);
2473 $sql .= ' WHERE rowid='.((int) $this->id);
2474
2475 if ($this->db->query($sql)) {
2476 $this->multicurrency_tx = $rate;
2477
2478 // Update line price
2479 if (!empty($this->lines)) {
2480 foreach ($this->lines as &$line) {
2481 // Amounts in company currency will be recalculated
2482 if ($mode == 1) {
2483 $line->subprice = 0;
2484 }
2485
2486 // Amounts in foreign currency will be recalculated
2487 if ($mode == 2) {
2488 $line->multicurrency_subprice = 0;
2489 }
2490
2491 switch ($this->element) {
2492 case 'propal':
2495 $this->updateline(
2496 $line->id,
2497 $line->subprice,
2498 $line->qty,
2499 $line->remise_percent,
2500 $line->tva_tx,
2501 $line->localtax1_tx,
2502 $line->localtax2_tx,
2503 ($line->description ? $line->description : $line->desc),
2504 'HT',
2505 $line->info_bits,
2506 $line->special_code,
2507 $line->fk_parent_line,
2508 $line->skip_update_total,
2509 $line->fk_fournprice,
2510 $line->pa_ht,
2511 $line->label,
2512 $line->product_type,
2513 $line->date_start,
2514 $line->date_end,
2515 $line->array_options,
2516 $line->fk_unit,
2517 $line->multicurrency_subprice
2518 );
2519 break;
2520 case 'commande':
2523 $this->updateline(
2524 $line->id,
2525 ($line->description ? $line->description : $line->desc),
2526 $line->subprice,
2527 $line->qty,
2528 $line->remise_percent,
2529 $line->tva_tx,
2530 $line->localtax1_tx,
2531 $line->localtax2_tx,
2532 'HT',
2533 $line->info_bits,
2534 $line->date_start,
2535 $line->date_end,
2536 $line->product_type,
2537 $line->fk_parent_line,
2538 $line->skip_update_total,
2539 $line->fk_fournprice,
2540 $line->pa_ht,
2541 $line->label,
2542 $line->special_code,
2543 $line->array_options,
2544 $line->fk_unit,
2545 $line->multicurrency_subprice
2546 );
2547 break;
2548 case 'facture':
2551 $this->updateline(
2552 $line->id,
2553 ($line->description ? $line->description : $line->desc),
2554 $line->subprice,
2555 $line->qty,
2556 $line->remise_percent,
2557 $line->date_start,
2558 $line->date_end,
2559 $line->tva_tx,
2560 $line->localtax1_tx,
2561 $line->localtax2_tx,
2562 'HT',
2563 $line->info_bits,
2564 $line->product_type,
2565 $line->fk_parent_line,
2566 $line->skip_update_total,
2567 $line->fk_fournprice,
2568 $line->pa_ht,
2569 $line->label,
2570 $line->special_code,
2571 $line->array_options,
2572 $line->situation_percent,
2573 $line->fk_unit,
2574 $line->multicurrency_subprice
2575 );
2576 break;
2577 case 'supplier_proposal':
2580 $this->updateline(
2581 $line->id,
2582 $line->subprice,
2583 $line->qty,
2584 $line->remise_percent,
2585 $line->tva_tx,
2586 $line->localtax1_tx,
2587 $line->localtax2_tx,
2588 ($line->description ? $line->description : $line->desc),
2589 'HT',
2590 $line->info_bits,
2591 $line->special_code,
2592 $line->fk_parent_line,
2593 $line->skip_update_total,
2594 $line->fk_fournprice,
2595 $line->pa_ht,
2596 $line->label,
2597 $line->product_type,
2598 $line->array_options,
2599 $line->ref_fourn,
2600 $line->multicurrency_subprice
2601 );
2602 break;
2603 case 'order_supplier':
2606 $this->updateline(
2607 $line->id,
2608 ($line->description ? $line->description : $line->desc),
2609 $line->subprice,
2610 $line->qty,
2611 $line->remise_percent,
2612 $line->tva_tx,
2613 $line->localtax1_tx,
2614 $line->localtax2_tx,
2615 'HT',
2616 $line->info_bits,
2617 $line->product_type,
2618 false,
2619 $line->date_start,
2620 $line->date_end,
2621 $line->array_options,
2622 $line->fk_unit,
2623 $line->multicurrency_subprice,
2624 $line->ref_supplier
2625 );
2626 break;
2627 case 'invoice_supplier':
2630 $this->updateline(
2631 $line->id,
2632 ($line->description ? $line->description : $line->desc),
2633 $line->subprice,
2634 $line->tva_tx,
2635 $line->localtax1_tx,
2636 $line->localtax2_tx,
2637 $line->qty,
2638 0,
2639 'HT',
2640 $line->info_bits,
2641 $line->product_type,
2642 $line->remise_percent,
2643 false,
2644 $line->date_start,
2645 $line->date_end,
2646 $line->array_options,
2647 $line->fk_unit,
2648 $line->multicurrency_subprice,
2649 $line->ref_supplier
2650 );
2651 break;
2652 default:
2653 dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
2654 break;
2655 }
2656 }
2657 }
2658
2659 return 1;
2660 } else {
2661 dol_syslog(get_class($this).'::setMulticurrencyRate Error '.$sql.' - '.$this->db->error());
2662 $this->error = $this->db->error();
2663 return -1;
2664 }
2665 } else {
2666 dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
2667 $this->error = 'Status of the object is incompatible '.$this->statut;
2668 return -2;
2669 }
2670 }
2671
2679 public function setPaymentTerms($id, $deposit_percent = null)
2680 {
2681 dol_syslog(get_class($this).'::setPaymentTerms('.$id.', '.var_export($deposit_percent, true).')');
2682 if ($this->statut >= 0 || $this->element == 'societe') {
2683 // TODO uniformize field name
2684 $fieldname = 'fk_cond_reglement';
2685 if ($this->element == 'societe') {
2686 $fieldname = 'cond_reglement';
2687 }
2688 if (get_class($this) == 'Fournisseur') {
2689 $fieldname = 'cond_reglement_supplier';
2690 }
2691
2692 if (empty($deposit_percent) || $deposit_percent < 0) {
2693 $deposit_percent = getDictionaryValue('c_payment_term', 'deposit_percent', $id);
2694 }
2695
2696 if ($deposit_percent > 100) {
2697 $deposit_percent = 100;
2698 }
2699
2700 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2701 $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2702 if (in_array($this->table_element, array('propal', 'commande', 'societe'))) {
2703 $sql .= " , deposit_percent = " . (empty($deposit_percent) ? 'NULL' : "'".$this->db->escape($deposit_percent)."'");
2704 }
2705 $sql .= ' WHERE rowid='.((int) $this->id);
2706
2707 if ($this->db->query($sql)) {
2708 $this->cond_reglement_id = $id;
2709 // for supplier
2710 if (get_class($this) == 'Fournisseur') {
2711 $this->cond_reglement_supplier_id = $id;
2712 }
2713 $this->cond_reglement = $id; // for compatibility
2714 $this->deposit_percent = $deposit_percent;
2715 return 1;
2716 } else {
2717 dol_syslog(get_class($this).'::setPaymentTerms Error '.$sql.' - '.$this->db->error());
2718 $this->error = $this->db->error();
2719 return -1;
2720 }
2721 } else {
2722 dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
2723 $this->error = 'Status of the object is incompatible '.$this->statut;
2724 return -2;
2725 }
2726 }
2727
2734 public function setTransportMode($id)
2735 {
2736 dol_syslog(get_class($this).'::setTransportMode('.$id.')');
2737 if ($this->statut >= 0 || $this->element == 'societe') {
2738 $fieldname = 'fk_transport_mode';
2739 if ($this->element == 'societe') {
2740 $fieldname = 'transport_mode';
2741 }
2742 if (get_class($this) == 'Fournisseur') {
2743 $fieldname = 'transport_mode_supplier';
2744 }
2745
2746 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2747 $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2748 $sql .= ' WHERE rowid='.((int) $this->id);
2749
2750 if ($this->db->query($sql)) {
2751 $this->transport_mode_id = $id;
2752 // for supplier
2753 if (get_class($this) == 'Fournisseur') {
2754 $this->transport_mode_supplier_id = $id;
2755 }
2756 return 1;
2757 } else {
2758 dol_syslog(get_class($this).'::setTransportMode Error '.$sql.' - '.$this->db->error());
2759 $this->error = $this->db->error();
2760 return -1;
2761 }
2762 } else {
2763 dol_syslog(get_class($this).'::setTransportMode, status of the object is incompatible');
2764 $this->error = 'Status of the object is incompatible '.$this->statut;
2765 return -2;
2766 }
2767 }
2768
2776 {
2777 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms('.$id.')');
2778 if ($this->statut >= 0 || $this->element == 'societe') {
2779 $fieldname = 'retained_warranty_fk_cond_reglement';
2780
2781 $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2782 $sql .= " SET ".$fieldname." = ".((int) $id);
2783 $sql .= ' WHERE rowid='.((int) $this->id);
2784
2785 if ($this->db->query($sql)) {
2786 $this->retained_warranty_fk_cond_reglement = $id;
2787 return 1;
2788 } else {
2789 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms Error '.$sql.' - '.$this->db->error());
2790 $this->error = $this->db->error();
2791 return -1;
2792 }
2793 } else {
2794 dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms, status of the object is incompatible');
2795 $this->error = 'Status of the object is incompatible '.$this->statut;
2796 return -2;
2797 }
2798 }
2799
2807 public function setDeliveryAddress($id)
2808 {
2809 $fieldname = 'fk_delivery_address';
2810 if ($this->element == 'delivery' || $this->element == 'shipping') {
2811 $fieldname = 'fk_address';
2812 }
2813
2814 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ".$fieldname." = ".((int) $id);
2815 $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
2816
2817 if ($this->db->query($sql)) {
2818 $this->fk_delivery_address = $id;
2819 return 1;
2820 } else {
2821 $this->error = $this->db->error();
2822 dol_syslog(get_class($this).'::setDeliveryAddress Error '.$this->error);
2823 return -1;
2824 }
2825 }
2826
2827
2837 public function setShippingMethod($shipping_method_id, $notrigger = false, $userused = null)
2838 {
2839 global $user;
2840
2841 if (empty($userused)) {
2842 $userused = $user;
2843 }
2844
2845 $error = 0;
2846
2847 if (!$this->table_element) {
2848 dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined", LOG_ERR);
2849 return -1;
2850 }
2851
2852 $this->db->begin();
2853
2854 if ($shipping_method_id < 0) {
2855 $shipping_method_id = 'NULL';
2856 }
2857 dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
2858
2859 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2860 $sql .= " SET fk_shipping_method = ".((int) $shipping_method_id);
2861 $sql .= " WHERE rowid=".((int) $this->id);
2862 $resql = $this->db->query($sql);
2863 if (!$resql) {
2864 dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
2865 $this->error = $this->db->lasterror();
2866 $error++;
2867 } else {
2868 if (!$notrigger) {
2869 // Call trigger
2870 $this->context = array('shippingmethodupdate'=>1);
2871 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused);
2872 if ($result < 0) {
2873 $error++;
2874 }
2875 // End call trigger
2876 }
2877 }
2878 if ($error) {
2879 $this->db->rollback();
2880 return -1;
2881 } else {
2882 $this->shipping_method_id = ($shipping_method_id == 'NULL') ?null:$shipping_method_id;
2883 $this->db->commit();
2884 return 1;
2885 }
2886 }
2887
2888
2895 public function setWarehouse($warehouse_id)
2896 {
2897 if (!$this->table_element) {
2898 dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined", LOG_ERR);
2899 return -1;
2900 }
2901 if ($warehouse_id < 0) {
2902 $warehouse_id = 'NULL';
2903 }
2904 dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
2905
2906 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2907 $sql .= " SET fk_warehouse = ".((int) $warehouse_id);
2908 $sql .= " WHERE rowid=".((int) $this->id);
2909
2910 if ($this->db->query($sql)) {
2911 $this->warehouse_id = ($warehouse_id == 'NULL') ?null:$warehouse_id;
2912 return 1;
2913 } else {
2914 dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
2915 $this->error = $this->db->error();
2916 return 0;
2917 }
2918 }
2919
2920
2928 public function setDocModel($user, $modelpdf)
2929 {
2930 if (!$this->table_element) {
2931 dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined", LOG_ERR);
2932 return -1;
2933 }
2934
2935 $newmodelpdf = dol_trunc($modelpdf, 255);
2936
2937 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2938 $sql .= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
2939 $sql .= " WHERE rowid = ".((int) $this->id);
2940
2941 dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
2942 $resql = $this->db->query($sql);
2943 if ($resql) {
2944 $this->model_pdf = $modelpdf;
2945 $this->modelpdf = $modelpdf; // For bakward compatibility
2946 return 1;
2947 } else {
2948 dol_print_error($this->db);
2949 return 0;
2950 }
2951 }
2952
2953
2962 public function setBankAccount($fk_account, $notrigger = false, $userused = null)
2963 {
2964 global $user;
2965
2966 if (empty($userused)) {
2967 $userused = $user;
2968 }
2969
2970 $error = 0;
2971
2972 if (!$this->table_element) {
2973 dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined", LOG_ERR);
2974 return -1;
2975 }
2976 $this->db->begin();
2977
2978 if ($fk_account < 0) {
2979 $fk_account = 'NULL';
2980 }
2981 dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
2982
2983 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2984 $sql .= " SET fk_account = ".((int) $fk_account);
2985 $sql .= " WHERE rowid=".((int) $this->id);
2986
2987 $resql = $this->db->query($sql);
2988 if (!$resql) {
2989 dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
2990 $this->error = $this->db->lasterror();
2991 $error++;
2992 } else {
2993 if (!$notrigger) {
2994 // Call trigger
2995 $this->context = array('bankaccountupdate'=>1);
2996 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused);
2997 if ($result < 0) {
2998 $error++;
2999 }
3000 // End call trigger
3001 }
3002 }
3003 if ($error) {
3004 $this->db->rollback();
3005 return -1;
3006 } else {
3007 $this->fk_account = ($fk_account == 'NULL') ?null:$fk_account;
3008 $this->db->commit();
3009 return 1;
3010 }
3011 }
3012
3013
3014 // TODO: Move line related operations to CommonObjectLine?
3015
3016 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3026 public function line_order($renum = false, $rowidorder = 'ASC', $fk_parent_line = true)
3027 {
3028 // phpcs:enable
3029 if (!$this->table_element_line) {
3030 dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined", LOG_ERR);
3031 return -1;
3032 }
3033 if (!$this->fk_element) {
3034 dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined", LOG_ERR);
3035 return -1;
3036 }
3037
3038 $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3039 if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3040 $fieldposition = 'position';
3041 }
3042
3043 // Count number of lines to reorder (according to choice $renum)
3044 $nl = 0;
3045 $sql = "SELECT count(rowid) FROM ".$this->db->prefix().$this->table_element_line;
3046 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3047 if (!$renum) {
3048 $sql .= " AND " . $fieldposition . " = 0";
3049 }
3050 if ($renum) {
3051 $sql .= " AND " . $fieldposition . " <> 0";
3052 }
3053
3054 dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
3055 $resql = $this->db->query($sql);
3056 if ($resql) {
3057 $row = $this->db->fetch_row($resql);
3058 $nl = $row[0];
3059 } else {
3060 dol_print_error($this->db);
3061 }
3062 if ($nl > 0) {
3063 // The goal of this part is to reorder all lines, with all children lines sharing the same counter that parents.
3064 $rows = array();
3065
3066 // We first search all lines that are parent lines (for multilevel details lines)
3067 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3068 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3069 if ($fk_parent_line) {
3070 $sql .= ' AND fk_parent_line IS NULL';
3071 }
3072 $sql .= " ORDER BY " . $fieldposition . " ASC, rowid " . $rowidorder;
3073
3074 dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
3075 $resql = $this->db->query($sql);
3076 if ($resql) {
3077 $i = 0;
3078 $num = $this->db->num_rows($resql);
3079 while ($i < $num) {
3080 $row = $this->db->fetch_row($resql);
3081 $rows[] = $row[0]; // Add parent line into array rows
3082 $childrens = $this->getChildrenOfLine($row[0]);
3083 if (!empty($childrens)) {
3084 foreach ($childrens as $child) {
3085 array_push($rows, $child);
3086 }
3087 }
3088 $i++;
3089 }
3090
3091 // Now we set a new number for each lines (parent and children with children included into parent tree)
3092 if (!empty($rows)) {
3093 foreach ($rows as $key => $row) {
3094 $this->updateRangOfLine($row, ($key + 1));
3095 }
3096 }
3097 } else {
3098 dol_print_error($this->db);
3099 }
3100 }
3101 return 1;
3102 }
3103
3111 public function getChildrenOfLine($id, $includealltree = 0)
3112 {
3113 $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3114 if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3115 $fieldposition = 'position';
3116 }
3117
3118 $rows = array();
3119
3120 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3121 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3122 $sql .= ' AND fk_parent_line = '.((int) $id);
3123 $sql .= " ORDER BY " . $fieldposition . " ASC";
3124
3125 dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id, LOG_DEBUG);
3126 $resql = $this->db->query($sql);
3127 if ($resql) {
3128 if ($this->db->num_rows($resql) > 0) {
3129 while ($row = $this->db->fetch_row($resql)) {
3130 $rows[] = $row[0];
3131 if (!empty($includealltree)) {
3132 $rows = array_merge($rows, $this->getChildrenOfLine($row[0], $includealltree));
3133 }
3134 }
3135 }
3136 }
3137 return $rows;
3138 }
3139
3140 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3148 public function line_up($rowid, $fk_parent_line = true)
3149 {
3150 // phpcs:enable
3151 $this->line_order(false, 'ASC', $fk_parent_line);
3152
3153 // Get rang of line
3154 $rang = $this->getRangOfLine($rowid);
3155
3156 // Update position of line
3157 $this->updateLineUp($rowid, $rang);
3158 }
3159
3160 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3168 public function line_down($rowid, $fk_parent_line = true)
3169 {
3170 // phpcs:enable
3171 $this->line_order(false, 'ASC', $fk_parent_line);
3172
3173 // Get rang of line
3174 $rang = $this->getRangOfLine($rowid);
3175
3176 // Get max value for rang
3177 $max = $this->line_max();
3178
3179 // Update position of line
3180 $this->updateLineDown($rowid, $rang, $max);
3181 }
3182
3190 public function updateRangOfLine($rowid, $rang)
3191 {
3192 global $hookmanager;
3193 $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3194 if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3195 $fieldposition = 'position';
3196 }
3197
3198 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3199 $sql .= ' WHERE rowid = '.((int) $rowid);
3200
3201 dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
3202 if (!$this->db->query($sql)) {
3203 dol_print_error($this->db);
3204 return -1;
3205 } else {
3206 $parameters=array('rowid'=>$rowid, 'rang'=>$rang, 'fieldposition' => $fieldposition);
3207 $action='';
3208 $reshook = $hookmanager->executeHooks('afterRankOfLineUpdate', $parameters, $this, $action);
3209 return 1;
3210 }
3211 }
3212
3213 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3220 public function line_ajaxorder($rows)
3221 {
3222 // phpcs:enable
3223 $num = count($rows);
3224 for ($i = 0; $i < $num; $i++) {
3225 $this->updateRangOfLine($rows[$i], ($i + 1));
3226 }
3227 }
3228
3236 public function updateLineUp($rowid, $rang)
3237 {
3238 if ($rang > 1) {
3239 $fieldposition = 'rang';
3240 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3241 $fieldposition = 'position';
3242 }
3243
3244 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3245 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3246 $sql .= " AND " . $fieldposition . " = " . ((int) ($rang - 1));
3247 if ($this->db->query($sql)) {
3248 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang - 1));
3249 $sql .= ' WHERE rowid = '.((int) $rowid);
3250 if (!$this->db->query($sql)) {
3251 dol_print_error($this->db);
3252 }
3253 } else {
3254 dol_print_error($this->db);
3255 }
3256 }
3257 }
3258
3267 public function updateLineDown($rowid, $rang, $max)
3268 {
3269 if ($rang < $max) {
3270 $fieldposition = 'rang';
3271 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3272 $fieldposition = 'position';
3273 }
3274
3275 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3276 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3277 $sql .= " AND " . $fieldposition . " = " . ((int) ($rang + 1));
3278 if ($this->db->query($sql)) {
3279 $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang + 1));
3280 $sql .= ' WHERE rowid = '.((int) $rowid);
3281 if (!$this->db->query($sql)) {
3282 dol_print_error($this->db);
3283 }
3284 } else {
3285 dol_print_error($this->db);
3286 }
3287 }
3288 }
3289
3296 public function getRangOfLine($rowid)
3297 {
3298 $fieldposition = 'rang';
3299 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3300 $fieldposition = 'position';
3301 }
3302
3303 $sql = "SELECT " . $fieldposition . " FROM ".$this->db->prefix().$this->table_element_line;
3304 $sql .= " WHERE rowid = ".((int) $rowid);
3305
3306 dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
3307 $resql = $this->db->query($sql);
3308 if ($resql) {
3309 $row = $this->db->fetch_row($resql);
3310 return $row[0];
3311 }
3312
3313 return 0;
3314 }
3315
3322 public function getIdOfLine($rang)
3323 {
3324 $fieldposition = 'rang';
3325 if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3326 $fieldposition = 'position';
3327 }
3328
3329 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3330 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3331 $sql .= " AND " . $fieldposition . " = ".((int) $rang);
3332 $resql = $this->db->query($sql);
3333 if ($resql) {
3334 $row = $this->db->fetch_row($resql);
3335 return $row[0];
3336 }
3337
3338 return 0;
3339 }
3340
3341 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3348 public function line_max($fk_parent_line = 0)
3349 {
3350 // phpcs:enable
3351 $positionfield = 'rang';
3352 if (in_array($this->table_element, array('bom_bom', 'product_attribute'))) {
3353 $positionfield = 'position';
3354 }
3355
3356 // Search the last rang with fk_parent_line
3357 if ($fk_parent_line) {
3358 $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
3359 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3360 $sql .= " AND fk_parent_line = ".((int) $fk_parent_line);
3361
3362 dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
3363 $resql = $this->db->query($sql);
3364 if ($resql) {
3365 $row = $this->db->fetch_row($resql);
3366 if (!empty($row[0])) {
3367 return $row[0];
3368 } else {
3369 return $this->getRangOfLine($fk_parent_line);
3370 }
3371 }
3372 } else {
3373 // If not, search the last rang of element
3374 $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
3375 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3376
3377 dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
3378 $resql = $this->db->query($sql);
3379 if ($resql) {
3380 $row = $this->db->fetch_row($resql);
3381 return $row[0];
3382 }
3383 }
3384
3385 return 0;
3386 }
3387
3388 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3395 public function update_ref_ext($ref_ext)
3396 {
3397 // phpcs:enable
3398 if (!$this->table_element) {
3399 dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
3400 return -1;
3401 }
3402
3403 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3404 $sql .= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
3405 $sql .= " WHERE ".(isset($this->table_rowid) ? $this->table_rowid : 'rowid')." = ".((int) $this->id);
3406
3407 dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
3408 if ($this->db->query($sql)) {
3409 $this->ref_ext = $ref_ext;
3410 return 1;
3411 } else {
3412 $this->error = $this->db->error();
3413 return -1;
3414 }
3415 }
3416
3417 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3426 public function update_note($note, $suffix = '', $notrigger = 0)
3427 {
3428 // phpcs:enable
3429 global $user;
3430
3431 if (!$this->table_element) {
3432 $this->error = 'update_note was called on objet with property table_element not defined';
3433 dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
3434 return -1;
3435 }
3436 if (!in_array($suffix, array('', '_public', '_private'))) {
3437 $this->error = 'update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
3438 dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
3439 return -2;
3440 }
3441
3442 $newsuffix = $suffix;
3443
3444 // Special cas
3445 if ($this->table_element == 'product' && $newsuffix == '_private') {
3446 $newsuffix = '';
3447 }
3448 if (in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
3449 $fieldusermod = "fk_user_mod";
3450 } elseif ($this->table_element == 'ecm_files') {
3451 $fieldusermod = "fk_user_m";
3452 } else {
3453 $fieldusermod = "fk_user_modif";
3454 }
3455 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3456 $sql .= " SET note".$newsuffix." = ".(!empty($note) ? ("'".$this->db->escape($note)."'") : "NULL");
3457 $sql .= ", ".$fieldusermod." = ".((int) $user->id);
3458 $sql .= " WHERE rowid = ".((int) $this->id);
3459
3460 dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
3461 if ($this->db->query($sql)) {
3462 if ($suffix == '_public') {
3463 $this->note_public = $note;
3464 } elseif ($suffix == '_private') {
3465 $this->note_private = $note;
3466 } else {
3467 $this->note = $note; // deprecated
3468 $this->note_private = $note;
3469 }
3470 if (empty($notrigger)) {
3471 switch ($this->element) {
3472 case 'societe':
3473 $trigger_name = 'COMPANY_MODIFY';
3474 break;
3475 case 'commande':
3476 $trigger_name = 'ORDER_MODIFY';
3477 break;
3478 case 'facture':
3479 $trigger_name = 'BILL_MODIFY';
3480 break;
3481 case 'invoice_supplier':
3482 $trigger_name = 'BILL_SUPPLIER_MODIFY';
3483 break;
3484 case 'facturerec':
3485 $trigger_name = 'BILLREC_MODIFIY';
3486 break;
3487 case 'expensereport':
3488 $trigger_name = 'EXPENSE_REPORT_MODIFY';
3489 break;
3490 default:
3491 $trigger_name = strtoupper($this->element) . '_MODIFY';
3492 }
3493 $ret = $this->call_trigger($trigger_name, $user);
3494 if ($ret < 0) {
3495 return -1;
3496 }
3497 }
3498 return 1;
3499 } else {
3500 $this->error = $this->db->lasterror();
3501 return -1;
3502 }
3503 }
3504
3505 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3514 public function update_note_public($note)
3515 {
3516 // phpcs:enable
3517 return $this->update_note($note, '_public');
3518 }
3519
3520 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3531 public function update_price($exclspec = 0, $roundingadjust = 'none', $nodatabaseupdate = 0, $seller = null)
3532 {
3533 // phpcs:enable
3534 global $conf, $hookmanager, $action;
3535
3536 $parameters = array('exclspec' => $exclspec, 'roundingadjust' => $roundingadjust, 'nodatabaseupdate' => $nodatabaseupdate, 'seller' => $seller);
3537 $reshook = $hookmanager->executeHooks('updateTotalPrice', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3538 if ($reshook > 0) {
3539 return 1; // replacement code
3540 } elseif ($reshook < 0) {
3541 return -1; // failure
3542 } // reshook = 0 => execute normal code
3543
3544 // Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
3545 $MODULE = "";
3546 if ($this->element == 'propal') {
3547 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
3548 } elseif ($this->element == 'commande' || $this->element == 'order') {
3549 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
3550 } elseif ($this->element == 'facture' || $this->element == 'invoice') {
3551 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
3552 } elseif ($this->element == 'facture_fourn' || $this->element == 'supplier_invoice' || $this->element == 'invoice_supplier' || $this->element == 'invoice_supplier_rec') {
3553 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
3554 } elseif ($this->element == 'order_supplier' || $this->element == 'supplier_order') {
3555 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
3556 } elseif ($this->element == 'supplier_proposal') {
3557 $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
3558 }
3559
3560 if (!empty($MODULE)) {
3561 if (!empty($conf->global->$MODULE)) {
3562 $modsactivated = explode(',', $conf->global->$MODULE);
3563 foreach ($modsactivated as $mod) {
3564 if (isModEnabled($mod)) {
3565 return 1; // update was disabled by specific setup
3566 }
3567 }
3568 }
3569 }
3570
3571 include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3572
3573 $forcedroundingmode = $roundingadjust;
3574 if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) {
3575 $forcedroundingmode = getDolGlobalString('MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND');
3576 } elseif ($forcedroundingmode == 'auto') {
3577 $forcedroundingmode = '0';
3578 }
3579
3580 $error = 0;
3581
3582 $multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
3583
3584 // Define constants to find lines to sum (field name int the table_element_line not into table_element)
3585 $fieldtva = 'total_tva';
3586 $fieldlocaltax1 = 'total_localtax1';
3587 $fieldlocaltax2 = 'total_localtax2';
3588 $fieldup = 'subprice';
3589 if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
3590 $fieldtva = 'tva';
3591 $fieldup = 'pu_ht';
3592 }
3593 if ($this->element == 'invoice_supplier_rec') {
3594 $fieldup = 'pu_ht';
3595 }
3596 if ($this->element == 'expensereport') {
3597 $fieldup = 'value_unit';
3598 }
3599
3600 $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,";
3601 $sql .= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
3602 if ($this->table_element_line == 'facturedet') {
3603 $sql .= ', situation_percent';
3604 }
3605 $sql .= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
3606 $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
3607 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3608 if ($exclspec) {
3609 $product_field = 'product_type';
3610 if ($this->table_element_line == 'contratdet') {
3611 $product_field = ''; // contratdet table has no product_type field
3612 }
3613 if ($product_field) {
3614 $sql .= " AND ".$product_field." <> 9";
3615 }
3616 }
3617 $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
3618
3619 dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3620
3621 $resql = $this->db->query($sql);
3622 if ($resql) {
3623 $this->total_ht = 0;
3624 $this->total_tva = 0;
3625 $this->total_localtax1 = 0;
3626 $this->total_localtax2 = 0;
3627 $this->total_ttc = 0;
3628 $total_ht_by_vats = array();
3629 $total_tva_by_vats = array();
3630 $total_ttc_by_vats = array();
3631 $this->multicurrency_total_ht = 0;
3632 $this->multicurrency_total_tva = 0;
3633 $this->multicurrency_total_ttc = 0;
3634
3635 $this->db->begin();
3636
3637 $num = $this->db->num_rows($resql);
3638 $i = 0;
3639 while ($i < $num) {
3640 $obj = $this->db->fetch_object($resql);
3641
3642 // Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
3643 $parameters = array('fk_element' => $obj->rowid);
3644 $reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3645
3646 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'
3647 // This part of code is to fix data. We should not call it too often.
3648 $localtax_array = array($obj->localtax1_type, $obj->localtax1_tx, $obj->localtax2_type, $obj->localtax2_tx);
3649 $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);
3650
3651 $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.
3652 $diff_on_current_total = price2num($obj->total_ttc - $obj->total_ht - $obj->total_tva - $obj->total_localtax1 - $obj->total_localtax2, 'MT', 1);
3653 //var_dump($obj->total_ht.' '.$obj->total_tva.' '.$obj->total_localtax1.' '.$obj->total_localtax2.' => '.$obj->total_ttc);
3654 //var_dump($diff_when_using_price_ht.' '.$diff_on_current_total);
3655
3656 if ($diff_on_current_total) {
3657 // This should not happen, we should always have in table: total_ttc = total_ht + total_vat + total_localtax1 + total_localtax2
3658 $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);
3659 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);
3660 $resqlfix = $this->db->query($sqlfix);
3661 if (!$resqlfix) {
3662 dol_print_error($this->db, 'Failed to update line');
3663 }
3664 $obj->total_tva = $tmpcal[1];
3665 $obj->total_ttc = $tmpcal[2];
3666 } elseif ($diff_when_using_price_ht && $roundingadjust == '0') {
3667 // After calculation from HT, total is consistent but we have found a difference between VAT part in calculation and into database and
3668 // we ask to force the use of rounding on line (like done on calculation) so we force update of line
3669 $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);
3670 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);
3671 $resqlfix = $this->db->query($sqlfix);
3672 if (!$resqlfix) {
3673 dol_print_error($this->db, 'Failed to update line');
3674 }
3675 $obj->total_tva = $tmpcal[1];
3676 $obj->total_ttc = $tmpcal[2];
3677 }
3678 }
3679
3680 $this->total_ht += $obj->total_ht; // The field visible at end of line detail
3681 $this->total_tva += $obj->total_tva;
3682 $this->total_localtax1 += $obj->total_localtax1;
3683 $this->total_localtax2 += $obj->total_localtax2;
3684 $this->total_ttc += $obj->total_ttc;
3685 $this->multicurrency_total_ht += $obj->multicurrency_total_ht; // The field visible at end of line detail
3686 $this->multicurrency_total_tva += $obj->multicurrency_total_tva;
3687 $this->multicurrency_total_ttc += $obj->multicurrency_total_ttc;
3688
3689 if (!isset($total_ht_by_vats[$obj->vatrate])) {
3690 $total_ht_by_vats[$obj->vatrate] = 0;
3691 }
3692 if (!isset($total_tva_by_vats[$obj->vatrate])) {
3693 $total_tva_by_vats[$obj->vatrate] = 0;
3694 }
3695 if (!isset($total_ttc_by_vats[$obj->vatrate])) {
3696 $total_ttc_by_vats[$obj->vatrate] = 0;
3697 }
3698 $total_ht_by_vats[$obj->vatrate] += $obj->total_ht;
3699 $total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
3700 $total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
3701
3702 if ($forcedroundingmode == '1') { // Check if we need adjustement onto line for vat. TODO This works on the company currency but not on foreign currency
3703 $tmpvat = price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
3704 $diff = price2num($total_tva_by_vats[$obj->vatrate] - $tmpvat, 'MT', 1);
3705 //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";
3706 if ($diff) {
3707 if (abs($diff) > (10 * pow(10, -1 * getDolGlobalInt('MAIN_MAX_DECIMALS_TOT', 0)))) {
3708 // If error is more than 10 times the accurancy of rounding. This should not happen.
3709 $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.';
3710 dol_syslog($errmsg, LOG_WARNING);
3711 $this->error = $errmsg;
3712 $error++;
3713 break;
3714 }
3715 $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);
3716 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);
3717
3718 $resqlfix = $this->db->query($sqlfix);
3719
3720 if (!$resqlfix) {
3721 dol_print_error($this->db, 'Failed to update line');
3722 }
3723
3724 $this->total_tva = (float) price2num($this->total_tva - $diff, '', 1);
3725 $this->total_ttc = (float) price2num($this->total_ttc - $diff, '', 1);
3726 $total_tva_by_vats[$obj->vatrate] = (float) price2num($total_tva_by_vats[$obj->vatrate] - $diff, '', 1);
3727 $total_ttc_by_vats[$obj->vatrate] = (float) price2num($total_ttc_by_vats[$obj->vatrate] - $diff, '', 1);
3728 }
3729 }
3730
3731 $i++;
3732 }
3733
3734 // Add revenue stamp to total
3735 $this->total_ttc += isset($this->revenuestamp) ? $this->revenuestamp : 0;
3736 $this->multicurrency_total_ttc += isset($this->revenuestamp) ? ($this->revenuestamp * $multicurrency_tx) : 0;
3737
3738 // Situations totals
3739 if (!empty($this->situation_cycle_ref) && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits') && $this->type != $this::TYPE_CREDIT_NOTE) {
3740 $prev_sits = $this->get_prev_sits();
3741
3742 foreach ($prev_sits as $sit) { // $sit is an object Facture loaded with a fetch.
3743 $this->total_ht -= $sit->total_ht;
3744 $this->total_tva -= $sit->total_tva;
3745 $this->total_localtax1 -= $sit->total_localtax1;
3746 $this->total_localtax2 -= $sit->total_localtax2;
3747 $this->total_ttc -= $sit->total_ttc;
3748 $this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
3749 $this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
3750 $this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
3751 }
3752 }
3753
3754 // Clean total
3755 $this->total_ht = (float) price2num($this->total_ht);
3756 $this->total_tva = (float) price2num($this->total_tva);
3757 $this->total_localtax1 = (float) price2num($this->total_localtax1);
3758 $this->total_localtax2 = (float) price2num($this->total_localtax2);
3759 $this->total_ttc = (float) price2num($this->total_ttc);
3760
3761 $this->db->free($resql);
3762
3763 // Now update global fields total_ht, total_ttc, total_tva, total_localtax1, total_localtax2, multicurrency_total_* of main object
3764 $fieldht = 'total_ht';
3765 $fieldtva = 'tva';
3766 $fieldlocaltax1 = 'localtax1';
3767 $fieldlocaltax2 = 'localtax2';
3768 $fieldttc = 'total_ttc';
3769 // Specific code for backward compatibility with old field names
3770 if (in_array($this->element, array('propal', 'commande', 'facture', 'facturerec', 'supplier_proposal', 'order_supplier', 'facture_fourn', 'invoice_supplier', 'invoice_supplier_rec', 'expensereport'))) {
3771 $fieldtva = 'total_tva';
3772 }
3773
3774 if (!$error && empty($nodatabaseupdate)) {
3775 $sql = "UPDATE ".$this->db->prefix().$this->table_element.' SET';
3776 $sql .= " ".$fieldht." = ".((float) price2num($this->total_ht, 'MT', 1)).",";
3777 $sql .= " ".$fieldtva." = ".((float) price2num($this->total_tva, 'MT', 1)).",";
3778 $sql .= " ".$fieldlocaltax1." = ".((float) price2num($this->total_localtax1, 'MT', 1)).",";
3779 $sql .= " ".$fieldlocaltax2." = ".((float) price2num($this->total_localtax2, 'MT', 1)).",";
3780 $sql .= " ".$fieldttc." = ".((float) price2num($this->total_ttc, 'MT', 1));
3781 $sql .= ", multicurrency_total_ht = ".((float) price2num($this->multicurrency_total_ht, 'MT', 1));
3782 $sql .= ", multicurrency_total_tva = ".((float) price2num($this->multicurrency_total_tva, 'MT', 1));
3783 $sql .= ", multicurrency_total_ttc = ".((float) price2num($this->multicurrency_total_ttc, 'MT', 1));
3784 $sql .= " WHERE rowid = ".((int) $this->id);
3785
3786 dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3787 $resql = $this->db->query($sql);
3788
3789 if (!$resql) {
3790 $error++;
3791 $this->error = $this->db->lasterror();
3792 $this->errors[] = $this->db->lasterror();
3793 }
3794 }
3795
3796 if (!$error) {
3797 $this->db->commit();
3798 return 1;
3799 } else {
3800 $this->db->rollback();
3801 return -1;
3802 }
3803 } else {
3804 dol_print_error($this->db, 'Bad request in update_price');
3805 return -1;
3806 }
3807 }
3808
3809 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3820 public function add_object_linked($origin = null, $origin_id = null, $f_user = null, $notrigger = 0)
3821 {
3822 // phpcs:enable
3823 global $user, $hookmanager, $action;
3824 $origin = (!empty($origin) ? $origin : $this->origin);
3825 $origin_id = (!empty($origin_id) ? $origin_id : $this->origin_id);
3826 $f_user = isset($f_user) ? $f_user : $user;
3827
3828 // Special case
3829 if ($origin == 'order') {
3830 $origin = 'commande';
3831 }
3832 if ($origin == 'invoice') {
3833 $origin = 'facture';
3834 }
3835 if ($origin == 'invoice_template') {
3836 $origin = 'facturerec';
3837 }
3838 if ($origin == 'supplierorder') {
3839 $origin = 'order_supplier';
3840 }
3841
3842 // 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.
3843 // 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.
3844 $coremodule = array('knowledgemanagement', 'partnership', 'workstation', 'ticket', 'recruitment', 'eventorganization', 'asset');
3845 // Add module part to target type if object has $module property and isn't in core modules.
3846 $targettype = ((!empty($this->module) && ! in_array($this->module, $coremodule)) ? $this->module.'_' : '').$this->element;
3847
3848 $parameters = array('targettype'=>$targettype);
3849 // Hook for explicitly set the targettype if it must be differtent than $this->element
3850 $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3851 if ($reshook > 0) {
3852 if (!empty($hookmanager->resArray['targettype'])) $targettype = $hookmanager->resArray['targettype'];
3853 }
3854
3855 $this->db->begin();
3856 $error = 0;
3857
3858 $sql = "INSERT INTO " . $this->db->prefix() . "element_element (";
3859 $sql .= "fk_source";
3860 $sql .= ", sourcetype";
3861 $sql .= ", fk_target";
3862 $sql .= ", targettype";
3863 $sql .= ") VALUES (";
3864 $sql .= ((int) $origin_id);
3865 $sql .= ", '" . $this->db->escape($origin) . "'";
3866 $sql .= ", " . ((int) $this->id);
3867 $sql .= ", '" . $this->db->escape($targettype) . "'";
3868 $sql .= ")";
3869
3870 dol_syslog(get_class($this) . "::add_object_linked", LOG_DEBUG);
3871 if ($this->db->query($sql)) {
3872 if (!$notrigger) {
3873 // Call trigger
3874 $this->context['link_origin'] = $origin;
3875 $this->context['link_origin_id'] = $origin_id;
3876 $result = $this->call_trigger('OBJECT_LINK_INSERT', $f_user);
3877 if ($result < 0) {
3878 $error++;
3879 }
3880 // End call triggers
3881 }
3882 } else {
3883 $this->error = $this->db->lasterror();
3884 $error++;
3885 }
3886
3887 if (!$error) {
3888 $this->db->commit();
3889 return 1;
3890 } else {
3891 $this->db->rollback();
3892 return 0;
3893 }
3894 }
3895
3918 public function fetchObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1)
3919 {
3920 global $conf, $hookmanager, $action;
3921
3922 // Important for pdf generation time reduction
3923 // This boolean is true if $this->linkedObjects has already been loaded with all objects linked without filter
3924 if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
3925 return 1;
3926 }
3927
3928 $this->linkedObjectsIds = array();
3929 $this->linkedObjects = array();
3930
3931 $justsource = false;
3932 $justtarget = false;
3933 $withtargettype = false;
3934 $withsourcetype = false;
3935
3936 $parameters = array('sourcetype'=>$sourcetype, 'sourceid'=>$sourceid, 'targettype'=>$targettype, 'targetid'=>$targetid);
3937 // Hook for explicitly set the targettype if it must be differtent than $this->element
3938 $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3939 if ($reshook > 0) {
3940 if (!empty($hookmanager->resArray['sourcetype'])) $sourcetype = $hookmanager->resArray['sourcetype'];
3941 if (!empty($hookmanager->resArray['sourceid'])) $sourceid = $hookmanager->resArray['sourceid'];
3942 if (!empty($hookmanager->resArray['targettype'])) $targettype = $hookmanager->resArray['targettype'];
3943 if (!empty($hookmanager->resArray['targetid'])) $targetid = $hookmanager->resArray['targetid'];
3944 }
3945
3946 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid)) {
3947 $justsource = true; // the source (id and type) is a search criteria
3948 if (!empty($targettype)) {
3949 $withtargettype = true;
3950 }
3951 }
3952 if (!empty($targetid) && !empty($targettype) && empty($sourceid)) {
3953 $justtarget = true; // the target (id and type) is a search criteria
3954 if (!empty($sourcetype)) {
3955 $withsourcetype = true;
3956 }
3957 }
3958
3959 $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
3960 $targetid = (!empty($targetid) ? $targetid : $this->id);
3961 $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
3962 $targettype = (!empty($targettype) ? $targettype : $this->element);
3963
3964 /*if (empty($sourceid) && empty($targetid))
3965 {
3966 dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
3967 return -1;
3968 }*/
3969
3970 // Links between objects are stored in table element_element
3971 $sql = "SELECT rowid, fk_source, sourcetype, fk_target, targettype";
3972 $sql .= " FROM ".$this->db->prefix()."element_element";
3973 $sql .= " WHERE ";
3974 if ($justsource || $justtarget) {
3975 if ($justsource) {
3976 $sql .= "fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."'";
3977 if ($withtargettype) {
3978 $sql .= " AND targettype = '".$this->db->escape($targettype)."'";
3979 }
3980 } elseif ($justtarget) {
3981 $sql .= "fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."'";
3982 if ($withsourcetype) {
3983 $sql .= " AND sourcetype = '".$this->db->escape($sourcetype)."'";
3984 }
3985 }
3986 } else {
3987 $sql .= "(fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."')";
3988 $sql .= " ".$clause." (fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."')";
3989 if ($loadalsoobjects && $this->id > 0 && $sourceid == $this->id && $sourcetype == $this->element && $targetid == $this->id && $targettype == $this->element && $clause == 'OR') {
3990 $this->linkedObjectsFullLoaded[$this->id] = true;
3991 }
3992 }
3993 $sql .= " ORDER BY ".$orderby;
3994
3995 dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
3996 $resql = $this->db->query($sql);
3997 if ($resql) {
3998 $num = $this->db->num_rows($resql);
3999 $i = 0;
4000 while ($i < $num) {
4001 $obj = $this->db->fetch_object($resql);
4002 if ($justsource || $justtarget) {
4003 if ($justsource) {
4004 $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
4005 } elseif ($justtarget) {
4006 $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
4007 }
4008 } else {
4009 if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype) {
4010 $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
4011 }
4012 if ($obj->fk_target == $targetid && $obj->targettype == $targettype) {
4013 $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
4014 }
4015 }
4016 $i++;
4017 }
4018
4019 if (!empty($this->linkedObjectsIds)) {
4020 $tmparray = $this->linkedObjectsIds;
4021 foreach ($tmparray as $objecttype => $objectids) { // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
4022 // Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
4023 $module = $element = $subelement = $objecttype;
4024 $regs = array();
4025 if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
4026 && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
4027 $module = $element = $regs[1];
4028 $subelement = $regs[2];
4029 }
4030
4031 $classpath = $element.'/class';
4032 // To work with non standard classpath or module name
4033 if ($objecttype == 'facture') {
4034 $classpath = 'compta/facture/class';
4035 } elseif ($objecttype == 'facturerec') {
4036 $classpath = 'compta/facture/class';
4037 $module = 'facture';
4038 } elseif ($objecttype == 'propal') {
4039 $classpath = 'comm/propal/class';
4040 } elseif ($objecttype == 'supplier_proposal') {
4041 $classpath = 'supplier_proposal/class';
4042 } elseif ($objecttype == 'shipping') {
4043 $classpath = 'expedition/class';
4044 $subelement = 'expedition';
4045 $module = 'expedition';
4046 } elseif ($objecttype == 'delivery') {
4047 $classpath = 'delivery/class';
4048 $subelement = 'delivery';
4049 $module = 'delivery_note';
4050 } elseif ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier') {
4051 $classpath = 'fourn/class';
4052 $module = 'fournisseur';
4053 } elseif ($objecttype == 'fichinter') {
4054 $classpath = 'fichinter/class';
4055 $subelement = 'fichinter';
4056 $module = 'ficheinter';
4057 } elseif ($objecttype == 'subscription') {
4058 $classpath = 'adherents/class';
4059 $module = 'adherent';
4060 } elseif ($objecttype == 'contact') {
4061 $module = 'societe';
4062 } elseif ($objecttype == 'action') {
4063 $module = 'agenda';
4064 $subelement = 'actionComm';
4065 }
4066
4067 // Set classfile
4068 $classfile = strtolower($subelement);
4069 $classname = ucfirst($subelement);
4070
4071 if ($objecttype == 'order') {
4072 $classfile = 'commande';
4073 $classname = 'Commande';
4074 } elseif ($objecttype == 'invoice_supplier') {
4075 $classfile = 'fournisseur.facture';
4076 $classname = 'FactureFournisseur';
4077 } elseif ($objecttype == 'order_supplier') {
4078 $classfile = 'fournisseur.commande';
4079 $classname = 'CommandeFournisseur';
4080 } elseif ($objecttype == 'supplier_proposal') {
4081 $classfile = 'supplier_proposal';
4082 $classname = 'SupplierProposal';
4083 } elseif ($objecttype == 'facturerec') {
4084 $classfile = 'facture-rec';
4085 $classname = 'FactureRec';
4086 } elseif ($objecttype == 'subscription') {
4087 $classfile = 'subscription';
4088 $classname = 'Subscription';
4089 } elseif ($objecttype == 'project' || $objecttype == 'projet') {
4090 $classpath = 'projet/class';
4091 $classfile = 'project';
4092 $classname = 'Project';
4093 } elseif ($objecttype == 'conferenceorboothattendee') {
4094 $classpath = 'eventorganization/class';
4095 $classfile = 'conferenceorboothattendee';
4096 $classname = 'ConferenceOrBoothAttendee';
4097 $module = 'eventorganization';
4098 } elseif ($objecttype == 'conferenceorbooth') {
4099 $classpath = 'eventorganization/class';
4100 $classfile = 'conferenceorbooth';
4101 $classname = 'ConferenceOrBooth';
4102 $module = 'eventorganization';
4103 } elseif ($objecttype == 'mo') {
4104 $classpath = 'mrp/class';
4105 $classfile = 'mo';
4106 $classname = 'Mo';
4107 $module = 'mrp';
4108 }
4109
4110 // Here $module, $classfile and $classname are set, we can use them.
4111 if (isModEnabled($module) && (($element != $this->element) || $alsosametype)) {
4112 if ($loadalsoobjects && (is_numeric($loadalsoobjects) || ($loadalsoobjects === $objecttype))) {
4113 dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
4114 //print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
4115 if (class_exists($classname)) {
4116 foreach ($objectids as $i => $objectid) { // $i is rowid into llx_element_element
4117 $object = new $classname($this->db);
4118 $ret = $object->fetch($objectid);
4119 if ($ret >= 0) {
4120 $this->linkedObjects[$objecttype][$i] = $object;
4121 }
4122 }
4123 }
4124 }
4125 } else {
4126 unset($this->linkedObjectsIds[$objecttype]);
4127 }
4128 }
4129 }
4130 return 1;
4131 } else {
4132 dol_print_error($this->db);
4133 return -1;
4134 }
4135 }
4136
4143 public function clearObjectLinkedCache()
4144 {
4145 if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
4146 unset($this->linkedObjectsFullLoaded[$this->id]);
4147 }
4148
4149 return 1;
4150 }
4151
4164 public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $f_user = null, $notrigger = 0)
4165 {
4166 global $user;
4167 $updatesource = false;
4168 $updatetarget = false;
4169 $f_user = isset($f_user) ? $f_user : $user;
4170
4171 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4172 $updatesource = true;
4173 } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4174 $updatetarget = true;
4175 }
4176
4177 $this->db->begin();
4178 $error = 0;
4179
4180 $sql = "UPDATE " . $this->db->prefix() . "element_element SET ";
4181 if ($updatesource) {
4182 $sql .= "fk_source = " . ((int) $sourceid);
4183 $sql .= ", sourcetype = '" . $this->db->escape($sourcetype) . "'";
4184 $sql .= " WHERE fk_target = " . ((int) $this->id);
4185 $sql .= " AND targettype = '" . $this->db->escape($this->element) . "'";
4186 } elseif ($updatetarget) {
4187 $sql .= "fk_target = " . ((int) $targetid);
4188 $sql .= ", targettype = '" . $this->db->escape($targettype) . "'";
4189 $sql .= " WHERE fk_source = " . ((int) $this->id);
4190 $sql .= " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4191 }
4192
4193 dol_syslog(get_class($this) . "::updateObjectLinked", LOG_DEBUG);
4194 if ($this->db->query($sql)) {
4195 if (!$notrigger) {
4196 // Call trigger
4197 $this->context['link_source_id'] = $sourceid;
4198 $this->context['link_source_type'] = $sourcetype;
4199 $this->context['link_target_id'] = $targetid;
4200 $this->context['link_target_type'] = $targettype;
4201 $result = $this->call_trigger('OBJECT_LINK_MODIFY', $f_user);
4202 if ($result < 0) {
4203 $error++;
4204 }
4205 // End call triggers
4206 }
4207 } else {
4208 $this->error = $this->db->lasterror();
4209 $error++;
4210 }
4211
4212 if (!$error) {
4213 $this->db->commit();
4214 return 1;
4215 } else {
4216 $this->db->rollback();
4217 return -1;
4218 }
4219 }
4220
4234 public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = '', $f_user = null, $notrigger = 0)
4235 {
4236 global $user;
4237 $deletesource = false;
4238 $deletetarget = false;
4239 $f_user = isset($f_user) ? $f_user : $user;
4240
4241 if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4242 $deletesource = true;
4243 } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4244 $deletetarget = true;
4245 }
4246
4247 $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
4248 $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
4249 $targetid = (!empty($targetid) ? $targetid : $this->id);
4250 $targettype = (!empty($targettype) ? $targettype : $this->element);
4251 $this->db->begin();
4252 $error = 0;
4253
4254 if (!$notrigger) {
4255 // Call trigger
4256 $this->context['link_id'] = $rowid;
4257 $this->context['link_source_id'] = $sourceid;
4258 $this->context['link_source_type'] = $sourcetype;
4259 $this->context['link_target_id'] = $targetid;
4260 $this->context['link_target_type'] = $targettype;
4261 $result = $this->call_trigger('OBJECT_LINK_DELETE', $f_user);
4262 if ($result < 0) {
4263 $error++;
4264 }
4265 // End call triggers
4266 }
4267
4268 if (!$error) {
4269 $sql = "DELETE FROM " . $this->db->prefix() . "element_element";
4270 $sql .= " WHERE";
4271 if ($rowid > 0) {
4272 $sql .= " rowid = " . ((int) $rowid);
4273 } else {
4274 if ($deletesource) {
4275 $sql .= " fk_source = " . ((int) $sourceid) . " AND sourcetype = '" . $this->db->escape($sourcetype) . "'";
4276 $sql .= " AND fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "'";
4277 } elseif ($deletetarget) {
4278 $sql .= " fk_target = " . ((int) $targetid) . " AND targettype = '" . $this->db->escape($targettype) . "'";
4279 $sql .= " AND fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4280 } else {
4281 $sql .= " (fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "')";
4282 $sql .= " OR";
4283 $sql .= " (fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "')";
4284 }
4285 }
4286
4287 dol_syslog(get_class($this) . "::deleteObjectLinked", LOG_DEBUG);
4288 if (!$this->db->query($sql)) {
4289 $this->error = $this->db->lasterror();
4290 $this->errors[] = $this->error;
4291 $error++;
4292 }
4293 }
4294
4295 if (!$error) {
4296 $this->db->commit();
4297 return 1;
4298 } else {
4299 $this->db->rollback();
4300 return 0;
4301 }
4302 }
4303
4313 public static function getAllItemsLinkedByObjectID($fk_object_where, $field_select, $field_where, $table_element)
4314 {
4315 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4316 return -1;
4317 }
4318
4319 global $db;
4320
4321 $sql = "SELECT ".$field_select." FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4322 $resql = $db->query($sql);
4323
4324 $TRes = array();
4325 if (!empty($resql)) {
4326 while ($res = $db->fetch_object($resql)) {
4327 $TRes[] = $res->{$field_select};
4328 }
4329 }
4330
4331 return $TRes;
4332 }
4333
4342 public static function deleteAllItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
4343 {
4344 if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4345 return -1;
4346 }
4347
4348 global $db;
4349
4350 $sql = "DELETE FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4351 $resql = $db->query($sql);
4352
4353 if (empty($resql)) {
4354 return 0;
4355 }
4356
4357 return 1;
4358 }
4359
4370 public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '', $fieldstatus = 'fk_statut')
4371 {
4372 global $user, $langs, $conf;
4373
4374 $savElementId = $elementId; // To be used later to know if we were using the method using the id of this or not.
4375
4376 $elementId = (!empty($elementId) ? $elementId : $this->id);
4377 $elementTable = (!empty($elementType) ? $elementType : $this->table_element);
4378
4379 $this->db->begin();
4380
4381 if ($elementTable == 'facture_rec') {
4382 $fieldstatus = "suspended";
4383 }
4384 if ($elementTable == 'mailing') {
4385 $fieldstatus = "statut";
4386 }
4387 if ($elementTable == 'cronjob') {
4388 $fieldstatus = "status";
4389 }
4390 if ($elementTable == 'user') {
4391 $fieldstatus = "statut";
4392 }
4393 if ($elementTable == 'expensereport') {
4394 $fieldstatus = "fk_statut";
4395 }
4396 if ($elementTable == 'commande_fournisseur_dispatch') {
4397 $fieldstatus = "status";
4398 }
4399 if (isset($this->fields) && is_array($this->fields) && array_key_exists('status', $this->fields)) {
4400 $fieldstatus = 'status';
4401 }
4402
4403 $sql = "UPDATE ".$this->db->prefix().$elementTable;
4404 $sql .= " SET ".$fieldstatus." = ".((int) $status);
4405 // If status = 1 = validated, update also fk_user_valid
4406 // TODO Replace the test on $elementTable by doing a test on existence of the field in $this->fields
4407 if ($status == 1 && in_array($elementTable, array('expensereport', 'inventory'))) {
4408 $sql .= ", fk_user_valid = ".((int) $user->id);
4409 }
4410 if ($status == 1 && in_array($elementTable, array('expensereport'))) {
4411 $sql .= ", date_valid = '".$this->db->idate(dol_now())."'";
4412 }
4413 if ($status == 1 && in_array($elementTable, array('inventory'))) {
4414 $sql .= ", date_validation = '".$this->db->idate(dol_now())."'";
4415 }
4416 $sql .= " WHERE rowid = ".((int) $elementId);
4417 $sql .= " AND ".$fieldstatus." <> ".((int) $status); // We avoid update if status already correct
4418
4419 dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
4420 $resql = $this->db->query($sql);
4421 if ($resql) {
4422 $error = 0;
4423
4424 $nb_rows_affected = $this->db->affected_rows($resql); // should be 1 or 0 if status was already correct
4425
4426 if ($nb_rows_affected > 0) {
4427 if (empty($trigkey)) {
4428 // Try to guess trigkey (for backward compatibility, now we should have trigkey defined into the call of setStatus)
4429 if ($this->element == 'supplier_proposal' && $status == 2) {
4430 $trigkey = 'SUPPLIER_PROPOSAL_SIGN'; // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
4431 }
4432 if ($this->element == 'supplier_proposal' && $status == 3) {
4433 $trigkey = 'SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
4434 }
4435 if ($this->element == 'supplier_proposal' && $status == 4) {
4436 $trigkey = 'SUPPLIER_PROPOSAL_CLOSE'; // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
4437 }
4438 if ($this->element == 'fichinter' && $status == 3) {
4439 $trigkey = 'FICHINTER_CLASSIFY_DONE';
4440 }
4441 if ($this->element == 'fichinter' && $status == 2) {
4442 $trigkey = 'FICHINTER_CLASSIFY_BILLED';
4443 }
4444 if ($this->element == 'fichinter' && $status == 1) {
4445 $trigkey = 'FICHINTER_CLASSIFY_UNBILLED';
4446 }
4447 }
4448
4449 if ($trigkey) {
4450 // Call trigger
4451 $result = $this->call_trigger($trigkey, $user);
4452 if ($result < 0) {
4453 $error++;
4454 }
4455 // End call triggers
4456 }
4457 } else {
4458 // The status was probably already good. We do nothing more, no triggers.
4459 }
4460
4461 if (!$error) {
4462 $this->db->commit();
4463
4464 if (empty($savElementId)) {
4465 // If the element we update is $this (so $elementId was provided as null)
4466 if ($fieldstatus == 'tosell') {
4467 $this->status = $status;
4468 } elseif ($fieldstatus == 'tobuy') {
4469 $this->status_buy = $status;
4470 } else {
4471 $this->statut = $status;
4472 $this->status = $status;
4473 }
4474 }
4475
4476 return 1;
4477 } else {
4478 $this->db->rollback();
4479 dol_syslog(get_class($this)."::setStatut ".$this->error, LOG_ERR);
4480 return -1;
4481 }
4482 } else {
4483 $this->error = $this->db->lasterror();
4484 $this->db->rollback();
4485 return -1;
4486 }
4487 }
4488
4489
4497 public function getCanvas($id = 0, $ref = '')
4498 {
4499 global $conf;
4500
4501 if (empty($id) && empty($ref)) {
4502 return 0;
4503 }
4504 if (!empty($conf->global->MAIN_DISABLE_CANVAS)) {
4505 return 0; // To increase speed. Not enabled by default.
4506 }
4507
4508 // Clean parameters
4509 $ref = trim($ref);
4510
4511 $sql = "SELECT rowid, canvas";
4512 $sql .= " FROM ".$this->db->prefix().$this->table_element;
4513 $sql .= " WHERE entity IN (".getEntity($this->element).")";
4514 if (!empty($id)) {
4515 $sql .= " AND rowid = ".((int) $id);
4516 }
4517 if (!empty($ref)) {
4518 $sql .= " AND ref = '".$this->db->escape($ref)."'";
4519 }
4520
4521 $resql = $this->db->query($sql);
4522 if ($resql) {
4523 $obj = $this->db->fetch_object($resql);
4524 if ($obj) {
4525 $this->canvas = $obj->canvas;
4526 return 1;
4527 } else {
4528 return 0;
4529 }
4530 } else {
4531 dol_print_error($this->db);
4532 return -1;
4533 }
4534 }
4535
4536
4543 public function getSpecialCode($lineid)
4544 {
4545 $sql = "SELECT special_code FROM ".$this->db->prefix().$this->table_element_line;
4546 $sql .= " WHERE rowid = ".((int) $lineid);
4547 $resql = $this->db->query($sql);
4548 if ($resql) {
4549 $row = $this->db->fetch_row($resql);
4550 return (!empty($row[0]) ? $row[0] : 0);
4551 }
4552
4553 return 0;
4554 }
4555
4564 public function isObjectUsed($id = 0, $entity = 0)
4565 {
4566 global $langs;
4567
4568 if (empty($id)) {
4569 $id = $this->id;
4570 }
4571
4572 // Check parameters
4573 if (!isset($this->childtables) || !is_array($this->childtables) || count($this->childtables) == 0) {
4574 dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
4575 return -1;
4576 }
4577
4578 $arraytoscan = $this->childtables; // array('tablename'=>array('fk_element'=>'parentfield'), ...) or array('tablename'=>array('parent'=>table_parent, 'parentkey'=>'nameoffieldforparentfkkey'), ...)
4579 // For backward compatibility, we check if array is old format array('tablename1', 'tablename2', ...)
4580 $tmparray = array_keys($this->childtables);
4581 if (is_numeric($tmparray[0])) {
4582 $arraytoscan = array_flip($this->childtables);
4583 }
4584
4585 // Test if child exists
4586 $haschild = 0;
4587 foreach ($arraytoscan as $table => $element) {
4588 //print $id.'-'.$table.'-'.$elementname.'<br>';
4589 // Check if element can be deleted
4590 $sql = "SELECT COUNT(*) as nb";
4591 $sql.= " FROM ".$this->db->prefix().$table." as c";
4592 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4593 $sql.= ", ".$this->db->prefix().$element['parent']." as p";
4594 }
4595 if (!empty($element['fk_element'])) {
4596 $sql.= " WHERE c.".$element['fk_element']." = ".((int) $id);
4597 } else {
4598 $sql.= " WHERE c.".$this->fk_element." = ".((int) $id);
4599 }
4600 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4601 $sql.= " AND c.".$element['parentkey']." = p.rowid";
4602 }
4603 if (!empty($element['parent']) && !empty($element['parenttypefield']) && !empty($element['parenttypevalue'])) {
4604 $sql.= " AND c.".$element['parenttypefield']." = '".$this->db->escape($element['parenttypevalue'])."'";
4605 }
4606 if (!empty($entity)) {
4607 if (!empty($element['parent']) && !empty($element['parentkey'])) {
4608 $sql.= " AND p.entity = ".((int) $entity);
4609 } else {
4610 $sql.= " AND c.entity = ".((int) $entity);
4611 }
4612 }
4613
4614 $resql = $this->db->query($sql);
4615 if ($resql) {
4616 $obj = $this->db->fetch_object($resql);
4617 if ($obj->nb > 0) {
4618 $langs->load("errors");
4619 //print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
4620 $haschild += $obj->nb;
4621 if (is_numeric($element)) { // very old usage array('table1', 'table2', ...)
4622 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $table);
4623 } elseif (is_string($element)) { // old usage array('table1' => 'TranslateKey1', 'table2' => 'TranslateKey2', ...)
4624 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element));
4625 } else { // new usage: $element['name']=Translation key
4626 $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element['name']));
4627 }
4628 break; // We found at least one, we stop here
4629 }
4630 } else {
4631 $this->errors[] = $this->db->lasterror();
4632 return -1;
4633 }
4634 }
4635 if ($haschild > 0) {
4636 $this->errors[] = "ErrorRecordHasChildren";
4637 return $haschild;
4638 } else {
4639 return 0;
4640 }
4641 }
4642
4649 public function hasProductsOrServices($predefined = -1)
4650 {
4651 $nb = 0;
4652
4653 foreach ($this->lines as $key => $val) {
4654 $qualified = 0;
4655 if ($predefined == -1) {
4656 $qualified = 1;
4657 }
4658 if ($predefined == 1 && $val->fk_product > 0) {
4659 $qualified = 1;
4660 }
4661 if ($predefined == 0 && $val->fk_product <= 0) {
4662 $qualified = 1;
4663 }
4664 if ($predefined == 2 && $val->fk_product > 0 && $val->product_type == 0) {
4665 $qualified = 1;
4666 }
4667 if ($predefined == 3 && $val->fk_product > 0 && $val->product_type == 1) {
4668 $qualified = 1;
4669 }
4670 if ($qualified) {
4671 $nb++;
4672 }
4673 }
4674 dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
4675 return $nb;
4676 }
4677
4683 public function getTotalDiscount()
4684 {
4685 if (!empty($this->table_element_line) ) {
4686 $total_discount = 0.00;
4687
4688 $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
4689 $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
4690 $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
4691
4692 dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
4693 $resql = $this->db->query($sql);
4694 if ($resql) {
4695 $num = $this->db->num_rows($resql);
4696 $i = 0;
4697 while ($i < $num) {
4698 $obj = $this->db->fetch_object($resql);
4699
4700 $pu_ht = $obj->pu_ht;
4701 $qty = $obj->qty;
4702 $total_ht = $obj->total_ht;
4703
4704 $total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
4705 $total_discount += $total_discount_line;
4706
4707 $i++;
4708 }
4709 }
4710
4711 //print $total_discount; exit;
4712 return price2num($total_discount);
4713 }
4714
4715 return null;
4716 }
4717
4718
4725 public function getTotalWeightVolume()
4726 {
4727 $totalWeight = 0;
4728 $totalVolume = 0;
4729 // defined for shipment only
4730 $totalOrdered = '';
4731 // defined for shipment only
4732 $totalToShip = '';
4733
4734 foreach ($this->lines as $line) {
4735 if (isset($line->qty_asked)) {
4736 if (empty($totalOrdered)) {
4737 $totalOrdered = 0; // Avoid warning because $totalOrdered is ''
4738 }
4739 $totalOrdered += $line->qty_asked; // defined for shipment only
4740 }
4741 if (isset($line->qty_shipped)) {
4742 if (empty($totalToShip)) {
4743 $totalToShip = 0; // Avoid warning because $totalToShip is ''
4744 }
4745 $totalToShip += $line->qty_shipped; // defined for shipment only
4746 } elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty)) {
4747 if (empty($totalToShip)) {
4748 $totalToShip = 0;
4749 }
4750 $totalToShip += $line->qty; // defined for reception only
4751 }
4752
4753 // Define qty, weight, volume, weight_units, volume_units
4754 if ($this->element == 'shipping') {
4755 // for shipments
4756 $qty = $line->qty_shipped ? $line->qty_shipped : 0;
4757 } else {
4758 $qty = $line->qty ? $line->qty : 0;
4759 }
4760
4761 $weight = !empty($line->weight) ? $line->weight : 0;
4762 ($weight == 0 && !empty($line->product->weight)) ? $weight = $line->product->weight : 0;
4763 $volume = !empty($line->volume) ? $line->volume : 0;
4764 ($volume == 0 && !empty($line->product->volume)) ? $volume = $line->product->volume : 0;
4765
4766 $weight_units = !empty($line->weight_units) ? $line->weight_units : 0;
4767 ($weight_units == 0 && !empty($line->product->weight_units)) ? $weight_units = $line->product->weight_units : 0;
4768 $volume_units = !empty($line->volume_units) ? $line->volume_units : 0;
4769 ($volume_units == 0 && !empty($line->product->volume_units)) ? $volume_units = $line->product->volume_units : 0;
4770
4771 $weightUnit = 0;
4772 $volumeUnit = 0;
4773 if (!empty($weight_units)) {
4774 $weightUnit = $weight_units;
4775 }
4776 if (!empty($volume_units)) {
4777 $volumeUnit = $volume_units;
4778 }
4779
4780 if (empty($totalWeight)) {
4781 $totalWeight = 0; // Avoid warning because $totalWeight is ''
4782 }
4783 if (empty($totalVolume)) {
4784 $totalVolume = 0; // Avoid warning because $totalVolume is ''
4785 }
4786
4787 //var_dump($line->volume_units);
4788 if ($weight_units < 50) { // < 50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
4789 $trueWeightUnit = pow(10, $weightUnit);
4790 $totalWeight += $weight * $qty * $trueWeightUnit;
4791 } else {
4792 if ($weight_units == 99) {
4793 // conversion 1 Pound = 0.45359237 KG
4794 $trueWeightUnit = 0.45359237;
4795 $totalWeight += $weight * $qty * $trueWeightUnit;
4796 } elseif ($weight_units == 98) {
4797 // conversion 1 Ounce = 0.0283495 KG
4798 $trueWeightUnit = 0.0283495;
4799 $totalWeight += $weight * $qty * $trueWeightUnit;
4800 } else {
4801 $totalWeight += $weight * $qty; // This may be wrong if we mix different units
4802 }
4803 }
4804 if ($volume_units < 50) { // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
4805 //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
4806 $trueVolumeUnit = pow(10, $volumeUnit);
4807 //print $line->volume;
4808 $totalVolume += $volume * $qty * $trueVolumeUnit;
4809 } else {
4810 $totalVolume += $volume * $qty; // This may be wrong if we mix different units
4811 }
4812 }
4813
4814 return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
4815 }
4816
4817
4823 public function setExtraParameters()
4824 {
4825 $this->db->begin();
4826
4827 $extraparams = (!empty($this->extraparams) ? json_encode($this->extraparams) : null);
4828
4829 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
4830 $sql .= " SET extraparams = ".(!empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
4831 $sql .= " WHERE rowid = ".((int) $this->id);
4832
4833 dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
4834 $resql = $this->db->query($sql);
4835 if (!$resql) {
4836 $this->error = $this->db->lasterror();
4837 $this->db->rollback();
4838 return -1;
4839 } else {
4840 $this->db->commit();
4841 return 1;
4842 }
4843 }
4844
4845
4846 // --------------------
4847 // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
4848 // --------------------
4849
4850 /* This is to show add lines */
4851
4861 public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl')
4862 {
4863 global $conf, $user, $langs, $object, $hookmanager, $extrafields;
4864 global $form;
4865
4866 // Line extrafield
4867 if (!is_object($extrafields)) {
4868 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4869 $extrafields = new ExtraFields($this->db);
4870 }
4871 $extrafields->fetch_name_optionals_label($this->table_element_line);
4872
4873 // Output template part (modules that overwrite templates must declare this into descriptor)
4874 // Use global variables + $dateSelector + $seller and $buyer
4875 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'.
4876 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4877 foreach ($dirtpls as $module => $reldir) {
4878 if (!empty($module)) {
4879 $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
4880 } else {
4881 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php';
4882 }
4883
4884 if (empty($conf->file->strict_mode)) {
4885 $res = @include $tpl;
4886 } else {
4887 $res = include $tpl; // for debug
4888 }
4889 if ($res) {
4890 break;
4891 }
4892 }
4893 }
4894
4895
4896
4897 /* This is to show array of line of details */
4898
4899
4914 public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl')
4915 {
4916 global $conf, $hookmanager, $langs, $user, $form, $extrafields, $object;
4917 // TODO We should not use global var for this
4918 global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
4919
4920 // Define usemargins
4921 $usemargins = 0;
4922 if (isModEnabled('margin') && !empty($this->element) && in_array($this->element, array('facture', 'facturerec', 'propal', 'commande'))) {
4923 $usemargins = 1;
4924 }
4925
4926 $num = count($this->lines);
4927
4928 // Line extrafield
4929 if (!is_object($extrafields)) {
4930 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4931 $extrafields = new ExtraFields($this->db);
4932 }
4933 $extrafields->fetch_name_optionals_label($this->table_element_line);
4934
4935 $parameters = array('num'=>$num, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$this->table_element_line);
4936 $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4937 if (empty($reshook)) {
4938 // Output template part (modules that overwrite templates must declare this into descriptor)
4939 // Use global variables + $dateSelector + $seller and $buyer
4940 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook.
4941 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4942 foreach ($dirtpls as $module => $reldir) {
4943 $res = 0;
4944 if (!empty($module)) {
4945 $tpl = dol_buildpath($reldir.'/objectline_title.tpl.php');
4946 } else {
4947 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php';
4948 }
4949 if (file_exists($tpl)) {
4950 if (empty($conf->file->strict_mode)) {
4951 $res = @include $tpl;
4952 } else {
4953 $res = include $tpl; // for debug
4954 }
4955 }
4956 if ($res) {
4957 break;
4958 }
4959 }
4960 }
4961
4962 $i = 0;
4963
4964 print "<!-- begin printObjectLines() --><tbody>\n";
4965 foreach ($this->lines as $line) {
4966 //Line extrafield
4967 $line->fetch_optionals();
4968
4969 //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line)))
4970 if (is_object($hookmanager)) { // Old code is commented on preceding line.
4971 if (empty($line->fk_parent_line)) {
4972 $parameters = array('line'=>$line, 'num'=>$num, 'i'=>$i, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$line->table_element, 'defaulttpldir'=>$defaulttpldir);
4973 $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4974 } else {
4975 $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);
4976 $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4977 }
4978 }
4979 if (empty($reshook)) {
4980 $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafields, $defaulttpldir);
4981 }
4982
4983 $i++;
4984 }
4985 print "</tbody><!-- end printObjectLines() -->\n";
4986 }
4987
5005 public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/core/tpl')
5006 {
5007 global $conf, $langs, $user, $object, $hookmanager;
5008 global $form;
5009 global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
5010
5011 $object_rights = $this->getRights();
5012
5013 $text = '';
5014 $description = '';
5015
5016 // Line in view mode
5017 if ($action != 'editline' || $selected != $line->id) {
5018 // Product
5019 if (!empty($line->fk_product) && $line->fk_product > 0) {
5020 $product_static = new Product($this->db);
5021 $product_static->fetch($line->fk_product);
5022
5023 $product_static->ref = $line->ref; //can change ref in hook
5024 $product_static->label = !empty($line->label) ? $line->label : ""; //can change label in hook
5025
5026 $text = $product_static->getNomUrl(1);
5027
5028 // Define output language and label
5029 if (getDolGlobalInt('MAIN_MULTILANGS')) {
5030 if (property_exists($this, 'socid') && !is_object($this->thirdparty)) {
5031 dol_print_error('', 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
5032 return;
5033 }
5034
5035 $prod = new Product($this->db);
5036 $prod->fetch($line->fk_product);
5037
5038 $outputlangs = $langs;
5039 $newlang = '';
5040 if (empty($newlang) && GETPOST('lang_id', 'aZ09')) {
5041 $newlang = GETPOST('lang_id', 'aZ09');
5042 }
5043 if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang) && is_object($this->thirdparty)) {
5044 $newlang = $this->thirdparty->default_lang; // To use language of customer
5045 }
5046 if (!empty($newlang)) {
5047 $outputlangs = new Translate("", $conf);
5048 $outputlangs->setDefaultLang($newlang);
5049 }
5050
5051 $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
5052 } else {
5053 $label = $line->product_label;
5054 }
5055
5056 $text .= ' - '.(!empty($line->label) ? $line->label : $label);
5057 $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.
5058 }
5059
5060 $line->pu_ttc = price2num((!empty($line->subprice) ? $line->subprice : 0) * (1 + ((!empty($line->tva_tx) ? $line->tva_tx : 0) / 100)), 'MU');
5061
5062 // Output template part (modules that overwrite templates must declare this into descriptor)
5063 // Use global variables + $dateSelector + $seller and $buyer
5064 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5065 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5066 foreach ($dirtpls as $module => $reldir) {
5067 $res = 0;
5068 if (!empty($module)) {
5069 $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
5070 } else {
5071 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php';
5072 }
5073 if (file_exists($tpl)) {
5074 if (empty($conf->file->strict_mode)) {
5075 $res = @include $tpl;
5076 } else {
5077 $res = include $tpl; // for debug
5078 }
5079 }
5080 if ($res) {
5081 break;
5082 }
5083 }
5084 }
5085
5086 // Line in update mode
5087 if ($this->statut == 0 && $action == 'editline' && $selected == $line->id) {
5088 $label = (!empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
5089
5090 $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU');
5091
5092 // Output template part (modules that overwrite templates must declare this into descriptor)
5093 // Use global variables + $dateSelector + $seller and $buyer
5094 // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5095 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5096 foreach ($dirtpls as $module => $reldir) {
5097 if (!empty($module)) {
5098 $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
5099 } else {
5100 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php';
5101 }
5102
5103 if (empty($conf->file->strict_mode)) {
5104 $res = @include $tpl;
5105 } else {
5106 $res = include $tpl; // for debug
5107 }
5108 if ($res) {
5109 break;
5110 }
5111 }
5112 }
5113 }
5114
5115
5116 /* This is to show array of line of details of source object */
5117
5118
5129 public function printOriginLinesList($restrictlist = '', $selectedLines = array())
5130 {
5131 global $langs, $hookmanager, $conf, $form, $action;
5132
5133 print '<tr class="liste_titre">';
5134 print '<td class="linecolref">'.$langs->trans('Ref').'</td>';
5135 print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
5136 print '<td class="linecolvat right">'.$langs->trans('VATRate').'</td>';
5137 print '<td class="linecoluht right">'.$langs->trans('PriceUHT').'</td>';
5138 if (isModEnabled("multicurrency")) {
5139 print '<td class="linecoluht_currency right">'.$langs->trans('PriceUHTCurrency').'</td>';
5140 }
5141 print '<td class="linecolqty right">'.$langs->trans('Qty').'</td>';
5142 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5143 print '<td class="linecoluseunit left">'.$langs->trans('Unit').'</td>';
5144 }
5145 print '<td class="linecoldiscount right">'.$langs->trans('ReductionShort').'</td>';
5146 print '<td class="linecolht right">'.$langs->trans('TotalHT').'</td>';
5147 print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
5148 print '</tr>';
5149 $i = 0;
5150
5151 if (!empty($this->lines)) {
5152 foreach ($this->lines as $line) {
5153 $reshook = 0;
5154 //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line))) {
5155 if (is_object($hookmanager)) { // Old code is commented on preceding line.
5156 $parameters = array('line'=>$line, 'i'=>$i, 'restrictlist'=>$restrictlist, 'selectedLines'=> $selectedLines);
5157 if (!empty($line->fk_parent_line)) { $parameters['fk_parent_line'] = $line->fk_parent_line; }
5158 $reshook = $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5159 }
5160 if (empty($reshook)) {
5161 $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines);
5162 }
5163
5164 $i++;
5165 }
5166 }
5167 }
5168
5182 public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
5183 {
5184 global $langs, $conf;
5185
5186 //var_dump($line);
5187 if (!empty($line->date_start)) {
5188 $date_start = $line->date_start;
5189 } else {
5190 $date_start = $line->date_debut_prevue;
5191 if ($line->date_debut_reel) {
5192 $date_start = $line->date_debut_reel;
5193 }
5194 }
5195 if (!empty($line->date_end)) {
5196 $date_end = $line->date_end;
5197 } else {
5198 $date_end = $line->date_fin_prevue;
5199 if ($line->date_fin_reel) {
5200 $date_end = $line->date_fin_reel;
5201 }
5202 }
5203
5204 $this->tpl['id'] = $line->id;
5205
5206 $this->tpl['label'] = '';
5207 if (!empty($line->fk_parent_line)) {
5208 $this->tpl['label'] .= img_picto('', 'rightarrow');
5209 }
5210
5211 if (($line->info_bits & 2) == 2) { // TODO Not sure this is used for source object
5212 $discount = new DiscountAbsolute($this->db);
5213 $discount->fk_soc = $this->socid;
5214 $this->tpl['label'] .= $discount->getNomUrl(0, 'discount');
5215 } elseif (!empty($line->fk_product)) {
5216 $productstatic = new Product($this->db);
5217 $productstatic->id = $line->fk_product;
5218 $productstatic->ref = $line->ref;
5219 $productstatic->type = $line->fk_product_type;
5220 if (empty($productstatic->ref)) {
5221 $line->fetch_product();
5222 $productstatic = $line->product;
5223 }
5224
5225 $this->tpl['label'] .= $productstatic->getNomUrl(1);
5226 $this->tpl['label'] .= ' - '.(!empty($line->label) ? $line->label : $line->product_label);
5227 // Dates
5228 if ($line->product_type == 1 && ($date_start || $date_end)) {
5229 $this->tpl['label'] .= get_date_range($date_start, $date_end);
5230 }
5231 } else {
5232 $this->tpl['label'] .= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
5233 if (!empty($line->desc)) {
5234 $this->tpl['label'] .= $line->desc;
5235 } else {
5236 $this->tpl['label'] .= ($line->label ? '&nbsp;'.$line->label : '');
5237 }
5238
5239 // Dates
5240 if ($line->product_type == 1 && ($date_start || $date_end)) {
5241 $this->tpl['label'] .= get_date_range($date_start, $date_end);
5242 }
5243 }
5244
5245 if (!empty($line->desc)) {
5246 if ($line->desc == '(CREDIT_NOTE)') { // TODO Not sure this is used for source object
5247 $discount = new DiscountAbsolute($this->db);
5248 $discount->fetch($line->fk_remise_except);
5249 $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
5250 } elseif ($line->desc == '(DEPOSIT)') { // TODO Not sure this is used for source object
5251 $discount = new DiscountAbsolute($this->db);
5252 $discount->fetch($line->fk_remise_except);
5253 $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
5254 } elseif ($line->desc == '(EXCESS RECEIVED)') {
5255 $discount = new DiscountAbsolute($this->db);
5256 $discount->fetch($line->fk_remise_except);
5257 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
5258 } elseif ($line->desc == '(EXCESS PAID)') {
5259 $discount = new DiscountAbsolute($this->db);
5260 $discount->fetch($line->fk_remise_except);
5261 $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
5262 } else {
5263 $this->tpl['description'] = dol_trunc($line->desc, 60);
5264 }
5265 } else {
5266 $this->tpl['description'] = '&nbsp;';
5267 }
5268
5269 // VAT Rate
5270 $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
5271 $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
5272 if (!empty($line->vat_src_code) && !preg_match('/\‍(/', $this->tpl['vat_rate'])) {
5273 $this->tpl['vat_rate'] .= ' ('.$line->vat_src_code.')';
5274 }
5275
5276 $this->tpl['price'] = price($line->subprice);
5277 $this->tpl['total_ht'] = price($line->total_ht);
5278 $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
5279 $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
5280 if (getDolGlobalInt('PRODUCT_USE_UNITS')) {
5281 $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
5282 }
5283 $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
5284
5285 // Is the line strike or not
5286 $this->tpl['strike'] = 0;
5287 if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) {
5288 $this->tpl['strike'] = 1;
5289 }
5290
5291 // Output template part (modules that overwrite templates must declare this into descriptor)
5292 // Use global variables + $dateSelector + $seller and $buyer
5293 $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5294 foreach ($dirtpls as $module => $reldir) {
5295 if (!empty($module)) {
5296 $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
5297 } else {
5298 $tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php';
5299 }
5300
5301 if (empty($conf->file->strict_mode)) {
5302 $res = @include $tpl;
5303 } else {
5304 $res = include $tpl; // for debug
5305 }
5306 if ($res) {
5307 break;
5308 }
5309 }
5310 }
5311
5312
5313 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5324 public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
5325 {
5326 // phpcs:enable
5327 $this->db->begin();
5328
5329 $sql = "INSERT INTO ".$this->db->prefix()."element_resources (";
5330 $sql .= "resource_id";
5331 $sql .= ", resource_type";
5332 $sql .= ", element_id";
5333 $sql .= ", element_type";
5334 $sql .= ", busy";
5335 $sql .= ", mandatory";
5336 $sql .= ") VALUES (";
5337 $sql .= ((int) $resource_id);
5338 $sql .= ", '".$this->db->escape($resource_type)."'";
5339 $sql .= ", '".$this->db->escape($this->id)."'";
5340 $sql .= ", '".$this->db->escape($this->element)."'";
5341 $sql .= ", '".$this->db->escape($busy)."'";
5342 $sql .= ", '".$this->db->escape($mandatory)."'";
5343 $sql .= ")";
5344
5345 dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
5346 if ($this->db->query($sql)) {
5347 $this->db->commit();
5348 return 1;
5349 } else {
5350 $this->error = $this->db->lasterror();
5351 $this->db->rollback();
5352 return 0;
5353 }
5354 }
5355
5356 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5365 public function delete_resource($rowid, $element, $notrigger = 0)
5366 {
5367 // phpcs:enable
5368 global $user;
5369
5370 $this->db->begin();
5371
5372 $sql = "DELETE FROM ".$this->db->prefix()."element_resources";
5373 $sql .= " WHERE rowid = ".((int) $rowid);
5374
5375 dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
5376
5377 $resql = $this->db->query($sql);
5378 if (!$resql) {
5379 $this->error = $this->db->lasterror();
5380 $this->db->rollback();
5381 return -1;
5382 } else {
5383 if (!$notrigger) {
5384 $result = $this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
5385 if ($result < 0) {
5386 $this->db->rollback();
5387 return -1;
5388 }
5389 }
5390 $this->db->commit();
5391 return 1;
5392 }
5393 }
5394
5395
5401 public function __clone()
5402 {
5403 // Force a copy of this->lines, otherwise it will point to same object.
5404 if (isset($this->lines) && is_array($this->lines)) {
5405 $nboflines = count($this->lines);
5406 for ($i = 0; $i < $nboflines; $i++) {
5407 if (is_object($this->lines[$i])) {
5408 $this->lines[$i] = clone $this->lines[$i];
5409 }
5410 }
5411 }
5412 }
5413
5427 protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
5428 {
5429 global $conf, $langs, $user, $hookmanager, $action;
5430
5431 $srctemplatepath = '';
5432
5433 $parameters = array('modelspath'=>$modelspath, 'modele'=>$modele, 'outputlangs'=>$outputlangs, 'hidedetails'=>$hidedetails, 'hidedesc'=>$hidedesc, 'hideref'=>$hideref, 'moreparams'=>$moreparams);
5434 $reshook = $hookmanager->executeHooks('commonGenerateDocument', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5435
5436 if (!empty($reshook)) {
5437 return $reshook;
5438 }
5439
5440 dol_syslog("commonGenerateDocument modele=".$modele." outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
5441
5442 if (empty($modele)) {
5443 $this->error = 'BadValueForParameterModele';
5444 return -1;
5445 }
5446
5447 // Increase limit for PDF build
5448 $err = error_reporting();
5449 error_reporting(0);
5450 @set_time_limit(120);
5451 error_reporting($err);
5452
5453 // If selected model is a filename template (then $modele="modelname" or "modelname:filename")
5454 $tmp = explode(':', $modele, 2);
5455 if (!empty($tmp[1])) {
5456 $modele = $tmp[0];
5457 $srctemplatepath = $tmp[1];
5458 }
5459
5460 // Search template files
5461 $file = '';
5462 $classname = '';
5463 $filefound = '';
5464 $dirmodels = array('/');
5465 if (is_array($conf->modules_parts['models'])) {
5466 $dirmodels = array_merge($dirmodels, $conf->modules_parts['models']);
5467 }
5468 foreach ($dirmodels as $reldir) {
5469 foreach (array('doc', 'pdf') as $prefix) {
5470 if (in_array(get_class($this), array('Adherent'))) {
5471 // Member module use prefix_modele.class.php
5472 $file = $prefix."_".$modele.".class.php";
5473 } else {
5474 // Other module use prefix_modele.modules.php
5475 $file = $prefix."_".$modele.".modules.php";
5476 }
5477
5478 // On verifie l'emplacement du modele
5479 $file = dol_buildpath($reldir.$modelspath.$file, 0);
5480 if (file_exists($file)) {
5481 $filefound = $file;
5482 $classname = $prefix.'_'.$modele;
5483 break;
5484 }
5485 }
5486 if ($filefound) {
5487 break;
5488 }
5489 }
5490
5491 if (!$filefound) {
5492 $this->error = $langs->trans("Error").' Failed to load doc generator with modelpaths='.$modelspath.' - modele='.$modele;
5493 $this->errors[] = $this->error;
5494 dol_syslog($this->error, LOG_ERR);
5495 return -1;
5496 }
5497
5498 // If generator was found
5499 global $db; // Required to solve a conception default making an include of code using $db instead of $this->db just after.
5500
5501 require_once $file;
5502
5503 $obj = new $classname($this->db);
5504
5505 // If generator is ODT, we must have srctemplatepath defined, if not we set it.
5506 if ($obj->type == 'odt' && empty($srctemplatepath)) {
5507 $varfortemplatedir = $obj->scandir;
5508 if ($varfortemplatedir && !empty($conf->global->$varfortemplatedir)) {
5509 $dirtoscan = $conf->global->$varfortemplatedir;
5510
5511 $listoffiles = array();
5512
5513 // Now we add first model found in directories scanned
5514 $listofdir = explode(',', $dirtoscan);
5515 foreach ($listofdir as $key => $tmpdir) {
5516 $tmpdir = trim($tmpdir);
5517 $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
5518 if (!$tmpdir) {
5519 unset($listofdir[$key]);
5520 continue;
5521 }
5522 if (is_dir($tmpdir)) {
5523 $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
5524 if (count($tmpfiles)) {
5525 $listoffiles = array_merge($listoffiles, $tmpfiles);
5526 }
5527 }
5528 }
5529
5530 if (count($listoffiles)) {
5531 foreach ($listoffiles as $record) {
5532 $srctemplatepath = $record['fullname'];
5533 break;
5534 }
5535 }
5536 }
5537
5538 if (empty($srctemplatepath)) {
5539 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
5540 return -1;
5541 }
5542 }
5543
5544 if ($obj->type == 'odt' && !empty($srctemplatepath)) {
5545 if (!dol_is_file($srctemplatepath)) {
5546 dol_syslog("Failed to locate template file ".$srctemplatepath, LOG_WARNING);
5547 $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
5548 return -1;
5549 }
5550 }
5551
5552 // We save charset_output to restore it because write_file can change it if needed for
5553 // output format that does not support UTF8.
5554 $sav_charset_output = empty($outputlangs->charset_output) ? '' : $outputlangs->charset_output;
5555
5556 if (in_array(get_class($this), array('Adherent'))) {
5557 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, 'tmp_cards', $moreparams);
5558 } else {
5559 $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
5560 }
5561 // After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
5562
5563 if ($resultwritefile > 0) {
5564 $outputlangs->charset_output = $sav_charset_output;
5565
5566 // We delete old preview
5567 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5568 dol_delete_preview($this);
5569
5570 // Index file in database
5571 if (!empty($obj->result['fullpath'])) {
5572 $destfull = $obj->result['fullpath'];
5573
5574 // Update the last_main_doc field into main object (if document generator has property ->update_main_doc_field set)
5575 $update_main_doc_field = 0;
5576 if (!empty($obj->update_main_doc_field)) {
5577 $update_main_doc_field = 1;
5578 }
5579
5580 // Check that the file exists, before indexing it.
5581 // Hint: It does not exist, if we create a PDF and auto delete the ODT File
5582 if (dol_is_file($destfull)) {
5583 $this->indexFile($destfull, $update_main_doc_field);
5584 }
5585 } else {
5586 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);
5587 }
5588
5589 // Success in building document. We build meta file.
5590 dol_meta_create($this);
5591
5592 return 1;
5593 } else {
5594 $outputlangs->charset_output = $sav_charset_output;
5595 $this->error = $obj->error;
5596 $this->errors = $obj->errors;
5597 dol_syslog("Error generating document for ".__CLASS__.". Error: ".$obj->error, LOG_ERR);
5598 return -1;
5599 }
5600 }
5601
5611 public function indexFile($destfull, $update_main_doc_field)
5612 {
5613 global $conf, $user;
5614
5615 $upload_dir = dirname($destfull);
5616 $destfile = basename($destfull);
5617 $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
5618
5619 if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a tmp dir
5620 $filename = basename($destfile);
5621 $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
5622 $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
5623
5624 include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
5625 $ecmfile = new EcmFiles($this->db);
5626 $result = $ecmfile->fetch(0, '', ($rel_dir ? $rel_dir.'/' : '').$filename);
5627
5628 // Set the public "share" key
5629 $setsharekey = false;
5630 if ($this->element == 'propal' || $this->element == 'proposal') {
5631 if (!isset($conf->global->PROPOSAL_ALLOW_ONLINESIGN) || !empty($conf->global->PROPOSAL_ALLOW_ONLINESIGN)) {
5632 $setsharekey = true; // feature to make online signature is not set or set to on (default)
5633 }
5634 if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) {
5635 $setsharekey = true;
5636 }
5637 }
5638 if ($this->element == 'commande' && !empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD)) {
5639 $setsharekey = true;
5640 }
5641 if ($this->element == 'facture' && !empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD)) {
5642 $setsharekey = true;
5643 }
5644 if ($this->element == 'bank_account' && !empty($conf->global->BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD)) {
5645 $setsharekey = true;
5646 }
5647 if ($this->element == 'product' && !empty($conf->global->PRODUCT_ALLOW_EXTERNAL_DOWNLOAD)) {
5648 $setsharekey = true;
5649 }
5650 if ($this->element == 'contrat' && !empty($conf->global->CONTRACT_ALLOW_EXTERNAL_DOWNLOAD)) {
5651 $setsharekey = true;
5652 }
5653 if ($this->element == 'fichinter' && !empty($conf->global->FICHINTER_ALLOW_EXTERNAL_DOWNLOAD)) {
5654 $setsharekey = true;
5655 }
5656 if ($this->element == 'supplier_proposal' && !empty($conf->global->SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) {
5657 $setsharekey = true;
5658 }
5659
5660 if ($setsharekey) {
5661 if (empty($ecmfile->share)) { // Because object not found or share not set yet
5662 require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
5663 $ecmfile->share = getRandomPassword(true);
5664 }
5665 }
5666
5667 if ($result > 0) {
5668 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5669 $ecmfile->fullpath_orig = '';
5670 $ecmfile->gen_or_uploaded = 'generated';
5671 $ecmfile->description = ''; // indexed content
5672 $ecmfile->keywords = ''; // keyword content
5673 $result = $ecmfile->update($user);
5674 if ($result < 0) {
5675 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5676 return -1;
5677 }
5678 } else {
5679 $ecmfile->entity = $conf->entity;
5680 $ecmfile->filepath = $rel_dir;
5681 $ecmfile->filename = $filename;
5682 $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5683 $ecmfile->fullpath_orig = '';
5684 $ecmfile->gen_or_uploaded = 'generated';
5685 $ecmfile->description = ''; // indexed content
5686 $ecmfile->keywords = ''; // keyword content
5687 $ecmfile->src_object_type = $this->table_element; // $this->table_name is 'myobject' or 'mymodule_myobject'.
5688 $ecmfile->src_object_id = $this->id;
5689
5690 $result = $ecmfile->create($user);
5691 if ($result < 0) {
5692 setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5693 return -1;
5694 }
5695 }
5696
5697 /*$this->result['fullname']=$destfull;
5698 $this->result['filepath']=$ecmfile->filepath;
5699 $this->result['filename']=$ecmfile->filename;*/
5700 //var_dump($obj->update_main_doc_field);exit;
5701
5702 if ($update_main_doc_field && !empty($this->table_element)) {
5703 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath."/".$ecmfile->filename)."'";
5704 $sql .= " WHERE rowid = ".((int) $this->id);
5705
5706 $resql = $this->db->query($sql);
5707 if (!$resql) {
5708 dol_print_error($this->db);
5709 return -1;
5710 } else {
5711 $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
5712 }
5713 }
5714 }
5715
5716 return 1;
5717 }
5718
5726 public function addThumbs($file)
5727 {
5728 $file_osencoded = dol_osencode($file);
5729
5730 if (file_exists($file_osencoded)) {
5731 require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
5732
5733 $tmparraysize = getDefaultImageSizes();
5734 $maxwidthsmall = $tmparraysize['maxwidthsmall'];
5735 $maxheightsmall = $tmparraysize['maxheightsmall'];
5736 $maxwidthmini = $tmparraysize['maxwidthmini'];
5737 $maxheightmini = $tmparraysize['maxheightmini'];
5738 //$quality = $tmparraysize['quality'];
5739 $quality = 50; // For thumbs, we force quality to 50
5740
5741 // Create small thumbs for company (Ratio is near 16/9)
5742 // Used on logon for example
5743 vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
5744
5745 // Create mini thumbs for company (Ratio is near 16/9)
5746 // Used on menu or for setup page for example
5747 vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
5748 }
5749 }
5750
5758 public function delThumbs($file)
5759 {
5760 $imgThumbName = getImageFileNameForSize($file, '_small'); // Full path of thumb file
5761 dol_delete_file($imgThumbName);
5762 $imgThumbName = getImageFileNameForSize($file, '_mini'); // Full path of thumb file
5763 dol_delete_file($imgThumbName);
5764 }
5765
5766
5767 /* Functions common to commonobject and commonobjectline */
5768
5769 /* For default values */
5770
5784 public function getDefaultCreateValueFor($fieldname, $alternatevalue = null, $type = 'alphanohtml')
5785 {
5786 global $conf, $_POST;
5787
5788 // If param here has been posted, we use this value first.
5789 if (GETPOSTISSET($fieldname)) {
5790 return GETPOST($fieldname, $type, 3);
5791 }
5792
5793 if (isset($alternatevalue)) {
5794 return $alternatevalue;
5795 }
5796
5797 $newelement = $this->element;
5798 if ($newelement == 'facture') {
5799 $newelement = 'invoice';
5800 }
5801 if ($newelement == 'commande') {
5802 $newelement = 'order';
5803 }
5804 if (empty($newelement)) {
5805 dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
5806 return '';
5807 }
5808
5809 $keyforfieldname = strtoupper($newelement.'_DEFAULT_'.$fieldname);
5810 //var_dump($keyforfieldname);
5811 if (isset($conf->global->$keyforfieldname)) {
5812 return $conf->global->$keyforfieldname;
5813 }
5814
5815 // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
5816 // store content into $conf->cache['overwrite_default']
5817
5818 return '';
5819 }
5820
5821
5822 /* For triggers */
5823
5824
5825 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5836 public function call_trigger($triggerName, $user)
5837 {
5838 // phpcs:enable
5839 global $langs, $conf;
5840 if (!empty(self::TRIGGER_PREFIX) && strpos($triggerName, self::TRIGGER_PREFIX . '_') !== 0) {
5841 dol_print_error('', 'The trigger "' . $triggerName . '" does not start with "' . self::TRIGGER_PREFIX . '_" as required.');
5842 exit;
5843 }
5844 if (!is_object($langs)) { // If lang was not defined, we set it. It is required by run_triggers.
5845 include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5846 $langs = new Translate('', $conf);
5847 }
5848
5849 include_once DOL_DOCUMENT_ROOT.'/core/class/interfaces.class.php';
5850 $interface = new Interfaces($this->db);
5851 $result = $interface->run_triggers($triggerName, $this, $user, $langs, $conf);
5852
5853 if ($result < 0) {
5854 if (!empty($this->errors)) {
5855 $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.
5856 } else {
5857 $this->errors = $interface->errors;
5858 }
5859 }
5860 return $result;
5861 }
5862
5863
5864 /* Functions for data in other language */
5865
5866
5875 {
5876 // To avoid SQL errors. Probably not the better solution though
5877 if (!$this->element) {
5878 return 0;
5879 }
5880 if (!($this->id > 0)) {
5881 return 0;
5882 }
5883 if (is_array($this->array_languages)) {
5884 return 1;
5885 }
5886
5887 $this->array_languages = array();
5888
5889 $element = $this->element;
5890 if ($element == 'categorie') {
5891 $element = 'categories'; // For compatibility
5892 }
5893
5894 // Request to get translation values for object
5895 $sql = "SELECT rowid, property, lang , value";
5896 $sql .= " FROM ".$this->db->prefix()."object_lang";
5897 $sql .= " WHERE type_object = '".$this->db->escape($element)."'";
5898 $sql .= " AND fk_object = ".((int) $this->id);
5899
5900 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
5901 $resql = $this->db->query($sql);
5902 if ($resql) {
5903 $numrows = $this->db->num_rows($resql);
5904 if ($numrows) {
5905 $i = 0;
5906 while ($i < $numrows) {
5907 $obj = $this->db->fetch_object($resql);
5908 $key = $obj->property;
5909 $value = $obj->value;
5910 $codelang = $obj->lang;
5911 $type = $this->fields[$key]['type'];
5912
5913 // we can add this attribute to object
5914 if (preg_match('/date/', $type)) {
5915 $this->array_languages[$key][$codelang] = $this->db->jdate($value);
5916 } else {
5917 $this->array_languages[$key][$codelang] = $value;
5918 }
5919
5920 $i++;
5921 }
5922 }
5923
5924 $this->db->free($resql);
5925
5926 if ($numrows) {
5927 return $numrows;
5928 } else {
5929 return 0;
5930 }
5931 } else {
5932 dol_print_error($this->db);
5933 return -1;
5934 }
5935 }
5936
5943 public function setValuesForExtraLanguages($onlykey = '')
5944 {
5945 global $_POST, $langs;
5946
5947 // Get extra fields
5948 foreach ($_POST as $postfieldkey => $postfieldvalue) {
5949 $tmparray = explode('-', $postfieldkey);
5950 if ($tmparray[0] != 'field') {
5951 continue;
5952 }
5953
5954 $element = $tmparray[1];
5955 $key = $tmparray[2];
5956 $codelang = $tmparray[3];
5957 //var_dump("postfieldkey=".$postfieldkey." element=".$element." key=".$key." codelang=".$codelang);
5958
5959 if (!empty($onlykey) && $key != $onlykey) {
5960 continue;
5961 }
5962 if ($element != $this->element) {
5963 continue;
5964 }
5965
5966 $key_type = $this->fields[$key]['type'];
5967
5968 $enabled = 1;
5969 if (isset($this->fields[$key]['enabled'])) {
5970 $enabled = dol_eval($this->fields[$key]['enabled'], 1, 1, '1');
5971 }
5972 /*$perms = 1;
5973 if (isset($this->fields[$key]['perms']))
5974 {
5975 $perms = dol_eval($this->fields[$key]['perms'], 1, 1, '1');
5976 }*/
5977 if (empty($enabled)) {
5978 continue;
5979 }
5980 //if (empty($perms)) continue;
5981
5982 if (in_array($key_type, array('date'))) {
5983 // Clean parameters
5984 // TODO GMT date in memory must be GMT so we should add gm=true in parameters
5985 $value_key = dol_mktime(0, 0, 0, GETPOST($postfieldkey."month", 'int'), GETPOST($postfieldkey."day", 'int'), GETPOST($postfieldkey."year", 'int'));
5986 } elseif (in_array($key_type, array('datetime'))) {
5987 // Clean parameters
5988 // TODO GMT date in memory must be GMT so we should add gm=true in parameters
5989 $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'));
5990 } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
5991 $value_arr = GETPOST($postfieldkey, 'array'); // check if an array
5992 if (!empty($value_arr)) {
5993 $value_key = implode(',', $value_arr);
5994 } else {
5995 $value_key = '';
5996 }
5997 } elseif (in_array($key_type, array('price', 'double'))) {
5998 $value_arr = GETPOST($postfieldkey, 'alpha');
5999 $value_key = price2num($value_arr);
6000 } else {
6001 $value_key = GETPOST($postfieldkey);
6002 if (in_array($key_type, array('link')) && $value_key == '-1') {
6003 $value_key = '';
6004 }
6005 }
6006
6007 $this->array_languages[$key][$codelang] = $value_key;
6008
6009 /*if ($nofillrequired) {
6010 $langs->load('errors');
6011 setEventMessages($langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required), null, 'errors');
6012 return -1;
6013 }*/
6014 }
6015
6016 return 1;
6017 }
6018
6019
6020 /* Functions for extrafields */
6021
6028 public function fetchNoCompute($id)
6029 {
6030 global $conf;
6031
6032 $savDisableCompute = $conf->disable_compute;
6033 $conf->disable_compute = 1;
6034
6035 $ret = $this->fetch($id);
6036
6037 $conf->disable_compute = $savDisableCompute;
6038
6039 return $ret;
6040 }
6041
6042 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
6052 public function fetch_optionals($rowid = null, $optionsArray = null)
6053 {
6054 // phpcs:enable
6055 global $conf, $extrafields;
6056
6057 if (empty($rowid)) {
6058 $rowid = $this->id;
6059 }
6060 if (empty($rowid) && isset($this->rowid)) {
6061 $rowid = $this->rowid; // deprecated
6062 }
6063
6064 // To avoid SQL errors. Probably not the better solution though
6065 if (!$this->table_element) {
6066 return 0;
6067 }
6068
6069 $this->array_options = array();
6070
6071 if (!is_array($optionsArray)) {
6072 // If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
6073 if (!isset($extrafields) || !is_object($extrafields)) {
6074 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6075 $extrafields = new ExtraFields($this->db);
6076 }
6077
6078 // Load array of extrafields for elementype = $this->table_element
6079 if (empty($extrafields->attributes[$this->table_element]['loaded'])) {
6080 $extrafields->fetch_name_optionals_label($this->table_element);
6081 }
6082 $optionsArray = (!empty($extrafields->attributes[$this->table_element]['label']) ? $extrafields->attributes[$this->table_element]['label'] : null);
6083 } else {
6084 global $extrafields;
6085 dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
6086 }
6087
6088 $table_element = $this->table_element;
6089 if ($table_element == 'categorie') {
6090 $table_element = 'categories'; // For compatibility
6091 }
6092
6093 // Request to get complementary values
6094 if (is_array($optionsArray) && count($optionsArray) > 0) {
6095 $sql = "SELECT rowid";
6096 foreach ($optionsArray as $name => $label) {
6097 if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate') {
6098 $sql .= ", ".$name;
6099 }
6100 }
6101 $sql .= " FROM ".$this->db->prefix().$table_element."_extrafields";
6102 $sql .= " WHERE fk_object = ".((int) $rowid);
6103
6104 //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6105 $resql = $this->db->query($sql);
6106 if ($resql) {
6107 $numrows = $this->db->num_rows($resql);
6108 if ($numrows) {
6109 $tab = $this->db->fetch_array($resql);
6110
6111 foreach ($tab as $key => $value) {
6112 // 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)
6113 if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && !is_int($key)) {
6114 // we can add this attribute to object
6115 if (!empty($extrafields->attributes[$this->table_element]) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date', 'datetime'))) {
6116 //var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
6117 $this->array_options["options_".$key] = $this->db->jdate($value);
6118 } else {
6119 $this->array_options["options_".$key] = $value;
6120 }
6121
6122 //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
6123 }
6124 }
6125 }
6126
6127 // If field is a computed field, value must become result of compute (regardless of whether a row exists
6128 // in the element's extrafields table)
6129 if (is_array($extrafields->attributes[$this->table_element]['label'])) {
6130 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
6131 if (!empty($extrafields->attributes[$this->table_element]) && !empty($extrafields->attributes[$this->table_element]['computed'][$key])) {
6132 //var_dump($conf->disable_compute);
6133 if (empty($conf->disable_compute)) {
6134 global $objectoffield; // We set a global variable to $objectoffield so
6135 $objectoffield = $this; // we can use it inside computed formula
6136 $this->array_options['options_' . $key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0, '');
6137 }
6138 }
6139 }
6140 }
6141
6142 $this->db->free($resql);
6143
6144 if ($numrows) {
6145 return $numrows;
6146 } else {
6147 return 0;
6148 }
6149 } else {
6150 $this->errors[]=$this->db->lasterror;
6151 return -1;
6152 }
6153 }
6154 return 0;
6155 }
6156
6163 public function deleteExtraFields()
6164 {
6165 global $conf;
6166
6167 if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) {
6168 return 0;
6169 }
6170
6171 $this->db->begin();
6172
6173 $table_element = $this->table_element;
6174 if ($table_element == 'categorie') {
6175 $table_element = 'categories'; // For compatibility
6176 }
6177
6178 dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
6179
6180 $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6181
6182 $resql = $this->db->query($sql_del);
6183 if (!$resql) {
6184 $this->error = $this->db->lasterror();
6185 $this->db->rollback();
6186 return -1;
6187 } else {
6188 $this->db->commit();
6189 return 1;
6190 }
6191 }
6192
6203 public function insertExtraFields($trigger = '', $userused = null)
6204 {
6205 global $conf, $langs, $user;
6206
6207 if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) {
6208 return 0;
6209 }
6210
6211 if (empty($userused)) {
6212 $userused = $user;
6213 }
6214
6215 $error = 0;
6216
6217 if (!empty($this->array_options)) {
6218 // Check parameters
6219 $langs->load('admin');
6220 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6221 $extrafields = new ExtraFields($this->db);
6222 $target_extrafields = $extrafields->fetch_name_optionals_label($this->table_element);
6223
6224 // Eliminate copied source object extra fields that do not exist in target object
6225 $new_array_options = array();
6226 foreach ($this->array_options as $key => $value) {
6227 if (in_array(substr($key, 8), array_keys($target_extrafields))) { // We remove the 'options_' from $key for test
6228 $new_array_options[$key] = $value;
6229 } elseif (in_array($key, array_keys($target_extrafields))) { // We test on $key that does not contain the 'options_' prefix
6230 $new_array_options['options_'.$key] = $value;
6231 }
6232 }
6233
6234 foreach ($new_array_options as $key => $value) {
6235 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6236 $attributeType = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
6237 $attributeLabel = $langs->transnoentities($extrafields->attributes[$this->table_element]['label'][$attributeKey]);
6238 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
6239 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
6240 $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
6241 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$attributeKey];
6242
6243 // If we clone, we have to clean unique extrafields to prevent duplicates.
6244 // This behaviour can be prevented by external code by changing $this->context['createfromclone'] value in createFrom hook
6245 if (!empty($this->context['createfromclone']) && $this->context['createfromclone'] == 'createfromclone' && !empty($attributeUnique)) {
6246 $new_array_options[$key] = null;
6247 }
6248
6249 // Similar code than into insertExtraFields
6250 if ($attributeRequired) {
6251 $mandatorypb = false;
6252 if ($attributeType == 'link' && $this->array_options[$key] == '-1') {
6253 $mandatorypb = true;
6254 }
6255 if ($this->array_options[$key] === '') {
6256 $mandatorypb = true;
6257 }
6258 if ($attributeType == 'sellist' && $this->array_options[$key] == '0') {
6259 $mandatorypb = true;
6260 }
6261 if ($mandatorypb) {
6262 $langs->load("errors");
6263 dol_syslog("Mandatory field '".$key."' is empty during create and set to required into definition of extrafields");
6264 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6265 return -1;
6266 }
6267 }
6268
6269 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6270 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6271
6272 if (!empty($attrfieldcomputed)) {
6273 if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS)) {
6274 $value = dol_eval($attrfieldcomputed, 1, 0, '');
6275 dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
6276 $new_array_options[$key] = $value;
6277 } else {
6278 $new_array_options[$key] = null;
6279 }
6280 }
6281
6282 switch ($attributeType) {
6283 case 'int':
6284 if (!is_numeric($value) && $value != '') {
6285 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6286 return -1;
6287 } elseif ($value == '') {
6288 $new_array_options[$key] = null;
6289 }
6290 break;
6291 case 'price':
6292 case 'double':
6293 $value = price2num($value);
6294 if (!is_numeric($value) && $value != '') {
6295 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." for ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6296 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6297 return -1;
6298 } elseif ($value == '') {
6299 $value = null;
6300 }
6301 //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6302 $new_array_options[$key] = $value;
6303 break;
6304 /*case 'select': // Not required, we chosed value='0' for undefined values
6305 if ($value=='-1')
6306 {
6307 $this->array_options[$key] = null;
6308 }
6309 break;*/
6310 case 'password':
6311 $algo = '';
6312 if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
6313 // If there is an encryption choice, we use it to crypt data before insert
6314 $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
6315 $algo = reset($tmparrays);
6316 if ($algo != '') {
6317 //global $action; // $action may be 'create', 'update', 'update_extras'...
6318 //var_dump($action);
6319 //var_dump($this->oldcopy);exit;
6320 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
6321 //var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]);
6322 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.
6323 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6324 } else {
6325 // var_dump($algo);
6326 $newvalue = dol_hash($this->array_options[$key], $algo);
6327 $new_array_options[$key] = $newvalue;
6328 }
6329 } else {
6330 $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6331 }
6332 }
6333 } else // Common usage
6334 {
6335 $new_array_options[$key] = $this->array_options[$key];
6336 }
6337 break;
6338 case 'date':
6339 case 'datetime':
6340 // If data is a string instead of a timestamp, we convert it
6341 if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6342 $this->array_options[$key] = strtotime($this->array_options[$key]);
6343 }
6344 $new_array_options[$key] = $this->db->idate($this->array_options[$key]);
6345 break;
6346 case 'datetimegmt':
6347 // If data is a string instead of a timestamp, we convert it
6348 if (!is_numeric($this->array_options[$key]) || $this->array_options[$key] != intval($this->array_options[$key])) {
6349 $this->array_options[$key] = strtotime($this->array_options[$key]);
6350 }
6351 $new_array_options[$key] = $this->db->idate($this->array_options[$key], 'gmt');
6352 break;
6353 case 'link':
6354 $param_list = array_keys($attributeParam['options']);
6355 // 0 : ObjectName
6356 // 1 : classPath
6357 $InfoFieldList = explode(":", $param_list[0]);
6358 dol_include_once($InfoFieldList[1]);
6359 if ($InfoFieldList[0] && class_exists($InfoFieldList[0])) {
6360 if ($value == '-1') { // -1 is key for no defined in combo list of objects
6361 $new_array_options[$key] = '';
6362 } elseif ($value) {
6363 $object = new $InfoFieldList[0]($this->db);
6364 if (is_numeric($value)) {
6365 $res = $object->fetch($value); // Common case
6366 } else {
6367 $res = $object->fetch('', $value); // For compatibility
6368 }
6369
6370 if ($res > 0) {
6371 $new_array_options[$key] = $object->id;
6372 } else {
6373 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
6374 return -1;
6375 }
6376 }
6377 } else {
6378 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6379 }
6380 break;
6381 case 'checkbox':
6382 case 'chkbxlst':
6383 if (is_array($this->array_options[$key])) {
6384 $new_array_options[$key] = implode(',', $this->array_options[$key]);
6385 } else {
6386 $new_array_options[$key] = $this->array_options[$key];
6387 }
6388 break;
6389 }
6390 }
6391
6392 $this->db->begin();
6393
6394 $table_element = $this->table_element;
6395 if ($table_element == 'categorie') {
6396 $table_element = 'categories'; // For compatibility
6397 }
6398
6399 dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG);
6400
6401 $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6402 $this->db->query($sql_del);
6403
6404 $sql = "INSERT INTO ".$this->db->prefix().$table_element."_extrafields (fk_object";
6405 foreach ($new_array_options as $key => $value) {
6406 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6407 // Add field of attribut
6408 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator
6409 $sql .= ",".$attributeKey;
6410 }
6411 }
6412 // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6413 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6414 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6415 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6416 $sql .= ",".$tmpkey;
6417 }
6418 }
6419 }
6420 $sql .= ") VALUES (".$this->id;
6421
6422 foreach ($new_array_options as $key => $value) {
6423 $attributeKey = substr($key, 8); // Remove 'options_' prefix
6424 // Add field of attribute
6425 if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator)
6426 if ($new_array_options[$key] != '' || $new_array_options[$key] == '0') {
6427 $sql .= ",'".$this->db->escape($new_array_options[$key])."'";
6428 } else {
6429 $sql .= ",null";
6430 }
6431 }
6432 }
6433 // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6434 if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6435 foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6436 if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6437 if (in_array($tmpval, array('int', 'double', 'price'))) {
6438 $sql .= ", 0";
6439 } else {
6440 $sql .= ", ''";
6441 }
6442 }
6443 }
6444 }
6445
6446 $sql .= ")";
6447
6448 $resql = $this->db->query($sql);
6449 if (!$resql) {
6450 $this->error = $this->db->lasterror();
6451 $error++;
6452 }
6453
6454 if (!$error && $trigger) {
6455 // Call trigger
6456 $this->context = array('extrafieldaddupdate'=>1);
6457 $result = $this->call_trigger($trigger, $userused);
6458 if ($result < 0) {
6459 $error++;
6460 }
6461 // End call trigger
6462 }
6463
6464 if ($error) {
6465 $this->db->rollback();
6466 return -1;
6467 } else {
6468 $this->db->commit();
6469 return 1;
6470 }
6471 } else {
6472 return 0;
6473 }
6474 }
6475
6486 public function insertExtraLanguages($trigger = '', $userused = null)
6487 {
6488 global $conf, $langs, $user;
6489
6490 if (empty($userused)) {
6491 $userused = $user;
6492 }
6493
6494 $error = 0;
6495
6496 if (!empty($conf->global->MAIN_EXTRALANGUAGES_DISABLED)) {
6497 return 0; // For avoid conflicts if trigger used
6498 }
6499
6500 if (is_array($this->array_languages)) {
6501 $new_array_languages = $this->array_languages;
6502
6503 foreach ($new_array_languages as $key => $value) {
6504 $attributeKey = $key;
6505 $attributeType = $this->fields[$attributeKey]['type'];
6506 $attributeLabel = $this->fields[$attributeKey]['label'];
6507
6508 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6509 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6510
6511 switch ($attributeType) {
6512 case 'int':
6513 if (!is_numeric($value) && $value != '') {
6514 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6515 return -1;
6516 } elseif ($value == '') {
6517 $new_array_languages[$key] = null;
6518 }
6519 break;
6520 case 'double':
6521 $value = price2num($value);
6522 if (!is_numeric($value) && $value != '') {
6523 dol_syslog($langs->trans("ExtraLanguageHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6524 $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6525 return -1;
6526 } elseif ($value == '') {
6527 $new_array_languages[$key] = null;
6528 } else {
6529 $new_array_languages[$key] = $value;
6530 }
6531 break;
6532 /*case 'select': // Not required, we chosed value='0' for undefined values
6533 if ($value=='-1')
6534 {
6535 $this->array_options[$key] = null;
6536 }
6537 break;*/
6538 }
6539 }
6540
6541 $this->db->begin();
6542
6543 $table_element = $this->table_element;
6544 if ($table_element == 'categorie') {
6545 $table_element = 'categories'; // For compatibility
6546 }
6547
6548 dol_syslog(get_class($this)."::insertExtraLanguages delete then insert", LOG_DEBUG);
6549
6550 foreach ($new_array_languages as $key => $langcodearray) { // $key = 'name', 'town', ...
6551 foreach ($langcodearray as $langcode => $value) {
6552 $sql_del = "DELETE FROM ".$this->db->prefix()."object_lang";
6553 $sql_del .= " WHERE fk_object = ".((int) $this->id)." AND property = '".$this->db->escape($key)."' AND type_object = '".$this->db->escape($table_element)."'";
6554 $sql_del .= " AND lang = '".$this->db->escape($langcode)."'";
6555 $this->db->query($sql_del);
6556
6557 if ($value !== '') {
6558 $sql = "INSERT INTO ".$this->db->prefix()."object_lang (fk_object, property, type_object, lang, value";
6559 $sql .= ") VALUES (".$this->id.", '".$this->db->escape($key)."', '".$this->db->escape($table_element)."', '".$this->db->escape($langcode)."', '".$this->db->escape($value)."'";
6560 $sql .= ")";
6561
6562 $resql = $this->db->query($sql);
6563 if (!$resql) {
6564 $this->error = $this->db->lasterror();
6565 $error++;
6566 break;
6567 }
6568 }
6569 }
6570 }
6571
6572 if (!$error && $trigger) {
6573 // Call trigger
6574 $this->context = array('extralanguagesaddupdate'=>1);
6575 $result = $this->call_trigger($trigger, $userused);
6576 if ($result < 0) {
6577 $error++;
6578 }
6579 // End call trigger
6580 }
6581
6582 if ($error) {
6583 $this->db->rollback();
6584 return -1;
6585 } else {
6586 $this->db->commit();
6587 return 1;
6588 }
6589 } else {
6590 return 0;
6591 }
6592 }
6593
6604 public function updateExtraField($key, $trigger = null, $userused = null)
6605 {
6606 global $conf, $langs, $user;
6607
6608 if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) {
6609 return 0;
6610 }
6611
6612 if (empty($userused)) {
6613 $userused = $user;
6614 }
6615
6616 $error = 0;
6617
6618 if (!empty($this->array_options) && isset($this->array_options["options_".$key])) {
6619 // Check parameters
6620 $langs->load('admin');
6621 require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6622 $extrafields = new ExtraFields($this->db);
6623 $extrafields->fetch_name_optionals_label($this->table_element);
6624
6625 $value = $this->array_options["options_".$key];
6626
6627 $attributeType = $extrafields->attributes[$this->table_element]['type'][$key];
6628 $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key];
6629 $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key];
6630 $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
6631 $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$key];
6632
6633 // Similar code than into insertExtraFields
6634 if ($attributeRequired) {
6635 $mandatorypb = false;
6636 if ($attributeType == 'link' && $this->array_options["options_".$key] == '-1') {
6637 $mandatorypb = true;
6638 }
6639 if ($this->array_options["options_".$key] === '') {
6640 $mandatorypb = true;
6641 }
6642 if ($mandatorypb) {
6643 $langs->load("errors");
6644 dol_syslog("Mandatory field 'options_".$key."' is empty during update and set to required into definition of extrafields");
6645 $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6646 return -1;
6647 }
6648 }
6649
6650 //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6651 //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6652
6653 if (!empty($attrfieldcomputed)) {
6654 if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS)) {
6655 $value = dol_eval($attrfieldcomputed, 1, 0, '');
6656 dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
6657 $this->array_options["options_".$key] = $value;
6658 } else {
6659 $this->array_options["options_".$key] = null;
6660 }
6661 }
6662
6663 switch ($attributeType) {
6664 case 'int':
6665 if (!is_numeric($value) && $value != '') {
6666 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6667 return -1;
6668 } elseif ($value === '') {
6669 $this->array_options["options_".$key] = null;
6670 }
6671 break;
6672 case 'price':
6673 case 'double':
6674 $value = price2num($value);
6675 if (!is_numeric($value) && $value != '') {
6676 dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6677 $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6678 return -1;
6679 } elseif ($value === '') {
6680 $value = null;
6681 }
6682 //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6683 $this->array_options["options_".$key] = $value;
6684 break;
6685 /*case 'select': // Not required, we chosed value='0' for undefined values
6686 if ($value=='-1')
6687 {
6688 $this->array_options[$key] = null;
6689 }
6690 break;*/
6691 case 'date':
6692 case 'datetime':
6693 if (empty($this->array_options["options_".$key])) {
6694 $this->array_options["options_".$key] = null;
6695 } else {
6696 $this->array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key]);
6697 }
6698 break;
6699 case 'datetimegmt':
6700 if (empty($this->array_options["options_".$key])) {
6701 $this->array_options["options_".$key] = null;
6702 } else {
6703 $this->array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key], 'gmt');
6704 }
6705 break;
6706 case 'boolean':
6707 if (empty($this->array_options["options_".$key])) {
6708 $this->array_options["options_".$key] = null;
6709 }
6710 break;
6711 case 'link':
6712 if ($this->array_options["options_".$key] === '') {
6713 $this->array_options["options_".$key] = null;
6714 }
6715 break;
6716 /*
6717 case 'link':
6718 $param_list = array_keys($attributeParam['options']);
6719 // 0 : ObjectName
6720 // 1 : classPath
6721 $InfoFieldList = explode(":", $param_list[0]);
6722 dol_include_once($InfoFieldList[1]);
6723 if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
6724 {
6725 if ($value == '-1') // -1 is key for no defined in combo list of objects
6726 {
6727 $new_array_options[$key] = '';
6728 } elseif ($value) {
6729 $object = new $InfoFieldList[0]($this->db);
6730 if (is_numeric($value)) $res = $object->fetch($value); // Common case
6731 else $res = $object->fetch('', $value); // For compatibility
6732
6733 if ($res > 0) $new_array_options[$key] = $object->id;
6734 else {
6735 $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
6736 $this->db->rollback();
6737 return -1;
6738 }
6739 }
6740 } else {
6741 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6742 }
6743 break;
6744 */
6745 case 'checkbox':
6746 case 'chkbxlst':
6747 if (is_array($this->array_options[$key])) {
6748 $new_array_options[$key] = implode(',', $this->array_options[$key]);
6749 } else {
6750 $new_array_options[$key] = $this->array_options[$key];
6751 }
6752 break;
6753 }
6754
6755 $this->db->begin();
6756
6757 $linealreadyfound = 0;
6758
6759 // 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)
6760 $sql = "SELECT COUNT(rowid) as nb FROM ".$this->db->prefix().$this->table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6761 $resql = $this->db->query($sql);
6762 if ($resql) {
6763 $tmpobj = $this->db->fetch_object($resql);
6764 if ($tmpobj) {
6765 $linealreadyfound = $tmpobj->nb;
6766 }
6767 }
6768
6769 if ($linealreadyfound) {
6770 if ($this->array_options["options_".$key] === null) {
6771 $sql = "UPDATE ".$this->db->prefix().$this->table_element."_extrafields SET ".$key." = null";
6772 } else {
6773 $sql = "UPDATE ".$this->db->prefix().$this->table_element."_extrafields SET ".$key." = '".$this->db->escape($this->array_options["options_".$key])."'";
6774 }
6775 $sql .= " WHERE fk_object = ".((int) $this->id);
6776 } else {
6777 $result = $this->insertExtraFields('', $user);
6778 if ($result < 0) {
6779 $error++;
6780 }
6781 }
6782
6783 $resql = $this->db->query($sql);
6784 if (!$resql) {
6785 $error++;
6786 $this->error = $this->db->lasterror();
6787 }
6788 if (!$error && $trigger) {
6789 // Call trigger
6790 $this->context = array('extrafieldupdate'=>1);
6791 $result = $this->call_trigger($trigger, $userused);
6792 if ($result < 0) {
6793 $error++;
6794 }
6795 // End call trigger
6796 }
6797
6798 if ($error) {
6799 dol_syslog(__METHOD__.$this->error, LOG_ERR);
6800 $this->db->rollback();
6801 return -1;
6802 } else {
6803 $this->db->commit();
6804 return 1;
6805 }
6806 } else {
6807 return 0;
6808 }
6809 }
6810
6821 public function updateExtraLanguages($key, $trigger = null, $userused = null)
6822 {
6823 global $conf, $langs, $user;
6824
6825 if (empty($userused)) {
6826 $userused = $user;
6827 }
6828
6829 $error = 0;
6830
6831 if (!empty($conf->global->MAIN_EXTRALANGUAGES_DISABLED)) {
6832 return 0; // For avoid conflicts if trigger used
6833 }
6834
6835 return 0;
6836 }
6837
6838
6853 public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0, $nonewbutton = 0)
6854 {
6855 global $conf, $langs, $form;
6856
6857 if (!is_object($form)) {
6858 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
6859 $form = new Form($this->db);
6860 }
6861
6862 if (!empty($this->fields)) {
6863 $val = $this->fields[$key];
6864 }
6865
6866 // Validation tests and output
6867 $fieldValidationErrorMsg = '';
6868 $validationClass = '';
6869 $fieldValidationErrorMsg = $this->getFieldError($key);
6870 if (!empty($fieldValidationErrorMsg)) {
6871 $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
6872 } else {
6873 $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
6874 }
6875
6876 $out = '';
6877 $type = '';
6878 $isDependList = 0;
6879 $param = array();
6880 $param['options'] = array();
6881 $reg = array();
6882 $size = !empty($this->fields[$key]['size']) ? $this->fields[$key]['size'] : 0;
6883 // Because we work on extrafields
6884 if (preg_match('/^(integer|link):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6885 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
6886 $type = 'link';
6887 } elseif (preg_match('/^(integer|link):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6888 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
6889 $type = 'link';
6890 } elseif (preg_match('/^(integer|link):(.*):(.*)/i', $val['type'], $reg)) {
6891 $param['options'] = array($reg[2].':'.$reg[3] => 'N');
6892 $type = 'link';
6893 } elseif (preg_match('/^(sellist):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6894 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
6895 $type = 'sellist';
6896 } elseif (preg_match('/^(sellist):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6897 $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
6898 $type = 'sellist';
6899 } elseif (preg_match('/^(sellist):(.*):(.*)/i', $val['type'], $reg)) {
6900 $param['options'] = array($reg[2].':'.$reg[3] => 'N');
6901 $type = 'sellist';
6902 } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
6903 $param['options'] = array($reg[1] => 'N');
6904 $type = 'chkbxlst';
6905 } elseif (preg_match('/varchar\‍((\d+)\‍)/', $val['type'], $reg)) {
6906 $param['options'] = array();
6907 $type = 'varchar';
6908 $size = $reg[1];
6909 } elseif (preg_match('/varchar/', $val['type'])) {
6910 $param['options'] = array();
6911 $type = 'varchar';
6912 } else {
6913 $param['options'] = array();
6914 $type = $this->fields[$key]['type'];
6915 }
6916
6917 // Special case that force options and type ($type can be integer, varchar, ...)
6918 if (!empty($this->fields[$key]['arrayofkeyval']) && is_array($this->fields[$key]['arrayofkeyval'])) {
6919 $param['options'] = $this->fields[$key]['arrayofkeyval'];
6920 $type = 'select';
6921 }
6922
6923 $label = $this->fields[$key]['label'];
6924 //$elementtype=$this->fields[$key]['elementtype']; // Seems not used
6925 $default = (!empty($this->fields[$key]['default']) ? $this->fields[$key]['default'] : '');
6926 $computed = (!empty($this->fields[$key]['computed']) ? $this->fields[$key]['computed'] : '');
6927 $unique = (!empty($this->fields[$key]['unique']) ? $this->fields[$key]['unique'] : 0);
6928 $required = (!empty($this->fields[$key]['required']) ? $this->fields[$key]['required'] : 0);
6929 $autofocusoncreate = (!empty($this->fields[$key]['autofocusoncreate']) ? $this->fields[$key]['autofocusoncreate'] : 0);
6930
6931 $langfile = (!empty($this->fields[$key]['langfile']) ? $this->fields[$key]['langfile'] : '');
6932 $list = (!empty($this->fields[$key]['list']) ? $this->fields[$key]['list'] : 0);
6933 $hidden = (in_array(abs($this->fields[$key]['visible']), array(0, 2)) ? 1 : 0);
6934
6935 $objectid = $this->id;
6936
6937 if ($computed) {
6938 if (!preg_match('/^search_/', $keyprefix)) {
6939 return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
6940 } else {
6941 return '';
6942 }
6943 }
6944
6945 // Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine
6946 if (empty($morecss) && !empty($val['css'])) {
6947 $morecss = $val['css'];
6948 } elseif (empty($morecss)) {
6949 if ($type == 'date') {
6950 $morecss = 'minwidth100imp';
6951 } elseif ($type == 'datetime' || $type == 'link') { // link means an foreign key to another primary id
6952 $morecss = 'minwidth200imp';
6953 } elseif (in_array($type, array('int', 'integer', 'price')) || preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', $type)) {
6954 $morecss = 'maxwidth75';
6955 } elseif ($type == 'url') {
6956 $morecss = 'minwidth400';
6957 } elseif ($type == 'boolean') {
6958 $morecss = '';
6959 } else {
6960 if (round($size) < 12) {
6961 $morecss = 'minwidth100';
6962 } elseif (round($size) <= 48) {
6963 $morecss = 'minwidth200';
6964 } else {
6965 $morecss = 'minwidth400';
6966 }
6967 }
6968 }
6969
6970 // Add validation state class
6971 if (!empty($validationClass)) {
6972 $morecss.= $validationClass;
6973 }
6974
6975 if (in_array($type, array('date'))) {
6976 $tmp = explode(',', $size);
6977 $newsize = $tmp[0];
6978 $showtime = 0;
6979
6980 // Do not show current date when field not required (see selectDate() method)
6981 if (!$required && $value == '') {
6982 $value = '-1';
6983 }
6984
6985 // TODO Must also support $moreparam
6986 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
6987 } elseif (in_array($type, array('datetime'))) {
6988 $tmp = explode(',', $size);
6989 $newsize = $tmp[0];
6990 $showtime = 1;
6991
6992 // Do not show current date when field not required (see selectDate() method)
6993 if (!$required && $value == '') $value = '-1';
6994
6995 // TODO Must also support $moreparam
6996 $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
6997 } elseif (in_array($type, array('duration'))) {
6998 $out = $form->select_duration($keyprefix.$key.$keysuffix, $value, 0, 'text', 0, 1);
6999 } elseif (in_array($type, array('int', 'integer'))) {
7000 $tmp = explode(',', $size);
7001 $newsize = $tmp[0];
7002 $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' : '').'>';
7003 } elseif (in_array($type, array('real'))) {
7004 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7005 } elseif (preg_match('/varchar/', $type)) {
7006 $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' : '').'>';
7007 } elseif (in_array($type, array('email', 'mail', 'phone', 'url', 'ip'))) {
7008 $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
7009 } elseif (preg_match('/^text/', $type)) {
7010 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7011 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7012 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
7013 $out = $doleditor->Create(1);
7014 } else {
7015 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7016 }
7017 } elseif (preg_match('/^html/', $type)) {
7018 if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
7019 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
7020 $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, isModEnabled('fckeditor') && $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_5, '90%');
7021 $out = $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
7022 } else {
7023 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
7024 }
7025 } elseif ($type == 'boolean') {
7026 $checked = '';
7027 if (!empty($value)) {
7028 $checked = ' checked value="1" ';
7029 } else {
7030 $checked = ' value="1" ';
7031 }
7032 $out = '<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
7033 } elseif ($type == 'price') {
7034 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7035 $value = price($value);
7036 }
7037 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '.$langs->getCurrencySymbol($conf->currency);
7038 } elseif (preg_match('/^double(\‍([0-9],[0-9]\‍)){0,1}/', $type)) {
7039 if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
7040 $value = price($value);
7041 }
7042 $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
7043 } elseif ($type == 'select') { // combo list
7044 $out = '';
7045 if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_EXTRAFIELDS_DISABLE_SELECT2)) {
7046 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7047 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7048 }
7049
7050 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7051 if ((!isset($this->fields[$key]['default'])) || ($this->fields[$key]['notnull'] != 1)) {
7052 $out .= '<option value="0">&nbsp;</option>';
7053 }
7054 foreach ($param['options'] as $keyb => $valb) {
7055 if ((string) $keyb == '') {
7056 continue;
7057 }
7058 if (strpos($valb, "|") !== false) {
7059 list($valb, $parent) = explode('|', $valb);
7060 }
7061 $out .= '<option value="'.$keyb.'"';
7062 $out .= (((string) $value == (string) $keyb) ? ' selected' : '');
7063 $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
7064 $out .= '>'.$valb.'</option>';
7065 }
7066 $out .= '</select>';
7067 } elseif ($type == 'sellist') {
7068 $out = '';
7069 if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_EXTRAFIELDS_DISABLE_SELECT2)) {
7070 include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
7071 $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
7072 }
7073
7074 $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
7075 if (is_array($param['options'])) {
7076 $param_list = array_keys($param['options']);
7077 $InfoFieldList = explode(":", $param_list[0]);
7078 $parentName = '';
7079 $parentField = '';
7080 // 0 : tableName
7081 // 1 : label field name
7082 // 2 : key fields name (if differ of rowid)
7083 // 3 : key field parent (for dependent lists)
7084 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
7085 // 5 : id category type
7086 // 6 : ids categories list separated by comma for category root
7087 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7088
7089 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7090 if (strpos($InfoFieldList[4], 'extra.') !== false) {
7091 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7092 } else {
7093 $keyList = $InfoFieldList[2].' as rowid';
7094 }
7095 }
7096 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7097 list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7098 $keyList .= ', '.$parentField;
7099 }
7100
7101 $filter_categorie = false;
7102 if (count($InfoFieldList) > 5) {
7103 if ($InfoFieldList[0] == 'categorie') {
7104 $filter_categorie = true;
7105 }
7106 }
7107
7108 if ($filter_categorie === false) {
7109 $fields_label = explode('|', $InfoFieldList[1]);
7110 if (is_array($fields_label)) {
7111 $keyList .= ', ';
7112 $keyList .= implode(', ', $fields_label);
7113 }
7114
7115 $sqlwhere = '';
7116 $sql = "SELECT " . $keyList;
7117 $sql .= " FROM " . $this->db->prefix() . $InfoFieldList[0];
7118 if (!empty($InfoFieldList[4])) {
7119 // can use SELECT request
7120 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7121 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7122 }
7123
7124 // current object id can be use into filter
7125 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7126 $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
7127 } else {
7128 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7129 }
7130
7131 //We have to join on extrafield table
7132 if (strpos($InfoFieldList[4], 'extra') !== false) {
7133 $sql .= " as main, " . $this->db->prefix() . $InfoFieldList[0] . "_extrafields as extra";
7134 $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2] . " AND " . $InfoFieldList[4];
7135 } else {
7136 $sqlwhere .= " WHERE " . $InfoFieldList[4];
7137 }
7138 } else {
7139 $sqlwhere .= ' WHERE 1=1';
7140 }
7141 // Some tables may have field, some other not. For the moment we disable it.
7142 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7143 $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7144 }
7145 $sql .= $sqlwhere;
7146 //print $sql;
7147
7148 $sql .= ' ORDER BY ' . implode(', ', $fields_label);
7149
7150 dol_syslog(get_class($this) . '::showInputField type=sellist', LOG_DEBUG);
7151 $resql = $this->db->query($sql);
7152 if ($resql) {
7153 $out .= '<option value="0">&nbsp;</option>';
7154 $num = $this->db->num_rows($resql);
7155 $i = 0;
7156 while ($i < $num) {
7157 $labeltoshow = '';
7158 $obj = $this->db->fetch_object($resql);
7159
7160 // Several field into label (eq table:code|libelle:rowid)
7161 $notrans = false;
7162 $fields_label = explode('|', $InfoFieldList[1]);
7163 if (count($fields_label) > 1) {
7164 $notrans = true;
7165 foreach ($fields_label as $field_toshow) {
7166 $labeltoshow .= $obj->$field_toshow . ' ';
7167 }
7168 } else {
7169 $labeltoshow = $obj->{$InfoFieldList[1]};
7170 }
7171 $labeltoshow = dol_trunc($labeltoshow, 45);
7172
7173 if ($value == $obj->rowid) {
7174 foreach ($fields_label as $field_toshow) {
7175 $translabel = $langs->trans($obj->$field_toshow);
7176 if ($translabel != $obj->$field_toshow) {
7177 $labeltoshow = dol_trunc($translabel) . ' ';
7178 } else {
7179 $labeltoshow = dol_trunc($obj->$field_toshow) . ' ';
7180 }
7181 }
7182 $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7183 } else {
7184 if (!$notrans) {
7185 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7186 if ($translabel != $obj->{$InfoFieldList[1]}) {
7187 $labeltoshow = dol_trunc($translabel, 18);
7188 } else {
7189 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]});
7190 }
7191 }
7192 if (empty($labeltoshow)) {
7193 $labeltoshow = '(not defined)';
7194 }
7195 if ($value == $obj->rowid) {
7196 $out .= '<option value="' . $obj->rowid . '" selected>' . $labeltoshow . '</option>';
7197 }
7198
7199 if (!empty($InfoFieldList[3]) && $parentField) {
7200 $parent = $parentName . ':' . $obj->{$parentField};
7201 $isDependList = 1;
7202 }
7203
7204 $out .= '<option value="' . $obj->rowid . '"';
7205 $out .= ($value == $obj->rowid ? ' selected' : '');
7206 $out .= (!empty($parent) ? ' parent="' . $parent . '"' : '');
7207 $out .= '>' . $labeltoshow . '</option>';
7208 }
7209
7210 $i++;
7211 }
7212 $this->db->free($resql);
7213 } else {
7214 print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
7215 }
7216 } else {
7217 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7218 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
7219 $out .= '<option value="0">&nbsp;</option>';
7220 foreach ($data as $data_key => $data_value) {
7221 $out .= '<option value="' . $data_key . '"';
7222 $out .= ($value == $data_key ? ' selected' : '');
7223 $out .= '>' . $data_value . '</option>';
7224 }
7225 }
7226 }
7227 $out .= '</select>';
7228 } elseif ($type == 'checkbox') {
7229 $value_arr = explode(',', $value);
7230 $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ?null:$param['options']), $value_arr, '', 0, $morecss, 0, '100%');
7231 } elseif ($type == 'radio') {
7232 $out = '';
7233 foreach ($param['options'] as $keyopt => $valopt) {
7234 $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
7235 $out .= ' value="'.$keyopt.'"';
7236 $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
7237 $out .= ($value == $keyopt ? 'checked' : '');
7238 $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$valopt.'</label><br>';
7239 }
7240 } elseif ($type == 'chkbxlst') {
7241 if (is_array($value)) {
7242 $value_arr = $value;
7243 } else {
7244 $value_arr = explode(',', $value);
7245 }
7246
7247 if (is_array($param['options'])) {
7248 $param_list = array_keys($param['options']);
7249 $InfoFieldList = explode(":", $param_list[0]);
7250 $parentName = '';
7251 $parentField = '';
7252 // 0 : tableName
7253 // 1 : label field name
7254 // 2 : key fields name (if differ of rowid)
7255 // 3 : key field parent (for dependent lists)
7256 // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
7257 // 5 : id category type
7258 // 6 : ids categories list separated by comma for category root
7259 $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7260
7261 if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7262 list ($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7263 $keyList .= ', '.$parentField;
7264 }
7265 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7266 if (strpos($InfoFieldList[4], 'extra.') !== false) {
7267 $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7268 } else {
7269 $keyList = $InfoFieldList[2].' as rowid';
7270 }
7271 }
7272
7273 $filter_categorie = false;
7274 if (count($InfoFieldList) > 5) {
7275 if ($InfoFieldList[0] == 'categorie') {
7276 $filter_categorie = true;
7277 }
7278 }
7279
7280 if ($filter_categorie === false) {
7281 $fields_label = explode('|', $InfoFieldList[1]);
7282 if (is_array($fields_label)) {
7283 $keyList .= ', ';
7284 $keyList .= implode(', ', $fields_label);
7285 }
7286
7287 $sqlwhere = '';
7288 $sql = "SELECT " . $keyList;
7289 $sql .= ' FROM ' . $this->db->prefix() . $InfoFieldList[0];
7290 if (!empty($InfoFieldList[4])) {
7291 // can use SELECT request
7292 if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7293 $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7294 }
7295
7296 // current object id can be use into filter
7297 if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7298 $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
7299 } else {
7300 $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7301 }
7302
7303 // We have to join on extrafield table
7304 if (strpos($InfoFieldList[4], 'extra') !== false) {
7305 $sql .= ' as main, ' . $this->db->prefix() . $InfoFieldList[0] . '_extrafields as extra';
7306 $sqlwhere .= " WHERE extra.fk_object=main." . $InfoFieldList[2] . " AND " . $InfoFieldList[4];
7307 } else {
7308 $sqlwhere .= " WHERE " . $InfoFieldList[4];
7309 }
7310 } else {
7311 $sqlwhere .= ' WHERE 1=1';
7312 }
7313 // Some tables may have field, some other not. For the moment we disable it.
7314 if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7315 $sqlwhere .= " AND entity = " . ((int) $conf->entity);
7316 }
7317 // $sql.=preg_replace('/^ AND /','',$sqlwhere);
7318 // print $sql;
7319
7320 $sql .= $sqlwhere;
7321 dol_syslog(get_class($this) . '::showInputField type=chkbxlst', LOG_DEBUG);
7322 $resql = $this->db->query($sql);
7323 if ($resql) {
7324 $num = $this->db->num_rows($resql);
7325 $i = 0;
7326
7327 $data = array();
7328
7329 while ($i < $num) {
7330 $labeltoshow = '';
7331 $obj = $this->db->fetch_object($resql);
7332
7333 $notrans = false;
7334 // Several field into label (eq table:code|libelle:rowid)
7335 $fields_label = explode('|', $InfoFieldList[1]);
7336 if (count($fields_label) > 1) {
7337 $notrans = true;
7338 foreach ($fields_label as $field_toshow) {
7339 $labeltoshow .= $obj->$field_toshow . ' ';
7340 }
7341 } else {
7342 $labeltoshow = $obj->{$InfoFieldList[1]};
7343 }
7344 $labeltoshow = dol_trunc($labeltoshow, 45);
7345
7346 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7347 foreach ($fields_label as $field_toshow) {
7348 $translabel = $langs->trans($obj->$field_toshow);
7349 if ($translabel != $obj->$field_toshow) {
7350 $labeltoshow = dol_trunc($translabel, 18) . ' ';
7351 } else {
7352 $labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
7353 }
7354 }
7355
7356 $data[$obj->rowid] = $labeltoshow;
7357 } else {
7358 if (!$notrans) {
7359 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7360 if ($translabel != $obj->{$InfoFieldList[1]}) {
7361 $labeltoshow = dol_trunc($translabel, 18);
7362 } else {
7363 $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
7364 }
7365 }
7366 if (empty($labeltoshow)) {
7367 $labeltoshow = '(not defined)';
7368 }
7369
7370 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7371 $data[$obj->rowid] = $labeltoshow;
7372 }
7373
7374 if (!empty($InfoFieldList[3]) && $parentField) {
7375 $parent = $parentName . ':' . $obj->{$parentField};
7376 $isDependList = 1;
7377 }
7378
7379 $data[$obj->rowid] = $labeltoshow;
7380 }
7381
7382 $i++;
7383 }
7384 $this->db->free($resql);
7385
7386 $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, '', 0, $morecss, 0, '100%');
7387 } else {
7388 print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
7389 }
7390 } else {
7391 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
7392 $data = $form->select_all_categories(Categorie::$MAP_ID_TO_CODE[$InfoFieldList[5]], '', 'parent', 64, $InfoFieldList[6], 1, 1);
7393 $out = $form->multiselectarray($keyprefix . $key . $keysuffix, $data, $value_arr, '', 0, $morecss, 0, '100%');
7394 }
7395 }
7396 } elseif ($type == 'link') {
7397 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath[:AddCreateButtonOrNot[:Filter[:Sortfield]]]'
7398 $param_list_array = explode(':', $param_list[0]);
7399 $showempty = (($required && $default != '') ? 0 : 1);
7400
7401 if (!preg_match('/search_/', $keyprefix)) {
7402 if (!empty($param_list_array[2])) { // If the entry into $fields is set to add a create button
7403 if (!empty($this->fields[$key]['picto'])) {
7404 $morecss .= ' widthcentpercentminusxx';
7405 } else {
7406 $morecss .= ' widthcentpercentminusx';
7407 }
7408 } else {
7409 if (!empty($this->fields[$key]['picto'])) {
7410 $morecss .= ' widthcentpercentminusx';
7411 }
7412 }
7413 }
7414
7415 $out = $form->selectForForms($param_list[0], $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, $moreparam, 0, empty($val['disabled']) ? 0 : 1);
7416
7417 if (!empty($param_list_array[2])) { // If the entry into $fields is set, we must add a create button
7418 if ((!GETPOSTISSET('backtopage') || strpos(GETPOST('backtopage'), $_SERVER['PHP_SELF']) === 0) // // To avoid to open several times the 'Plus' button (we accept only one level)
7419 && empty($val['disabled']) && empty($nonewbutton)) { // and to avoid to show the button if the field is protected by a "disabled".
7420 list($class, $classfile) = explode(':', $param_list[0]);
7421 if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) {
7422 $url_path = dol_buildpath(dirname(dirname($classfile)).'/card.php', 1);
7423 } else {
7424 $url_path = dol_buildpath(dirname(dirname($classfile)).'/'.strtolower($class).'_card.php', 1);
7425 }
7426 $paramforthenewlink = '';
7427 $paramforthenewlink .= (GETPOSTISSET('action') ? '&action='.GETPOST('action', 'aZ09') : '');
7428 $paramforthenewlink .= (GETPOSTISSET('id') ? '&id='.GETPOST('id', 'int') : '');
7429 $paramforthenewlink .= (GETPOSTISSET('origin') ? '&origin='.GETPOST('origin', 'aZ09') : '');
7430 $paramforthenewlink .= (GETPOSTISSET('originid') ? '&originid='.GETPOST('originid', 'int') : '');
7431 $paramforthenewlink .= '&fk_'.strtolower($class).'=--IDFORBACKTOPAGE--';
7432 // TODO Add Javascript code to add input fields already filled into $paramforthenewlink so we won't loose them when going back to main page
7433 $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>';
7434 }
7435 }
7436 } elseif ($type == 'password') {
7437 // If prefix is 'search_', field is used as a filter, we use a common text field.
7438 $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
7439 } elseif ($type == 'array') {
7440 $newval = $val;
7441 $newval['type'] = 'varchar(256)';
7442
7443 $out = '';
7444 if (!empty($value)) {
7445 foreach ($value as $option) {
7446 $out .= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
7447 $out .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $morecss).'<br></span>';
7448 }
7449 }
7450 $out .= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
7451
7452 $newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
7453 $newInput .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $morecss).'<br></span>';
7454
7455 if (!empty($conf->use_javascript_ajax)) {
7456 $out .= '
7457 <script nonce="'.getNonce().'">
7458 $(document).ready(function() {
7459 $("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
7460 $("'.dol_escape_js($newInput).'").insertBefore(this);
7461 });
7462
7463 $(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
7464 $(this).parent().remove();
7465 });
7466 });
7467 </script>';
7468 }
7469 }
7470 if (!empty($hidden)) {
7471 $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
7472 }
7473
7474 if ($isDependList==1) {
7475 $out .= $this->getJSListDependancies('_common');
7476 }
7477 /* Add comments
7478 if ($type == 'date') $out.=' (YYYY-MM-DD)';
7479 elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
7480 */
7481
7482 // Display error message for field
7483 if (!empty($fieldValidationErrorMsg) && function_exists('getFieldErrorIcon')) {
7484 $out .= ' '.getFieldErrorIcon($fieldValidationErrorMsg);
7485 }
7486
7487 return $out;
7488 }
7489
7503 public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '')
7504 {
7505 global $conf, $langs, $form;
7506
7507 if (!is_object($form)) {
7508 require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
7509 $form = new Form($this->db);
7510 }
7511
7512 $label = empty($val['label']) ? '' : $val['label'];
7513 $type = empty($val['type']) ? '' : $val['type'];
7514 $size = empty($val['css']) ? '' : $val['css'];
7515 $reg = array();
7516
7517 // Convert var to be able to share same code than showOutputField of extrafields
7518 if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
7519 $type = 'varchar'; // convert varchar(xx) int varchar
7520 $size = $reg[1];
7521 } elseif (preg_match('/varchar/', $type)) {
7522 $type = 'varchar'; // convert varchar(xx) int varchar
7523 }
7524 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
7525 $type = 'select';
7526 }
7527 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
7528 $type = 'link';
7529 }
7530
7531 $default = empty($val['default']) ? '' : $val['default'];
7532 $computed = empty($val['computed']) ? '' : $val['computed'];
7533 $unique = empty($val['unique']) ? '' : $val['unique'];
7534 $required = empty($val['required']) ? '' : $val['required'];
7535 $param = array();
7536 $param['options'] = array();
7537
7538 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
7539 $param['options'] = $val['arrayofkeyval'];
7540 }
7541 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
7542 $type = 'link';
7543 $stringforoptions = $reg[1].':'.$reg[2];
7544 if ($reg[1] == 'User') {
7545 $stringforoptions .= ':-1';
7546 }
7547 $param['options'] = array($stringforoptions => $stringforoptions);
7548 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
7549 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
7550 $type = 'sellist';
7551 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
7552 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
7553 $type = 'sellist';
7554 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
7555 $param['options'] = array($reg[1].':'.$reg[2] => 'N');
7556 $type = 'sellist';
7557 } elseif (preg_match('/^chkbxlst:(.*)/i', $val['type'], $reg)) {
7558 $param['options'] = array($reg[1] => 'N');
7559 $type = 'chkbxlst';
7560 }
7561
7562 $langfile = empty($val['langfile']) ? '' : $val['langfile'];
7563 $list = (empty($val['list']) ? '' : $val['list']);
7564 $help = (empty($val['help']) ? '' : $val['help']);
7565 $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)
7566
7567 if ($hidden) {
7568 return '';
7569 }
7570
7571 // If field is a computed field, value must become result of compute
7572 if ($computed) {
7573 // Make the eval of compute string
7574 //var_dump($computed);
7575 $value = dol_eval($computed, 1, 0, '');
7576 }
7577
7578 if (empty($morecss)) {
7579 if ($type == 'date') {
7580 $morecss = 'minwidth100imp';
7581 } elseif ($type == 'datetime' || $type == 'timestamp') {
7582 $morecss = 'minwidth200imp';
7583 } elseif (in_array($type, array('int', 'double', 'price'))) {
7584 $morecss = 'maxwidth75';
7585 } elseif ($type == 'url') {
7586 $morecss = 'minwidth400';
7587 } elseif ($type == 'boolean') {
7588 $morecss = '';
7589 } else {
7590 if (is_numeric($size) && round($size) < 12) {
7591 $morecss = 'minwidth100';
7592 } elseif (is_numeric($size) && round($size) <= 48) {
7593 $morecss = 'minwidth200';
7594 } else {
7595 $morecss = 'minwidth400';
7596 }
7597 }
7598 }
7599
7600 // Format output value differently according to properties of field
7601 if (in_array($key, array('rowid', 'ref')) && method_exists($this, 'getNomUrl')) {
7602 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.
7603 $value = $this->getNomUrl(1, '', 0, '', 1);
7604 }
7605 } elseif ($key == 'status' && method_exists($this, 'getLibStatut')) {
7606 $value = $this->getLibStatut(3);
7607 } elseif ($type == 'date') {
7608 if (!empty($value)) {
7609 $value = dol_print_date($value, 'day'); // We suppose dates without time are always gmt (storage of course + output)
7610 } else {
7611 $value = '';
7612 }
7613 } elseif ($type == 'datetime' || $type == 'timestamp') {
7614 if (!empty($value)) {
7615 $value = dol_print_date($value, 'dayhour', 'tzuserrel');
7616 } else {
7617 $value = '';
7618 }
7619 } elseif ($type == 'duration') {
7620 include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
7621 if (!is_null($value) && $value !== '') {
7622 $value = convertSecondToTime($value, 'allhourmin');
7623 }
7624 } elseif ($type == 'double' || $type == 'real') {
7625 if (!is_null($value) && $value !== '') {
7626 $value = price($value);
7627 }
7628 } elseif ($type == 'boolean') {
7629 $checked = '';
7630 if (!empty($value)) {
7631 $checked = ' checked ';
7632 }
7633 $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
7634 } elseif ($type == 'mail' || $type == 'email') {
7635 $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
7636 } elseif ($type == 'url') {
7637 $value = dol_print_url($value, '_blank', 32, 1);
7638 } elseif ($type == 'phone') {
7639 $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 'phone');
7640 } elseif ($type == 'ip') {
7641 $value = dol_print_ip($value, 0);
7642 } elseif ($type == 'price') {
7643 if (!is_null($value) && $value !== '') {
7644 $value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
7645 }
7646 } elseif ($type == 'select') {
7647 $value = isset($param['options'][$value])?$param['options'][$value]:'';
7648 } elseif ($type == 'sellist') {
7649 $param_list = array_keys($param['options']);
7650 $InfoFieldList = explode(":", $param_list[0]);
7651
7652 $selectkey = "rowid";
7653 $keyList = 'rowid';
7654
7655 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7656 $selectkey = $InfoFieldList[2];
7657 $keyList = $InfoFieldList[2].' as rowid';
7658 }
7659
7660 $fields_label = explode('|', $InfoFieldList[1]);
7661 if (is_array($fields_label)) {
7662 $keyList .= ', ';
7663 $keyList .= implode(', ', $fields_label);
7664 }
7665
7666 $filter_categorie = false;
7667 if (count($InfoFieldList) > 5) {
7668 if ($InfoFieldList[0] == 'categorie') {
7669 $filter_categorie = true;
7670 }
7671 }
7672
7673 $sql = "SELECT ".$keyList;
7674 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
7675 if (strpos($InfoFieldList[4], 'extra') !== false) {
7676 $sql .= ' as main';
7677 }
7678 if ($selectkey == 'rowid' && empty($value)) {
7679 $sql .= " WHERE ".$selectkey." = 0";
7680 } elseif ($selectkey == 'rowid') {
7681 $sql .= " WHERE ".$selectkey." = ".((int) $value);
7682 } else {
7683 $sql .= " WHERE ".$selectkey." = '".$this->db->escape($value)."'";
7684 }
7685
7686 //$sql.= ' AND entity = '.$conf->entity;
7687
7688 dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
7689 $resql = $this->db->query($sql);
7690 if ($resql) {
7691 if ($filter_categorie === false) {
7692 $value = ''; // value was used, so now we reste it to use it to build final output
7693 $numrows = $this->db->num_rows($resql);
7694 if ($numrows) {
7695 $obj = $this->db->fetch_object($resql);
7696
7697 // Several field into label (eq table:code|libelle:rowid)
7698 $fields_label = explode('|', $InfoFieldList[1]);
7699
7700 if (is_array($fields_label) && count($fields_label) > 1) {
7701 foreach ($fields_label as $field_toshow) {
7702 $translabel = '';
7703 if (!empty($obj->$field_toshow)) {
7704 $translabel = $langs->trans($obj->$field_toshow);
7705 }
7706 if ($translabel != $field_toshow) {
7707 $value .= dol_trunc($translabel, 18) . ' ';
7708 } else {
7709 $value .= $obj->$field_toshow . ' ';
7710 }
7711 }
7712 } else {
7713 $translabel = '';
7714 if (!empty($obj->{$InfoFieldList[1]})) {
7715 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7716 }
7717 if ($translabel != $obj->{$InfoFieldList[1]}) {
7718 $value = dol_trunc($translabel, 18);
7719 } else {
7720 $value = $obj->{$InfoFieldList[1]};
7721 }
7722 }
7723 }
7724 } else {
7725 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
7726
7727 $toprint = array();
7728 $obj = $this->db->fetch_object($resql);
7729 $c = new Categorie($this->db);
7730 $c->fetch($obj->rowid);
7731 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
7732 foreach ($ways as $way) {
7733 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
7734 }
7735 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
7736 }
7737 } else {
7738 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
7739 }
7740 } elseif ($type == 'radio') {
7741 $value = $param['options'][$value];
7742 } elseif ($type == 'checkbox') {
7743 $value_arr = explode(',', $value);
7744 $value = '';
7745 if (is_array($value_arr) && count($value_arr) > 0) {
7746 $toprint = array();
7747 foreach ($value_arr as $keyval => $valueval) {
7748 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$param['options'][$valueval].'</li>';
7749 }
7750 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
7751 }
7752 } elseif ($type == 'chkbxlst') {
7753 $value_arr = explode(',', $value);
7754
7755 $param_list = array_keys($param['options']);
7756 $InfoFieldList = explode(":", $param_list[0]);
7757
7758 $selectkey = "rowid";
7759 $keyList = 'rowid';
7760
7761 if (count($InfoFieldList) >= 3) {
7762 $selectkey = $InfoFieldList[2];
7763 $keyList = $InfoFieldList[2].' as rowid';
7764 }
7765
7766 $fields_label = explode('|', $InfoFieldList[1]);
7767 if (is_array($fields_label)) {
7768 $keyList .= ', ';
7769 $keyList .= implode(', ', $fields_label);
7770 }
7771
7772 $filter_categorie = false;
7773 if (count($InfoFieldList) > 5) {
7774 if ($InfoFieldList[0] == 'categorie') {
7775 $filter_categorie = true;
7776 }
7777 }
7778
7779 $sql = "SELECT ".$keyList;
7780 $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
7781 if (strpos($InfoFieldList[4], 'extra') !== false) {
7782 $sql .= ' as main';
7783 }
7784 // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
7785 // $sql.= ' AND entity = '.$conf->entity;
7786
7787 dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG);
7788 $resql = $this->db->query($sql);
7789 if ($resql) {
7790 if ($filter_categorie === false) {
7791 $value = ''; // value was used, so now we reste it to use it to build final output
7792 $toprint = array();
7793 while ($obj = $this->db->fetch_object($resql)) {
7794 // Several field into label (eq table:code|libelle:rowid)
7795 $fields_label = explode('|', $InfoFieldList[1]);
7796 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7797 if (is_array($fields_label) && count($fields_label) > 1) {
7798 foreach ($fields_label as $field_toshow) {
7799 $translabel = '';
7800 if (!empty($obj->$field_toshow)) {
7801 $translabel = $langs->trans($obj->$field_toshow);
7802 }
7803 if ($translabel != $field_toshow) {
7804 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
7805 } else {
7806 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->$field_toshow . '</li>';
7807 }
7808 }
7809 } else {
7810 $translabel = '';
7811 if (!empty($obj->{$InfoFieldList[1]})) {
7812 $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7813 }
7814 if ($translabel != $obj->{$InfoFieldList[1]}) {
7815 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . dol_trunc($translabel, 18) . '</li>';
7816 } else {
7817 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">' . $obj->{$InfoFieldList[1]} . '</li>';
7818 }
7819 }
7820 }
7821 }
7822 } else {
7823 require_once DOL_DOCUMENT_ROOT . '/categories/class/categorie.class.php';
7824
7825 $toprint = array();
7826 while ($obj = $this->db->fetch_object($resql)) {
7827 if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7828 $c = new Categorie($this->db);
7829 $c->fetch($obj->rowid);
7830 $ways = $c->print_all_ways(); // $ways[0] = "ccc2 >> ccc2a >> ccc2a1" with html formatted text
7831 foreach ($ways as $way) {
7832 $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories"' . ($c->color ? ' style="background: #' . $c->color . ';"' : ' style="background: #aaa"') . '>' . img_object('', 'category') . ' ' . $way . '</li>';
7833 }
7834 }
7835 }
7836 }
7837 $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
7838 } else {
7839 dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
7840 }
7841 } elseif ($type == 'link') {
7842 $out = '';
7843
7844 // only if something to display (perf)
7845 if ($value) {
7846 $param_list = array_keys($param['options']); // Example: $param_list='ObjectName:classPath:-1::customer'
7847
7848 $InfoFieldList = explode(":", $param_list[0]);
7849 $classname = $InfoFieldList[0];
7850 $classpath = $InfoFieldList[1];
7851 $getnomurlparam = (empty($InfoFieldList[2]) ? 3 : $InfoFieldList[2]);
7852 $getnomurlparam2 = (empty($InfoFieldList[4]) ? '' : $InfoFieldList[4]);
7853 if (!empty($classpath)) {
7854 dol_include_once($InfoFieldList[1]);
7855 if ($classname && class_exists($classname)) {
7856 $object = new $classname($this->db);
7857 if ($object->element === 'product') { // Special cas for product because default valut of fetch are wrong
7858 $result = $object->fetch($value, '', '', '', 0, 1, 1);
7859 } else {
7860 $result = $object->fetch($value);
7861 }
7862 if ($result > 0) {
7863 if ($object->element === 'product') {
7864 $get_name_url_param_arr = array($getnomurlparam, $getnomurlparam2, 0, -1, 0, '', 0);
7865 if (isset($val['get_name_url_params'])) {
7866 $get_name_url_params = explode(':', $val['get_name_url_params']);
7867 if (!empty($get_name_url_params)) {
7868 $param_num_max = count($get_name_url_param_arr) - 1;
7869 foreach ($get_name_url_params as $param_num => $param_value) {
7870 if ($param_num > $param_num_max) {
7871 break;
7872 }
7873 $get_name_url_param_arr[$param_num] = $param_value;
7874 }
7875 }
7876 }
7877
7881 $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]);
7882 } else {
7883 $value = $object->getNomUrl($getnomurlparam, $getnomurlparam2);
7884 }
7885 } else {
7886 $value = '';
7887 }
7888 }
7889 } else {
7890 dol_syslog('Error bad setup of extrafield', LOG_WARNING);
7891 return 'Error bad setup of extrafield';
7892 }
7893 } else {
7894 $value = '';
7895 }
7896 } elseif ($type == 'password') {
7897 $value = preg_replace('/./i', '*', $value);
7898 } elseif ($type == 'array') {
7899 $value = implode('<br>', $value);
7900 } else { // text|html|varchar
7901 $value = dol_htmlentitiesbr($value);
7902 }
7903
7904 //print $type.'-'.$size.'-'.$value;
7905 $out = $value;
7906
7907 return $out;
7908 }
7909
7916 public function clearFieldError($fieldKey)
7917 {
7918 $this->error = '';
7919 unset($this->validateFieldsErrors[$fieldKey]);
7920 }
7921
7929 public function setFieldError($fieldKey, $msg = '')
7930 {
7931 global $langs;
7932 if (empty($msg)) {
7933 $msg = $langs->trans("UnknowError");
7934 }
7935
7936 $this->error = $this->validateFieldsErrors[$fieldKey] = $msg;
7937 }
7938
7945 public function getFieldError($fieldKey)
7946 {
7947 if (!empty($this->validateFieldsErrors[$fieldKey])) {
7948 return $this->validateFieldsErrors[$fieldKey];
7949 }
7950 return '';
7951 }
7952
7961 public function validateField($fields, $fieldKey, $fieldValue)
7962 {
7963 global $langs;
7964
7965 if (!class_exists('Validate')) {
7966 require_once DOL_DOCUMENT_ROOT . '/core/class/validate.class.php';
7967 }
7968
7969 $this->clearFieldError($fieldKey);
7970
7971 if (!isset($fields[$fieldKey])) {
7972 $this->setFieldError($fieldKey, $langs->trans('FieldNotFoundInObject'));
7973 return false;
7974 }
7975
7976 $val = $fields[$fieldKey];
7977
7978 $param = array();
7979 $param['options'] = array();
7980 $type = $val['type'];
7981
7982 $required = false;
7983 if (isset($val['notnull']) && $val['notnull'] === 1) {
7984 // 'notnull' is set to 1 if not null in database. Set to -1 if we must set data to null if empty ('' or 0).
7985 $required = true;
7986 }
7987
7988 $maxSize = 0;
7989 $minSize = 0;
7990
7991 //
7992 // PREPARE Elements
7993 //
7994 $reg = array();
7995
7996 // Convert var to be able to share same code than showOutputField of extrafields
7997 if (preg_match('/varchar\‍((\d+)\‍)/', $type, $reg)) {
7998 $type = 'varchar'; // convert varchar(xx) int varchar
7999 $maxSize = $reg[1];
8000 } elseif (preg_match('/varchar/', $type)) {
8001 $type = 'varchar'; // convert varchar(xx) int varchar
8002 }
8003
8004 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8005 $type = 'select';
8006 }
8007
8008 if (!empty($val['type']) && preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8009 $type = 'link';
8010 }
8011
8012 if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) {
8013 $param['options'] = $val['arrayofkeyval'];
8014 }
8015
8016 if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
8017 $type = 'link';
8018 $param['options'] = array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
8019 } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
8020 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
8021 $type = 'sellist';
8022 } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
8023 $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
8024 $type = 'sellist';
8025 } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
8026 $param['options'] = array($reg[1].':'.$reg[2] => 'N');
8027 $type = 'sellist';
8028 }
8029
8030 //
8031 // TEST Value
8032 //
8033
8034 // Use Validate class to allow external Modules to use data validation part instead of concentrate all test here (factoring) or just for reuse
8035 $validate = new Validate($this->db, $langs);
8036
8037
8038 // little trick : to perform tests with good performances sort tests by quick to low
8039
8040 //
8041 // COMMON TESTS
8042 //
8043
8044 // Required test and empty value
8045 if ($required && !$validate->isNotEmptyString($fieldValue)) {
8046 $this->setFieldError($fieldKey, $validate->error);
8047 return false;
8048 } elseif (!$required && !$validate->isNotEmptyString($fieldValue)) {
8049 // if no value sent and the field is not mandatory, no need to perform tests
8050 return true;
8051 }
8052
8053 // MAX Size test
8054 if (!empty($maxSize) && !$validate->isMaxLength($fieldValue, $maxSize)) {
8055 $this->setFieldError($fieldKey, $validate->error);
8056 return false;
8057 }
8058
8059 // MIN Size test
8060 if (!empty($minSize) && !$validate->isMinLength($fieldValue, $minSize)) {
8061 $this->setFieldError($fieldKey, $validate->error);
8062 return false;
8063 }
8064
8065 //
8066 // TESTS for TYPE
8067 //
8068
8069 if (in_array($type, array('date', 'datetime', 'timestamp'))) {
8070 if (!$validate->isTimestamp($fieldValue)) {
8071 $this->setFieldError($fieldKey, $validate->error);
8072 return false;
8073 } else { return true; }
8074 } elseif ($type == 'duration') {
8075 if (!$validate->isDuration($fieldValue)) {
8076 $this->setFieldError($fieldKey, $validate->error);
8077 return false;
8078 } else { return true; }
8079 } elseif (in_array($type, array('double', 'real', 'price'))) {
8080 // is numeric
8081 if (!$validate->isNumeric($fieldValue)) {
8082 $this->setFieldError($fieldKey, $validate->error);
8083 return false;
8084 } else { return true; }
8085 } elseif ($type == 'boolean') {
8086 if (!$validate->isBool($fieldValue)) {
8087 $this->setFieldError($fieldKey, $validate->error);
8088 return false;
8089 } else { return true; }
8090 } elseif ($type == 'mail') {
8091 if (!$validate->isEmail($fieldValue)) {
8092 $this->setFieldError($fieldKey, $validate->error);
8093 return false;
8094 }
8095 } elseif ($type == 'url') {
8096 if (!$validate->isUrl($fieldValue)) {
8097 $this->setFieldError($fieldKey, $validate->error);
8098 return false;
8099 } else { return true; }
8100 } elseif ($type == 'phone') {
8101 if (!$validate->isPhone($fieldValue)) {
8102 $this->setFieldError($fieldKey, $validate->error);
8103 return false;
8104 } else { return true; }
8105 } elseif ($type == 'select' || $type == 'radio') {
8106 if (!isset($param['options'][$fieldValue])) {
8107 $this->error = $langs->trans('RequireValidValue');
8108 return false;
8109 } else { return true; }
8110 } elseif ($type == 'sellist' || $type == 'chkbxlst') {
8111 $param_list = array_keys($param['options']);
8112 $InfoFieldList = explode(":", $param_list[0]);
8113 $value_arr = explode(',', $fieldValue);
8114 $value_arr = array_map(array($this->db, 'escape'), $value_arr);
8115
8116 $selectkey = "rowid";
8117 if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
8118 $selectkey = $InfoFieldList[2];
8119 }
8120
8121 if (!$validate->isInDb($value_arr, $InfoFieldList[0], $selectkey)) {
8122 $this->setFieldError($fieldKey, $validate->error);
8123 return false;
8124 } else { return true; }
8125 } elseif ($type == 'link') {
8126 $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
8127 $InfoFieldList = explode(":", $param_list[0]);
8128 $classname = $InfoFieldList[0];
8129 $classpath = $InfoFieldList[1];
8130 if (!$validate->isFetchable($fieldValue, $classname, $classpath)) {
8131 $this->setFieldError($fieldKey, $validate->error);
8132 return false;
8133 } else { return true; }
8134 }
8135
8136 // if no test failled all is ok
8137 return true;
8138 }
8139
8153 public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = 0, $display_type = 'card')
8154 {
8155 global $db, $conf, $langs, $action, $form, $hookmanager;
8156
8157 if (!is_object($form)) {
8158 $form = new Form($db);
8159 }
8160 if (!is_object($extrafields)) {
8161 dol_syslog('Bad parameter extrafields for showOptionals', LOG_ERR);
8162 return 'Bad parameter extrafields for showOptionals';
8163 }
8164 if (!is_array($extrafields->attributes[$this->table_element])) {
8165 dol_syslog("extrafields->attributes was not loaded with extrafields->fetch_name_optionals_label(table_element);", LOG_WARNING);
8166 }
8167
8168 $out = '';
8169
8170 $parameters = array('mode'=>$mode, 'params'=>$params, 'keysuffix'=>$keysuffix, 'keyprefix'=>$keyprefix, 'display_type'=>$display_type);
8171 $reshook = $hookmanager->executeHooks('showOptionals', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
8172
8173 if (empty($reshook)) {
8174 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) {
8175 $out .= "\n";
8176 $out .= '<!-- commonobject:showOptionals --> ';
8177 $out .= "\n";
8178
8179 $nbofextrafieldsshown = 0;
8180 $e = 0; // var to manage the modulo (odd/even)
8181
8182 $lastseparatorkeyfound = '';
8183 $extrafields_collapse_num = '';
8184 $extrafields_collapse_num_old = '';
8185 $i = 0;
8186
8187 foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $label) {
8188 $i++;
8189
8190 // Show only the key field in params
8191 if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) {
8192 continue;
8193 }
8194
8195 // Test on 'enabled' ('enabled' is different than 'list' = 'visibility')
8196 $enabled = 1;
8197 if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key])) {
8198 $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1, 1, '2');
8199 }
8200 if (empty($enabled)) {
8201 continue;
8202 }
8203
8204 $visibility = 1;
8205 if ($visibility && isset($extrafields->attributes[$this->table_element]['list'][$key])) {
8206 $visibility = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1, 1, '2');
8207 }
8208
8209 $perms = 1;
8210 if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key])) {
8211 $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1, 1, '2');
8212 }
8213
8214 if (($mode == 'create') && abs($visibility) != 1 && abs($visibility) != 3) {
8215 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list
8216 } elseif (($mode == 'edit') && abs($visibility) != 1 && abs($visibility) != 3 && abs($visibility) != 4) {
8217 // 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
8218 $ef_name = 'options_' . $key;
8219 $ef_value = $this->array_options[$ef_name];
8220 $out .= '<input type="hidden" name="' . $ef_name . '" id="' . $ef_name . '" value="' . $ef_value . '" />' . "\n";
8221 continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list and <> 4 = not visible at the creation
8222 } elseif ($mode == 'view' && empty($visibility)) {
8223 continue;
8224 }
8225 if (empty($perms)) {
8226 continue;
8227 }
8228
8229 // Load language if required
8230 if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
8231 $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
8232 }
8233
8234 $colspan = 0;
8235 if (is_array($params) && count($params) > 0 && $display_type=='card') {
8236 if (array_key_exists('cols', $params)) {
8237 $colspan = $params['cols'];
8238 } elseif (array_key_exists('colspan', $params)) { // For backward compatibility. Use cols instead now.
8239 $reg = array();
8240 if (preg_match('/colspan="(\d+)"/', $params['colspan'], $reg)) {
8241 $colspan = $reg[1];
8242 } else {
8243 $colspan = $params['colspan'];
8244 }
8245 }
8246 }
8247 $colspan = intval($colspan);
8248
8249 switch ($mode) {
8250 case "view":
8251 $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
8252 break;
8253 case "create":
8254 case "edit":
8255 // We get the value of property found with GETPOST so it takes into account:
8256 // default values overwrite, restore back to list link, ... (but not 'default value in database' of field)
8257 $check = 'alphanohtml';
8258 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text'))) {
8259 $check = 'restricthtml';
8260 }
8261 $getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, $check, 3); // GETPOST can get value from GET, POST or setup of default values overwrite.
8262 // GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
8263 if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)) {
8264 if (is_array($getposttemp)) {
8265 // $getposttemp is an array but following code expects a comma separated string
8266 $value = implode(",", $getposttemp);
8267 } else {
8268 $value = $getposttemp;
8269 }
8270 } else {
8271 $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.
8272 }
8273 //var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
8274 break;
8275 }
8276
8277 $nbofextrafieldsshown++;
8278
8279 // Output value of the current field
8280 if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate') {
8281 $extrafields_collapse_num = $key;
8282 /*
8283 $extrafield_param = $extrafields->attributes[$this->table_element]['param'][$key];
8284 if (!empty($extrafield_param) && is_array($extrafield_param)) {
8285 $extrafield_param_list = array_keys($extrafield_param['options']);
8286
8287 if (count($extrafield_param_list) > 0) {
8288 $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
8289
8290 if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
8291 //$extrafields_collapse_num = $extrafields->attributes[$this->table_element]['pos'][$key];
8292 $extrafields_collapse_num = $key;
8293 }
8294 }
8295 }
8296 */
8297
8298 // if colspan=0 or 1, the second column is not extended, so the separator must be on 2 columns
8299 $out .= $extrafields->showSeparator($key, $this, ($colspan ? $colspan + 1 : 2), $display_type, $mode);
8300
8301 $lastseparatorkeyfound = $key;
8302 } else {
8303 $collapse_group = $extrafields_collapse_num.(!empty($this->id) ? '_'.$this->id : '');
8304
8305 $class = (!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
8306 $csstyle = '';
8307 if (is_array($params) && count($params) > 0) {
8308 if (array_key_exists('class', $params)) {
8309 $class .= $params['class'].' ';
8310 }
8311 if (array_key_exists('style', $params)) {
8312 $csstyle = $params['style'];
8313 }
8314 }
8315
8316 // add html5 elements
8317 $domData = ' data-element="extrafield"';
8318 $domData .= ' data-targetelement="'.$this->element.'"';
8319 $domData .= ' data-targetid="'.$this->id.'"';
8320
8321 $html_id = (empty($this->id) ? '' : 'extrarow-'.$this->element.'_'.$key.'_'.$this->id);
8322 if ($display_type=='card') {
8323 if (!empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0) {
8324 $colspan = 0;
8325 }
8326
8327 if ($action == 'selectlines') {
8328 $colspan++;
8329 }
8330 }
8331
8332 // Convert date into timestamp format (value in memory must be a timestamp)
8333 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date'))) {
8334 $datenotinstring = null;
8335 if (array_key_exists('options_'.$key, $this->array_options)) {
8336 $datenotinstring = $this->array_options['options_'.$key];
8337 if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
8338 $datenotinstring = $this->db->jdate($datenotinstring);
8339 }
8340 }
8341 $datekey = $keyprefix.'options_'.$key.$keysuffix;
8342 $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;
8343 }
8344 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('datetime'))) {
8345 $datenotinstring = null;
8346 if (array_key_exists('options_'.$key, $this->array_options)) {
8347 $datenotinstring = $this->array_options['options_'.$key];
8348 if (!is_numeric($this->array_options['options_'.$key])) { // For backward compatibility
8349 $datenotinstring = $this->db->jdate($datenotinstring);
8350 }
8351 }
8352 $timekey = $keyprefix.'options_'.$key.$keysuffix;
8353 $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;
8354 }
8355 // Convert float submited string into real php numeric (value in memory must be a php numeric)
8356 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price', 'double'))) {
8357 if (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) {
8358 $value = price2num($value);
8359 } elseif (isset($this->array_options['options_'.$key])) {
8360 $value = $this->array_options['options_'.$key];
8361 }
8362 }
8363
8364 // HTML, text, select, integer and varchar: take into account default value in database if in create mode
8365 if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text', 'varchar', 'select', 'radio', 'int', 'boolean'))) {
8366 if ($action == 'create') {
8367 $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) ? $value : $extrafields->attributes[$this->table_element]['default'][$key];
8368 }
8369 }
8370
8371 $labeltoshow = $langs->trans($label);
8372 $helptoshow = $langs->trans($extrafields->attributes[$this->table_element]['help'][$key]);
8373
8374 if ($display_type == 'card') {
8375 $out .= '<tr '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="field_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
8376 if (!empty($conf->global->MAIN_VIEW_LINE_NUMBER) && ($action == 'view' || $action == 'valid' || $action == 'editline' || $action == 'confirm_valid' || $action == 'confirm_cancel')) {
8377 $out .= '<td></td>';
8378 }
8379 $out .= '<td class="'.(empty($params['tdclass']) ? 'titlefieldcreate' : $params['tdclass']).' wordbreak';
8380 } elseif ($display_type == 'line') {
8381 $out .= '<div '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="fieldline_options_'.$key.' '.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$collapse_group.'" '.$domData.' >';
8382 $out .= '<div style="display: inline-block; padding-right:4px" class="wordbreak';
8383 }
8384 //$out .= "titlefield";
8385 //if (GETPOST('action', 'restricthtml') == 'create') $out.='create';
8386 // BUG #11554 : For public page, use red dot for required fields, instead of bold label
8387 $tpl_context = isset($params["tpl_context"]) ? $params["tpl_context"] : "none";
8388 if ($tpl_context != "public") { // Public page : red dot instead of fieldrequired characters
8389 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
8390 $out .= ' fieldrequired';
8391 }
8392 }
8393 $out .= '">';
8394 if ($tpl_context == "public") { // Public page : red dot instead of fieldrequired characters
8395 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
8396 $out .= $form->textwithpicto($labeltoshow, $helptoshow);
8397 } else {
8398 $out .= $labeltoshow;
8399 }
8400 if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) {
8401 $out .= '&nbsp;<span style="color: red">*</span>';
8402 }
8403 } else {
8404 if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) {
8405 $out .= $form->textwithpicto($labeltoshow, $helptoshow);
8406 } else {
8407 $out .= $labeltoshow;
8408 }
8409 }
8410
8411 $out .= ($display_type == 'card' ? '</td>' : '</div>');
8412
8413 $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
8414 if ($display_type == 'card') {
8415 // 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
8416 $out .= '<td '.($html_id ? 'id="'.$html_id.'" ' : '').' class="valuefieldcreate '.$this->element.'_extras_'.$key.'" '.($colspan ? ' colspan="'.$colspan.'"' : '').'>';
8417 } elseif ($display_type == 'line') {
8418 $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].'">';
8419 }
8420
8421 switch ($mode) {
8422 case "view":
8423 $out .= $extrafields->showOutputField($key, $value, '', $this->table_element);
8424 break;
8425 case "create":
8426 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
8427 break;
8428 case "edit":
8429 $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
8430 break;
8431 }
8432
8433 $out .= ($display_type=='card' ? '</td>' : '</div>');
8434
8435 if (!empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) {
8436 $out .= ($display_type=='card' ? '</tr>' : '</div>');
8437 } else {
8438 $out .= ($display_type=='card' ? '</tr>' : '</div>');
8439 }
8440
8441 $e++;
8442 }
8443 }
8444 $out .= "\n";
8445 // Add code to manage list depending on others
8446 if (!empty($conf->use_javascript_ajax)) {
8447 $out .= $this->getJSListDependancies();
8448 }
8449
8450 $out .= '<!-- commonobject:showOptionals end --> '."\n";
8451
8452 if (empty($nbofextrafieldsshown)) {
8453 $out = '';
8454 }
8455 }
8456 }
8457
8458 $out .= $hookmanager->resPrint;
8459
8460 return $out;
8461 }
8462
8467 public function getJSListDependancies($type = '_extra')
8468 {
8469 $out = '
8470 <script nonce="'.getNonce().'">
8471 jQuery(document).ready(function() {
8472 function showOptions'.$type.'(child_list, parent_list, orig_select)
8473 {
8474 var val = $("select[name=\""+parent_list+"\"]").val();
8475 var parentVal = parent_list + ":" + val;
8476 if(typeof val == "string"){
8477 if(val != "") {
8478 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
8479 $("select[name=\""+child_list+"\"] option[parent]").remove();
8480 $("select[name=\""+child_list+"\"]").append(options);
8481 } else {
8482 var options = orig_select.find("option[parent]").clone();
8483 $("select[name=\""+child_list+"\"] option[parent]").remove();
8484 $("select[name=\""+child_list+"\"]").append(options);
8485 }
8486 } else if(val > 0) {
8487 var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
8488 $("select[name=\""+child_list+"\"] option[parent]").remove();
8489 $("select[name=\""+child_list+"\"]").append(options);
8490 } else {
8491 var options = orig_select.find("option[parent]").clone();
8492 $("select[name=\""+child_list+"\"] option[parent]").remove();
8493 $("select[name=\""+child_list+"\"]").append(options);
8494 }
8495 }
8496 function setListDependencies'.$type.'() {
8497 jQuery("select option[parent]").parent().each(function() {
8498 var orig_select = {};
8499 var child_list = $(this).attr("name");
8500 orig_select[child_list] = $(this).clone();
8501 var parent = $(this).find("option[parent]:first").attr("parent");
8502 var infos = parent.split(":");
8503 var parent_list = infos[0];
8504
8505 //Hide daughters lists
8506 if ($("#"+child_list).val() == 0 && $("#"+parent_list).val() == 0){
8507 $("#"+child_list).hide();
8508 //Show mother lists
8509 } else if ($("#"+parent_list).val() != 0){
8510 $("#"+parent_list).show();
8511 }
8512 //Show the child list if the parent list value is selected
8513 $("select[name=\""+parent_list+"\"]").click(function() {
8514 if ($(this).val() != 0){
8515 $("#"+child_list).show()
8516 }
8517 });
8518
8519 //When we change parent list
8520 $("select[name=\""+parent_list+"\"]").change(function() {
8521 showOptions'.$type.'(child_list, parent_list, orig_select[child_list]);
8522 //Select the value 0 on child list after a change on the parent list
8523 $("#"+child_list).val(0).trigger("change");
8524 //Hide child lists if the parent value is set to 0
8525 if ($(this).val() == 0){
8526 $("#"+child_list).hide();
8527 }
8528 });
8529 });
8530 }
8531
8532 setListDependencies'.$type.'();
8533 });
8534 </script>'."\n";
8535 return $out;
8536 }
8537
8543 public function getRights()
8544 {
8545 global $user;
8546
8547 $module = empty($this->module) ? '' : $this->module;
8548 $element = $this->element;
8549
8550 if ($element == 'facturerec') {
8551 $element = 'facture';
8552 } elseif ($element == 'invoice_supplier_rec') {
8553 return empty($user->rights->fournisseur->facture) ? null : $user->rights->fournisseur->facture;
8554 } elseif ($module && !empty($user->rights->$module->$element)) {
8555 // for modules built with ModuleBuilder
8556 return $user->rights->$module->$element;
8557 }
8558
8559 return $user->rights->$element;
8560 }
8561
8574 public static function commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
8575 {
8576 foreach ($tables as $table) {
8577 $sql = 'UPDATE '.$dbs->prefix().$table.' SET fk_soc = '.((int) $dest_id).' WHERE fk_soc = '.((int) $origin_id);
8578
8579 if (!$dbs->query($sql)) {
8580 if ($ignoreerrors) {
8581 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.
8582 }
8583 //$this->errors = $db->lasterror();
8584 return false;
8585 }
8586 }
8587
8588 return true;
8589 }
8590
8603 public static function commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
8604 {
8605 foreach ($tables as $table) {
8606 $sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_product = '.((int) $dest_id).' WHERE fk_product = '.((int) $origin_id);
8607
8608 if (!$dbs->query($sql)) {
8609 if ($ignoreerrors) {
8610 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.
8611 }
8612 //$this->errors = $db->lasterror();
8613 return false;
8614 }
8615 }
8616
8617 return true;
8618 }
8619
8632 public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
8633 {
8634 global $conf;
8635
8636 $buyPrice = 0;
8637
8638 if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull > 0)) {
8639 // When ForceBuyingPriceIfNull is set
8640 $buyPrice = $unitPrice * (1 - $discountPercent / 100);
8641 } else {
8642 // Get cost price for margin calculation
8643 if (!empty($fk_product) && $fk_product > 0) {
8644 if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice') {
8645 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
8646 $product = new Product($this->db);
8647 $result = $product->fetch($fk_product);
8648 if ($result <= 0) {
8649 $this->errors[] = 'ErrorProductIdDoesNotExists';
8650 return -1;
8651 }
8652 if ($product->cost_price > 0) {
8653 $buyPrice = $product->cost_price;
8654 } elseif ($product->pmp > 0) {
8655 $buyPrice = $product->pmp;
8656 }
8657 } elseif (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp') {
8658 require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
8659 $product = new Product($this->db);
8660 $result = $product->fetch($fk_product);
8661 if ($result <= 0) {
8662 $this->errors[] = 'ErrorProductIdDoesNotExists';
8663 return -1;
8664 }
8665 if ($product->pmp > 0) {
8666 $buyPrice = $product->pmp;
8667 }
8668 }
8669
8670 if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1', 'pmp', 'costprice'))) {
8671 require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
8672 $productFournisseur = new ProductFournisseur($this->db);
8673 if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0) {
8674 $buyPrice = $productFournisseur->fourn_unitprice;
8675 } elseif ($result < 0) {
8676 $this->errors[] = $productFournisseur->error;
8677 return -2;
8678 }
8679 }
8680 }
8681 }
8682 return $buyPrice;
8683 }
8684
8685 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
8705 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')
8706 {
8707 // phpcs:enable
8708 global $conf, $user, $langs;
8709
8710 include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
8711 include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
8712
8713 $sortfield = 'position_name';
8714 $sortorder = 'asc';
8715
8716 $dir = $sdir.'/';
8717 $pdir = '/';
8718
8719 $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
8720 $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
8721
8722 // For backward compatibility
8723 if ($modulepart == 'product') {
8724 if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) {
8725 $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
8726 $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
8727 }
8728 }
8729
8730 // Defined relative dir to DOL_DATA_ROOT
8731 $relativedir = '';
8732 if ($dir) {
8733 $relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
8734 $relativedir = preg_replace('/^[\\/]/', '', $relativedir);
8735 $relativedir = preg_replace('/[\\/]$/', '', $relativedir);
8736 }
8737
8738 $dirthumb = $dir.'thumbs/';
8739 $pdirthumb = $pdir.'thumbs/';
8740
8741 $return = '<!-- Photo -->'."\n";
8742 $nbphoto = 0;
8743
8744 $filearray = dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ?SORT_DESC:SORT_ASC), 1);
8745
8746 /*if (getDolGlobalInt('PRODUCT_USE_OLD_PATH_FOR_PHOTO')) // For backward compatiblity, we scan also old dirs
8747 {
8748 $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
8749 $filearray=array_merge($filearray, $filearrayold);
8750 }*/
8751
8752 completeFileArrayWithDatabaseInfo($filearray, $relativedir);
8753
8754 if (count($filearray)) {
8755 if ($sortfield && $sortorder) {
8756 $filearray = dol_sort_array($filearray, $sortfield, $sortorder);
8757 }
8758
8759 foreach ($filearray as $key => $val) {
8760 $photo = '';
8761 $file = $val['name'];
8762
8763 //if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
8764 if (image_format_supported($file) >= 0) {
8765 $nbphoto++;
8766 $photo = $file;
8767 $viewfilename = $file;
8768
8769 if ($size == 1 || $size == 'small') { // Format vignette
8770 // Find name of thumb file
8771 $photo_vignette = basename(getImageFileNameForSize($dir.$file, '_small'));
8772 if (!dol_is_file($dirthumb.$photo_vignette)) {
8773 // The thumb does not exists, so we will use the original file
8774 $dirthumb = $dir;
8775 $pdirthumb = $pdir;
8776 $photo_vignette = basename($file);
8777 }
8778
8779 // Get filesize of original file
8780 $imgarray = dol_getImageSize($dir.$photo);
8781
8782 if ($nbbyrow > 0) {
8783 if ($nbphoto == 1) {
8784 $return .= '<table class="valigntop center centpercent" style="border: 0; padding: 2px; border-spacing: 2px; border-collapse: separate;">';
8785 }
8786
8787 if ($nbphoto % $nbbyrow == 1) {
8788 $return .= '<tr class="center valignmiddle" style="border: 1px">';
8789 }
8790 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%" class="photo">'."\n";
8791 } elseif ($nbbyrow < 0) {
8792 $return .= '<div class="inline-block">'."\n";
8793 }
8794
8795 $relativefile = preg_replace('/^\//', '', $pdir.$photo);
8796 if (empty($nolink)) {
8797 $urladvanced = getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
8798 if ($urladvanced) {
8799 $return .= '<a href="'.$urladvanced.'">';
8800 } else {
8801 $return .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank" rel="noopener noreferrer">';
8802 }
8803 }
8804
8805 // Show image (width height=$maxHeight)
8806 // Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
8807 $alt = $langs->transnoentitiesnoconv('File').': '.$relativefile;
8808 $alt .= ' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
8809 if ($overwritetitle) {
8810 if (is_numeric($overwritetitle)) {
8811 $alt = '';
8812 } else {
8813 $alt = $overwritetitle;
8814 }
8815 }
8816
8817 if ($usesharelink) {
8818 if ($val['share']) {
8819 if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
8820 $return .= '<!-- Show original file (thumb not yet available with shared links) -->';
8821 $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).'">';
8822 } else {
8823 $return .= '<!-- Show original file -->';
8824 $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).'">';
8825 }
8826 } else {
8827 $return .= '<!-- Show nophoto file (because file is not shared) -->';
8828 $return .= '<img class="photo photowithmargin'.($addphotorefcss ? ' '.$addphotorefcss : '').'" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
8829 }
8830 } else {
8831 if (empty($maxHeight) || ($photo_vignette && $imgarray['height'] > $maxHeight)) {
8832 $return .= '<!-- Show thumb -->';
8833 $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).'">';
8834 } else {
8835 $return .= '<!-- Show original file -->';
8836 $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).'">';
8837 }
8838 }
8839
8840 if (empty($nolink)) {
8841 $return .= '</a>';
8842 }
8843
8844 if ($showfilename) {
8845 $return .= '<br>'.$viewfilename;
8846 }
8847 if ($showaction) {
8848 $return .= '<br>';
8849 // On propose la generation de la vignette si elle n'existe pas et si la taille est superieure aux limites
8850 if ($photo_vignette && (image_format_supported($photo) > 0) && ($this->imgWidth > $maxWidth || $this->imgHeight > $maxHeight)) {
8851 $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>';
8852 }
8853 // Special cas for product
8854 if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
8855 // Link to resize
8856 $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; ';
8857
8858 // Link to delete
8859 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
8860 $return .= img_delete().'</a>';
8861 }
8862 }
8863 $return .= "\n";
8864
8865 if ($nbbyrow > 0) {
8866 $return .= '</td>';
8867 if (($nbphoto % $nbbyrow) == 0) {
8868 $return .= '</tr>';
8869 }
8870 } elseif ($nbbyrow < 0) {
8871 $return .= '</div>'."\n";
8872 }
8873 }
8874
8875 if (empty($size)) { // Format origine
8876 $return .= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
8877
8878 if ($showfilename) {
8879 $return .= '<br>'.$viewfilename;
8880 }
8881 if ($showaction) {
8882 // Special case for product
8883 if ($modulepart == 'product' && ($user->hasRight('produit', 'creer') || $user->hasRight('service', 'creer'))) {
8884 // Link to resize
8885 $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; ';
8886
8887 // Link to delete
8888 $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&action=delete&token='.newToken().'&file='.urlencode($pdir.$viewfilename).'">';
8889 $return .= img_delete().'</a>';
8890 }
8891 }
8892 }
8893
8894 // On continue ou on arrete de boucler ?
8895 if ($nbmax && $nbphoto >= $nbmax) {
8896 break;
8897 }
8898 }
8899 }
8900
8901 if ($size == 1 || $size == 'small') {
8902 if ($nbbyrow > 0) {
8903 // Ferme tableau
8904 while ($nbphoto % $nbbyrow) {
8905 $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%">&nbsp;</td>';
8906 $nbphoto++;
8907 }
8908
8909 if ($nbphoto) {
8910 $return .= '</table>';
8911 }
8912 }
8913 }
8914 }
8915
8916 $this->nbphoto = $nbphoto;
8917
8918 return $return;
8919 }
8920
8921
8928 protected function isArray($info)
8929 {
8930 if (is_array($info)) {
8931 if (isset($info['type']) && $info['type'] == 'array') {
8932 return true;
8933 } else {
8934 return false;
8935 }
8936 }
8937 return false;
8938 }
8939
8946 public function isDate($info)
8947 {
8948 if (isset($info['type']) && ($info['type'] == 'date' || $info['type'] == 'datetime' || $info['type'] == 'timestamp')) {
8949 return true;
8950 }
8951 return false;
8952 }
8953
8960 public function isDuration($info)
8961 {
8962 if (is_array($info)) {
8963 if (isset($info['type']) && ($info['type'] == 'duration')) {
8964 return true;
8965 } else {
8966 return false;
8967 }
8968 } else {
8969 return false;
8970 }
8971 }
8972
8979 public function isInt($info)
8980 {
8981 if (is_array($info)) {
8982 if (isset($info['type']) && (preg_match('/(^int|int$)/i', $info['type']))) {
8983 return true;
8984 } else {
8985 return false;
8986 }
8987 } else {
8988 return false;
8989 }
8990 }
8991
8998 public function isFloat($info)
8999 {
9000 if (is_array($info)) {
9001 if (isset($info['type']) && (preg_match('/^(double|real|price)/i', $info['type']))) {
9002 return true;
9003 } else {
9004 return false;
9005 }
9006 }
9007 return false;
9008 }
9009
9016 public function isText($info)
9017 {
9018 if (is_array($info)) {
9019 if (isset($info['type']) && $info['type'] == 'text') {
9020 return true;
9021 } else {
9022 return false;
9023 }
9024 }
9025 return false;
9026 }
9027
9034 protected function canBeNull($info)
9035 {
9036 if (is_array($info)) {
9037 if (isset($info['notnull']) && $info['notnull'] != '1') {
9038 return true;
9039 } else {
9040 return false;
9041 }
9042 }
9043 return true;
9044 }
9045
9052 protected function isForcedToNullIfZero($info)
9053 {
9054 if (is_array($info)) {
9055 if (isset($info['notnull']) && $info['notnull'] == '-1') {
9056 return true;
9057 } else {
9058 return false;
9059 }
9060 }
9061 return false;
9062 }
9063
9070 protected function isIndex($info)
9071 {
9072 if (is_array($info)) {
9073 if (isset($info['index']) && $info['index'] == true) {
9074 return true;
9075 } else {
9076 return false;
9077 }
9078 }
9079 return false;
9080 }
9081
9082
9091 protected function setSaveQuery()
9092 {
9093 global $conf;
9094
9095 $queryarray = array();
9096 foreach ($this->fields as $field => $info) { // Loop on definition of fields
9097 // Depending on field type ('datetime', ...)
9098 if ($this->isDate($info)) {
9099 if (empty($this->{$field})) {
9100 $queryarray[$field] = null;
9101 } else {
9102 $queryarray[$field] = $this->db->idate($this->{$field});
9103 }
9104 } elseif ($this->isDuration($info)) {
9105 // $this->{$field} may be null, '', 0, '0', 123, '123'
9106 if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
9107 if (!isset($this->{$field})) {
9108 if (!empty($info['default'])) {
9109 $queryarray[$field] = $info['default'];
9110 } else {
9111 $queryarray[$field] = 0;
9112 }
9113 } else {
9114 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9115 }
9116 } else {
9117 $queryarray[$field] = null;
9118 }
9119 } elseif ($this->isInt($info) || $this->isFloat($info)) {
9120 if ($field == 'entity' && is_null($this->{$field})) {
9121 $queryarray[$field] = ((int) $conf->entity);
9122 } else {
9123 // $this->{$field} may be null, '', 0, '0', 123, '123'
9124 if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
9125 if (!isset($this->{$field})) {
9126 $queryarray[$field] = 0;
9127 } elseif ($this->isInt($info)) {
9128 $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9129 } elseif ($this->isFloat($info)) {
9130 $queryarray[$field] = (double) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
9131 }
9132 } else {
9133 $queryarray[$field] = null;
9134 }
9135 }
9136 } else {
9137 // Note: If $this->{$field} is not defined, it means there is a bug into definition of ->fields or a missing declaration of property
9138 // We should keep the warning generated by this because it is a bug somewhere else in code, not here.
9139 $queryarray[$field] = $this->{$field};
9140 }
9141
9142 if ($info['type'] == 'timestamp' && empty($queryarray[$field])) {
9143 unset($queryarray[$field]);
9144 }
9145 if (!empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) {
9146 $queryarray[$field] = null; // May force 0 to null
9147 }
9148 }
9149
9150 return $queryarray;
9151 }
9152
9159 public function setVarsFromFetchObj(&$obj)
9160 {
9161 global $db;
9162
9163 foreach ($this->fields as $field => $info) {
9164 if ($this->isDate($info)) {
9165 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') {
9166 $this->$field = '';
9167 } else {
9168 $this->$field = $db->jdate($obj->$field);
9169 }
9170 } elseif ($this->isInt($info)) {
9171 if ($field == 'rowid') {
9172 $this->id = (int) $obj->$field;
9173 } else {
9174 if ($this->isForcedToNullIfZero($info)) {
9175 if (empty($obj->$field)) {
9176 $this->$field = null;
9177 } else {
9178 $this->$field = (double) $obj->$field;
9179 }
9180 } else {
9181 if (isset($obj->$field) && (!is_null($obj->$field) || (isset($info['notnull']) && $info['notnull'] == 1))) {
9182 $this->$field = (int) $obj->$field;
9183 } else {
9184 $this->$field = null;
9185 }
9186 }
9187 }
9188 } elseif ($this->isFloat($info)) {
9189 if ($this->isForcedToNullIfZero($info)) {
9190 if (empty($obj->$field)) {
9191 $this->$field = null;
9192 } else {
9193 $this->$field = (double) $obj->$field;
9194 }
9195 } else {
9196 if (isset($obj->$field) && (!is_null($obj->$field) || (isset($info['notnull']) && $info['notnull'] == 1))) {
9197 $this->$field = (double) $obj->$field;
9198 } else {
9199 $this->$field = null;
9200 }
9201 }
9202 } else {
9203 $this->$field = isset($obj->$field) ? $obj->$field : null;
9204 }
9205 }
9206
9207 // If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
9208 if (!isset($this->fields['ref']) && isset($this->id)) {
9209 $this->ref = $this->id;
9210 }
9211 }
9212
9220 public function getFieldList($alias = '', $excludefields = array())
9221 {
9222 $keys = array_keys($this->fields);
9223 if (!empty($alias)) {
9224 $keys_with_alias = array();
9225 foreach ($keys as $fieldname) {
9226 if (!empty($excludefields)) {
9227 if (in_array($fieldname, $excludefields)) { // The field is excluded and must not be in output
9228 continue;
9229 }
9230 }
9231 $keys_with_alias[] = $alias . '.' . $fieldname;
9232 }
9233 return implode(',', $keys_with_alias);
9234 } else {
9235 return implode(',', $keys);
9236 }
9237 }
9238
9246 protected function quote($value, $fieldsentry)
9247 {
9248 if (is_null($value)) {
9249 return 'NULL';
9250 } elseif (preg_match('/^(int|double|real|price)/i', $fieldsentry['type'])) {
9251 return price2num("$value");
9252 } elseif (preg_match('/int$/i', $fieldsentry['type'])) {
9253 return (int) $value;
9254 } elseif ($fieldsentry['type'] == 'boolean') {
9255 if ($value) {
9256 return 'true';
9257 } else {
9258 return 'false';
9259 }
9260 } else {
9261 return "'".$this->db->escape($value)."'";
9262 }
9263 }
9264
9265
9273 public function createCommon(User $user, $notrigger = false)
9274 {
9275 global $langs;
9276 dol_syslog(get_class($this)."::createCommon create", LOG_DEBUG);
9277
9278 $error = 0;
9279
9280 $now = dol_now();
9281
9282 $fieldvalues = $this->setSaveQuery();
9283
9284 if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) {
9285 $fieldvalues['date_creation'] = $this->db->idate($now);
9286 }
9287 if (array_key_exists('fk_user_creat', $fieldvalues) && !($fieldvalues['fk_user_creat'] > 0)) {
9288 $fieldvalues['fk_user_creat'] = $user->id;
9289 $this->fk_user_creat = $user->id;
9290 }
9291 if (array_key_exists('user_modification_id', $fieldvalues) && !($fieldvalues['user_modification_id'] > 0)) {
9292 $fieldvalues['user_modification_id'] = $user->id;
9293 $this->user_modification_id = $user->id;
9294 }
9295 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
9296 if (array_key_exists('ref', $fieldvalues)) {
9297 $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
9298 }
9299
9300 $keys = array();
9301 $values = array(); // Array to store string forged for SQL syntax
9302 foreach ($fieldvalues as $k => $v) {
9303 $keys[$k] = $k;
9304 $value = $this->fields[$k];
9305 $values[$k] = $this->quote($v, $value); // May return string 'NULL' if $value is null
9306 }
9307
9308 // Clean and check mandatory
9309 foreach ($keys as $key) {
9310 // If field is an implicit foreign key field (so type = 'integer:...')
9311 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
9312 $values[$key] = '';
9313 }
9314 if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
9315 $values[$key] = '';
9316 }
9317
9318 if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && is_null($this->fields[$key]['default'])) {
9319 $error++;
9320 $langs->load("errors");
9321 dol_syslog("Mandatory field '".$key."' is empty and required into ->fields definition of class");
9322 $this->errors[] = $langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
9323 }
9324
9325 // If value is null and there is a default value for field
9326 if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && !is_null($this->fields[$key]['default'])) {
9327 $values[$key] = $this->quote($this->fields[$key]['default'], $this->fields[$key]);
9328 }
9329
9330 // If field is an implicit foreign key field (so type = 'integer:...')
9331 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) {
9332 if (isset($this->fields[$key]['default'])) {
9333 $values[$key] = ((int) $this->fields[$key]['default']);
9334 } else {
9335 $values[$key] = 'null';
9336 }
9337 }
9338 if (!empty($this->fields[$key]['foreignkey']) && empty($values[$key])) {
9339 $values[$key] = 'null';
9340 }
9341 }
9342
9343 if ($error) {
9344 return -1;
9345 }
9346
9347 $this->db->begin();
9348
9349 if (!$error) {
9350 $sql = "INSERT INTO ".$this->db->prefix().$this->table_element;
9351 $sql .= " (".implode(", ", $keys).')';
9352 $sql .= " VALUES (".implode(", ", $values).")"; // $values can contains 'abc' or 123
9353
9354 $res = $this->db->query($sql);
9355 if (!$res) {
9356 $error++;
9357 if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
9358 $this->errors[] = "ErrorRefAlreadyExists";
9359 } else {
9360 $this->errors[] = $this->db->lasterror();
9361 }
9362 }
9363 }
9364
9365 if (!$error) {
9366 $this->id = $this->db->last_insert_id($this->db->prefix().$this->table_element);
9367 }
9368
9369 // If we have a field ref with a default value of (PROV)
9370 if (!$error) {
9371 if (key_exists('ref', $this->fields) && $this->fields['ref']['notnull'] > 0 && key_exists('default', $this->fields['ref']) && $this->fields['ref']['default'] == '(PROV)') {
9372 $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ref = '(PROV".((int) $this->id).")' WHERE (ref = '(PROV)' OR ref = '') AND rowid = ".((int) $this->id);
9373 $resqlupdate = $this->db->query($sql);
9374
9375 if ($resqlupdate === false) {
9376 $error++;
9377 $this->errors[] = $this->db->lasterror();
9378 } else {
9379 $this->ref = '(PROV'.$this->id.')';
9380 }
9381 }
9382 }
9383
9384 // Create extrafields
9385 if (!$error) {
9386 $result = $this->insertExtraFields();
9387 if ($result < 0) {
9388 $error++;
9389 }
9390 }
9391
9392 // Create lines
9393 if (!empty($this->table_element_line) && !empty($this->fk_element)) {
9394 $num = (is_array($this->lines) ? count($this->lines) : 0);
9395 for ($i = 0; $i < $num; $i++) {
9396 $line = $this->lines[$i];
9397
9398 $keyforparent = $this->fk_element;
9399 $line->$keyforparent = $this->id;
9400
9401 // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
9402 //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
9403 if (!is_object($line)) {
9404 $line = (object) $line;
9405 }
9406
9407 $result = 0;
9408 if (method_exists($line, 'insert')) {
9409 $result = $line->insert($user, 1);
9410 } elseif (method_exists($line, 'create')) {
9411 $result = $line->create($user, 1);
9412 }
9413 if ($result < 0) {
9414 $this->error = $line->error;
9415 $this->db->rollback();
9416 return -1;
9417 }
9418 }
9419 }
9420
9421 // Triggers
9422 if (!$error && !$notrigger) {
9423 // Call triggers
9424 $result = $this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user);
9425 if ($result < 0) {
9426 $error++;
9427 }
9428 // End call triggers
9429 }
9430
9431 // Commit or rollback
9432 if ($error) {
9433 $this->db->rollback();
9434 return -1;
9435 } else {
9436 $this->db->commit();
9437 return $this->id;
9438 }
9439 }
9440
9441
9450 public function fetchCommon($id, $ref = null, $morewhere = '')
9451 {
9452 if (empty($id) && empty($ref) && empty($morewhere)) {
9453 return -1;
9454 }
9455
9456 $fieldlist = $this->getFieldList('t');
9457 if (empty($fieldlist)) {
9458 return 0;
9459 }
9460
9461 $sql = "SELECT ".$fieldlist;
9462 $sql .= " FROM ".$this->db->prefix().$this->table_element.' as t';
9463
9464 if (!empty($id)) {
9465 $sql .= ' WHERE t.rowid = '.((int) $id);
9466 } elseif (!empty($ref)) {
9467 $sql .= " WHERE t.ref = '".$this->db->escape($ref)."'";
9468 } else {
9469 $sql .= ' WHERE 1 = 1'; // usage with empty id and empty ref is very rare
9470 }
9471 if (empty($id) && isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
9472 $sql .= ' AND t.entity IN ('.getEntity($this->element).')';
9473 }
9474 if ($morewhere) {
9475 $sql .= $morewhere;
9476 }
9477 $sql .= ' LIMIT 1'; // This is a fetch, to be sure to get only one record
9478
9479 $res = $this->db->query($sql);
9480 if ($res) {
9481 $obj = $this->db->fetch_object($res);
9482 if ($obj) {
9483 $this->setVarsFromFetchObj($obj);
9484
9485 // Retrieve all extrafield
9486 // fetch optionals attributes and labels
9487 $this->fetch_optionals();
9488
9489 return $this->id;
9490 } else {
9491 return 0;
9492 }
9493 } else {
9494 $this->error = $this->db->lasterror();
9495 $this->errors[] = $this->error;
9496 return -1;
9497 }
9498 }
9499
9506 public function fetchLinesCommon($morewhere = '')
9507 {
9508 $objectlineclassname = get_class($this).'Line';
9509 if (!class_exists($objectlineclassname)) {
9510 $this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
9511 return -1;
9512 }
9513
9514 $objectline = new $objectlineclassname($this->db);
9515
9516 $sql = "SELECT ".$objectline->getFieldList('l');
9517 $sql .= " FROM ".$this->db->prefix().$objectline->table_element." as l";
9518 $sql .= " WHERE l.fk_".$this->db->escape($this->element)." = ".((int) $this->id);
9519 if ($morewhere) {
9520 $sql .= $morewhere;
9521 }
9522 if (isset($objectline->fields['position'])) {
9523 $sql .= $this->db->order('position', 'ASC');
9524 }
9525
9526 $resql = $this->db->query($sql);
9527 if ($resql) {
9528 $num_rows = $this->db->num_rows($resql);
9529 $i = 0;
9530 $this->lines = array();
9531 while ($i < $num_rows) {
9532 $obj = $this->db->fetch_object($resql);
9533 if ($obj) {
9534 $newline = new $objectlineclassname($this->db);
9535 $newline->setVarsFromFetchObj($obj);
9536
9537 $this->lines[] = $newline;
9538 }
9539 $i++;
9540 }
9541
9542 return 1;
9543 } else {
9544 $this->error = $this->db->lasterror();
9545 $this->errors[] = $this->error;
9546 return -1;
9547 }
9548 }
9549
9557 public function updateCommon(User $user, $notrigger = false)
9558 {
9559 global $conf, $langs;
9560 dol_syslog(get_class($this)."::updateCommon update", LOG_DEBUG);
9561
9562 $error = 0;
9563
9564 $now = dol_now();
9565
9566 // $this->oldcopy should have been set by the caller of update
9567 //if (empty($this->oldcopy)) {
9568 // $this->oldcopy = dol_clone($this);
9569 //}
9570
9571 $fieldvalues = $this->setSaveQuery();
9572
9573 if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) {
9574 $fieldvalues['date_modification'] = $this->db->idate($now);
9575 }
9576 if (array_key_exists('fk_user_modif', $fieldvalues) && !($fieldvalues['fk_user_modif'] > 0)) {
9577 $fieldvalues['fk_user_modif'] = $user->id;
9578 }
9579 unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
9580 if (array_key_exists('ref', $fieldvalues)) {
9581 $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
9582 }
9583
9584 // Add quotes and escape on fields with type string
9585 $keys = array();
9586 $values = array();
9587 $tmp = array();
9588 foreach ($fieldvalues as $k => $v) {
9589 $keys[$k] = $k;
9590 $value = $this->fields[$k];
9591 $values[$k] = $this->quote($v, $value);
9592 $tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
9593 }
9594
9595 // Clean and check mandatory fields
9596 foreach ($keys as $key) {
9597 if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') {
9598 $values[$key] = ''; // This is an implicit foreign key field
9599 }
9600 if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') {
9601 $values[$key] = ''; // This is an explicit foreign key field
9602 }
9603
9604 //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
9605 /*
9606 if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
9607 {
9608 $error++;
9609 $this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
9610 }*/
9611 }
9612
9613 $sql = 'UPDATE '.$this->db->prefix().$this->table_element.' SET '.implode(', ', $tmp).' WHERE rowid='.((int) $this->id);
9614
9615 $this->db->begin();
9616
9617 if (!$error) {
9618 $res = $this->db->query($sql);
9619 if (!$res) {
9620 $error++;
9621 $this->errors[] = $this->db->lasterror();
9622 }
9623 }
9624
9625 // Update extrafield
9626 if (!$error) {
9627 $result = $this->insertExtraFields();
9628 if ($result < 0) {
9629 $error++;
9630 }
9631 }
9632
9633 // Triggers
9634 if (!$error && !$notrigger) {
9635 // Call triggers
9636 $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
9637 if ($result < 0) {
9638 $error++;
9639 } //Do also here what you must do to rollback action if trigger fail
9640 // End call triggers
9641 }
9642
9643 // Commit or rollback
9644 if ($error) {
9645 $this->db->rollback();
9646 return -1;
9647 } else {
9648 $this->db->commit();
9649 return $this->id;
9650 }
9651 }
9652
9661 public function deleteCommon(User $user, $notrigger = false, $forcechilddeletion = 0)
9662 {
9663 dol_syslog(get_class($this)."::deleteCommon delete", LOG_DEBUG);
9664
9665 $error = 0;
9666
9667 $this->db->begin();
9668
9669 if ($forcechilddeletion) { // Force also delete of childtables that should lock deletion in standard case when option force is off
9670 foreach ($this->childtables as $table) {
9671 $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
9672 $resql = $this->db->query($sql);
9673 if (!$resql) {
9674 $this->error = $this->db->lasterror();
9675 $this->errors[] = $this->error;
9676 $this->db->rollback();
9677 return -1;
9678 }
9679 }
9680 } elseif (!empty($this->childtables)) { // If object has childs linked with a foreign key field, we check all child tables.
9681 $objectisused = $this->isObjectUsed($this->id);
9682 if (!empty($objectisused)) {
9683 dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
9684 $this->error = 'ErrorRecordHasChildren';
9685 $this->errors[] = $this->error;
9686 $this->db->rollback();
9687 return 0;
9688 }
9689 }
9690
9691 // Delete cascade first
9692 if (is_array($this->childtablesoncascade) && !empty($this->childtablesoncascade)) {
9693 foreach ($this->childtablesoncascade as $table) {
9694 $deleteFromObject = explode(':', $table);
9695 if (count($deleteFromObject) >= 2) {
9696 $className = str_replace('@', '', $deleteFromObject[0]);
9697 $filePath = $deleteFromObject[1];
9698 $columnName = $deleteFromObject[2];
9699 $TMoreSQL = array();
9700 $more_sql = $deleteFromObject[3];
9701 if (!empty($more_sql)) {
9702 $TMoreSQL['customsql'] = $more_sql;
9703 }
9704 if (dol_include_once($filePath)) {
9705 $childObject = new $className($this->db);
9706 if (method_exists($childObject, 'deleteByParentField')) {
9707 $result = $childObject->deleteByParentField($this->id, $columnName, $TMoreSQL);
9708 if ($result < 0) {
9709 $error++;
9710 $this->errors[] = $childObject->error;
9711 break;
9712 }
9713 } else {
9714 $error++;
9715 $this->errors[] = "You defined a cascade delete on an object $childObject but there is no method deleteByParentField for it";
9716 break;
9717 }
9718 } else {
9719 $error++;
9720 $this->errors[] = 'Cannot include child class file '.$filePath;
9721 break;
9722 }
9723 } else {
9724 // Delete record in child table
9725 $sql = "DELETE FROM ".$this->db->prefix().$table." WHERE ".$this->fk_element." = ".((int) $this->id);
9726
9727 $resql = $this->db->query($sql);
9728 if (!$resql) {
9729 $error++;
9730 $this->error = $this->db->lasterror();
9731 $this->errors[] = $this->error;
9732 break;
9733 }
9734 }
9735 }
9736 }
9737
9738 if (!$error) {
9739 if (!$notrigger) {
9740 // Call triggers
9741 $result = $this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
9742 if ($result < 0) {
9743 $error++;
9744 } // Do also here what you must do to rollback action if trigger fail
9745 // End call triggers
9746 }
9747 }
9748
9749 // Delete llx_ecm_files
9750 if (!$error) {
9751 $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
9752 if (!$res) {
9753 $error++;
9754 }
9755 }
9756
9757 // Delete linked object
9758 $res = $this->deleteObjectLinked();
9759 if ($res < 0) {
9760 $error++;
9761 }
9762
9763 if (!$error && !empty($this->isextrafieldmanaged)) {
9764 $result = $this->deleteExtraFields();
9765 if ($result < 0) {
9766 $error++;
9767 }
9768 }
9769
9770 if (!$error) {
9771 $sql = 'DELETE FROM '.$this->db->prefix().$this->table_element.' WHERE rowid='.((int) $this->id);
9772
9773 $resql = $this->db->query($sql);
9774 if (!$resql) {
9775 $error++;
9776 $this->errors[] = $this->db->lasterror();
9777 }
9778 }
9779
9780 // Commit or rollback
9781 if ($error) {
9782 $this->db->rollback();
9783 return -1;
9784 } else {
9785 $this->db->commit();
9786 return 1;
9787 }
9788 }
9789
9800 public function deleteByParentField($parentId = 0, $parentField = '', $filter = array(), $filtermode = "AND")
9801 {
9802 global $user;
9803
9804 $error = 0;
9805 $deleted = 0;
9806
9807 if (!empty($parentId) && !empty($parentField)) {
9808 $this->db->begin();
9809
9810 $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element;
9811 $sql .= " WHERE ".$parentField." = ".(int) $parentId;
9812
9813 // Manage filters
9814 $sqlwhere = array();
9815 if (count($filter) > 0) {
9816 foreach ($filter as $key => $value) {
9817 if ($key == 'customsql') {
9818 $sqlwhere[] = $value;
9819 } elseif (strpos($value, '%') === false) {
9820 $sqlwhere[] = $key." IN (".$this->db->sanitize($this->db->escape($value)).")";
9821 } else {
9822 $sqlwhere[] = $key." LIKE '%".$this->db->escape($value)."%'";
9823 }
9824 }
9825 }
9826 if (count($sqlwhere) > 0) {
9827 $sql .= " AND (".implode(" ".$filtermode." ", $sqlwhere).")";
9828 }
9829
9830 $resql = $this->db->query($sql);
9831 if (!$resql) {
9832 $this->errors[] = $this->db->lasterror();
9833 $error++;
9834 } else {
9835 while ($obj = $this->db->fetch_object($resql)) {
9836 $result = $this->fetch($obj->rowid);
9837 if ($result < 0) {
9838 $error++;
9839 $this->errors[] = $this->error;
9840 } else {
9841 if (get_class($this) == 'Contact') { // TODO special code because delete() for contact has not been standardized like other delete.
9842 $result = $this->delete();
9843 } else {
9844 $result = $this->delete($user);
9845 }
9846 if ($result < 0) {
9847 $error++;
9848 $this->errors[] = $this->error;
9849 } else {
9850 $deleted++;
9851 }
9852 }
9853 }
9854 }
9855
9856 if (empty($error)) {
9857 $this->db->commit();
9858 return $deleted;
9859 } else {
9860 $this->error = implode(', ', $this->errors);
9861 $this->db->rollback();
9862 return $error * -1;
9863 }
9864 }
9865
9866 return $deleted;
9867 }
9868
9877 public function deleteLineCommon(User $user, $idline, $notrigger = false)
9878 {
9879 global $conf;
9880
9881 $error = 0;
9882
9883 $tmpforobjectclass = get_class($this);
9884 $tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line';
9885
9886 $this->db->begin();
9887
9888 // Call trigger
9889 $result = $this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user);
9890 if ($result < 0) {
9891 $error++;
9892 }
9893 // End call triggers
9894
9895 if (empty($error)) {
9896 $sql = "DELETE FROM ".$this->db->prefix().$this->table_element_line;
9897 $sql .= " WHERE rowid = ".((int) $idline);
9898
9899 $resql = $this->db->query($sql);
9900 if (!$resql) {
9901 $this->error = "Error ".$this->db->lasterror();
9902 $error++;
9903 }
9904 }
9905
9906 if (empty($error)) {
9907 // Remove extrafields
9908 $tmpobjectline = new $tmpforobjectlineclass($this->db);
9909 if (!isset($tmpobjectline->isextrafieldmanaged) || !empty($tmpobjectline->isextrafieldmanaged)) {
9910 $tmpobjectline->id = $idline;
9911 $result = $tmpobjectline->deleteExtraFields();
9912 if ($result < 0) {
9913 $error++;
9914 $this->error = "Error ".get_class($this)."::deleteLineCommon deleteExtraFields error -4 ".$tmpobjectline->error;
9915 }
9916 }
9917 }
9918
9919 if (empty($error)) {
9920 $this->db->commit();
9921 return 1;
9922 } else {
9923 dol_syslog(get_class($this)."::deleteLineCommon ERROR:".$this->error, LOG_ERR);
9924 $this->db->rollback();
9925 return -1;
9926 }
9927 }
9928
9929
9939 public function setStatusCommon($user, $status, $notrigger = 0, $triggercode = '')
9940 {
9941 $error = 0;
9942
9943 $this->db->begin();
9944
9945 $statusfield = 'status';
9946 if (in_array($this->element, array('don', 'donation', 'shipping'))) {
9947 $statusfield = 'fk_statut';
9948 }
9949
9950 $sql = "UPDATE ".$this->db->prefix().$this->table_element;
9951 $sql .= " SET ".$statusfield." = ".((int) $status);
9952 $sql .= " WHERE rowid = ".((int) $this->id);
9953
9954 if ($this->db->query($sql)) {
9955 if (!$error) {
9956 $this->oldcopy = clone $this;
9957 }
9958
9959 if (!$error && !$notrigger) {
9960 // Call trigger
9961 $result = $this->call_trigger($triggercode, $user);
9962 if ($result < 0) {
9963 $error++;
9964 }
9965 }
9966
9967 if (!$error) {
9968 $this->status = $status;
9969 $this->db->commit();
9970 return 1;
9971 } else {
9972 $this->db->rollback();
9973 return -1;
9974 }
9975 } else {
9976 $this->error = $this->db->error();
9977 $this->db->rollback();
9978 return -1;
9979 }
9980 }
9981
9982
9989 public function initAsSpecimenCommon()
9990 {
9991 global $user;
9992
9993 $this->id = 0;
9994 $this->specimen = 1;
9995 $fields = array(
9996 'label' => 'This is label',
9997 'ref' => 'ABCD1234',
9998 'description' => 'This is a description',
9999 'qty' => 123.12,
10000 'note_public' => 'Public note',
10001 'note_private' => 'Private note',
10002 'date_creation' => (dol_now() - 3600 * 48),
10003 'date_modification' => (dol_now() - 3600 * 24),
10004 'fk_user_creat' => $user->id,
10005 'fk_user_modif' => $user->id,
10006 'date' => dol_now(),
10007 );
10008 foreach ($fields as $key => $value) {
10009 if (array_key_exists($key, $this->fields)) {
10010 $this->{$key} = $value;
10011 }
10012 }
10013
10014 // Force values to default values when known
10015 if (property_exists($this, 'fields')) {
10016 foreach ($this->fields as $key => $value) {
10017 // If fields are already set, do nothing
10018 if (array_key_exists($key, $fields)) {
10019 continue;
10020 }
10021
10022 if (!empty($value['default'])) {
10023 $this->$key = $value['default'];
10024 }
10025 }
10026 }
10027
10028 return 1;
10029 }
10030
10031
10032 /* Part for comments */
10033
10038 public function fetchComments()
10039 {
10040 require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
10041
10042 $comment = new Comment($this->db);
10043 $result = $comment->fetchAllFor($this->element, $this->id);
10044 if ($result < 0) {
10045 $this->errors = array_merge($this->errors, $comment->errors);
10046 return -1;
10047 } else {
10048 $this->comments = $comment->comments;
10049 }
10050 return count($this->comments);
10051 }
10052
10058 public function getNbComments()
10059 {
10060 return count($this->comments);
10061 }
10062
10069 public function trimParameters($parameters)
10070 {
10071 if (!is_array($parameters)) {
10072 return;
10073 }
10074 foreach ($parameters as $parameter) {
10075 if (isset($this->$parameter)) {
10076 $this->$parameter = trim($this->$parameter);
10077 }
10078 }
10079 }
10080
10081 /* Part for categories/tags */
10082
10093 public function getCategoriesCommon($type_categ)
10094 {
10095 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10096
10097 // Get current categories
10098 $c = new Categorie($this->db);
10099 $existing = $c->containing($this->id, $type_categ, 'id');
10100
10101 return $existing;
10102 }
10103
10116 public function setCategoriesCommon($categories, $type_categ = '', $remove_existing = true)
10117 {
10118 // Handle single category
10119 if (!is_array($categories)) {
10120 $categories = array($categories);
10121 }
10122
10123 dol_syslog(get_class($this)."::setCategoriesCommon Oject Id:".$this->id.' type_categ:'.$type_categ.' nb tag add:'.count($categories), LOG_DEBUG);
10124
10125 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10126
10127 if (empty($type_categ)) {
10128 dol_syslog(__METHOD__.': Type '.$type_categ.'is an unknown category type. Done nothing.', LOG_ERR);
10129 return -1;
10130 }
10131
10132 // Get current categories
10133 $c = new Categorie($this->db);
10134 $existing = $c->containing($this->id, $type_categ, 'id');
10135 if ($remove_existing) {
10136 // Diff
10137 if (is_array($existing)) {
10138 $to_del = array_diff($existing, $categories);
10139 $to_add = array_diff($categories, $existing);
10140 } else {
10141 $to_del = array(); // Nothing to delete
10142 $to_add = $categories;
10143 }
10144 } else {
10145 $to_del = array(); // Nothing to delete
10146 $to_add = array_diff($categories, $existing);
10147 }
10148
10149 $error = 0;
10150 $ok = 0;
10151
10152 // Process
10153 foreach ($to_del as $del) {
10154 if ($c->fetch($del) > 0) {
10155 $result=$c->del_type($this, $type_categ);
10156 if ($result < 0) {
10157 $error++;
10158 $this->error = $c->error;
10159 $this->errors = $c->errors;
10160 break;
10161 } else {
10162 $ok += $result;
10163 }
10164 }
10165 }
10166 foreach ($to_add as $add) {
10167 if ($c->fetch($add) > 0) {
10168 $result = $c->add_type($this, $type_categ);
10169 if ($result < 0) {
10170 $error++;
10171 $this->error = $c->error;
10172 $this->errors = $c->errors;
10173 break;
10174 } else {
10175 $ok += $result;
10176 }
10177 }
10178 }
10179
10180 return $error ? (-1 * $error) : $ok;
10181 }
10182
10191 public function cloneCategories($fromId, $toId, $type = '')
10192 {
10193 $this->db->begin();
10194
10195 if (empty($type)) {
10196 $type = $this->table_element;
10197 }
10198
10199 require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
10200 $categorystatic = new Categorie($this->db);
10201
10202 $sql = "INSERT INTO ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type])." (fk_categorie, fk_product)";
10203 $sql .= " SELECT fk_categorie, $toId FROM ".$this->db->prefix()."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type]);
10204 $sql .= " WHERE fk_product = ".((int) $fromId);
10205
10206 if (!$this->db->query($sql)) {
10207 $this->error = $this->db->lasterror();
10208 $this->db->rollback();
10209 return -1;
10210 }
10211
10212 $this->db->commit();
10213 return 1;
10214 }
10215
10222 public function deleteEcmFiles($mode = 0)
10223 {
10224 global $conf;
10225
10226 $this->db->begin();
10227
10228 // Delete in database with mode 0
10229 if ($mode == 0) {
10230 switch ($this->element) {
10231 case 'propal':
10232 $element = 'propale';
10233 break;
10234 case 'product':
10235 $element = 'produit';
10236 break;
10237 case 'order_supplier':
10238 $element = 'fournisseur/commande';
10239 break;
10240 case 'invoice_supplier':
10241 $element = 'fournisseur/facture/'.get_exdir($this->id, 2, 0, 1, $this, 'invoice_supplier');
10242 break;
10243 case 'shipping':
10244 $element = 'expedition/sending';
10245 break;
10246 default:
10247 $element = $this->element;
10248 }
10249
10250 // Delete ecm_files extrafields
10251 $sql = "DELETE FROM ".$this->db->prefix()."ecm_files_extrafields WHERE fk_object IN (";
10252 $sql .= " SELECT rowid FROM ".$this->db->prefix()."ecm_files WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
10253 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
10254 $sql .= ")";
10255
10256 if (!$this->db->query($sql)) {
10257 $this->error = $this->db->lasterror();
10258 $this->db->rollback();
10259 return false;
10260 }
10261
10262 // Delete ecm_files
10263 $sql = "DELETE FROM ".$this->db->prefix()."ecm_files";
10264 $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
10265 $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".((int) $conf->entity); // No need of getEntity here
10266
10267 if (!$this->db->query($sql)) {
10268 $this->error = $this->db->lasterror();
10269 $this->db->rollback();
10270 return false;
10271 }
10272 }
10273
10274 // Delete in database with mode 1
10275 if ($mode == 1) {
10276 $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files_extrafields";
10277 $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).")";
10278 $resql = $this->db->query($sql);
10279 if (!$resql) {
10280 $this->error = $this->db->lasterror();
10281 $this->db->rollback();
10282 return false;
10283 }
10284
10285 $sql = 'DELETE FROM '.$this->db->prefix()."ecm_files";
10286 $sql .= " WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? "" : "@".$this->module))."' AND src_object_id = ".((int) $this->id);
10287 $resql = $this->db->query($sql);
10288 if (!$resql) {
10289 $this->error = $this->db->lasterror();
10290 $this->db->rollback();
10291 return false;
10292 }
10293 }
10294
10295 $this->db->commit();
10296 return true;
10297 }
10298}
ajax_combobox($htmlname, $events=array(), $minLengthToAutocomplete=0, $forcefocus=0, $widthTypeOfAutocomplete='resolve', $idforemptyvalue='-1', $morecss='')
Convert a html select field into an ajax combobox.
Definition ajax.lib.php:464
$object ref
Definition info.php:78
Class to manage categories.
Class to manage comment.
Parent class of all other business classes (invoices, contracts, proposals, orders,...
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
getCategoriesCommon($type_categ)
Sets object to given categories.
indexFile($destfull, $update_main_doc_field)
Index a file into the ECM database.
getFormatedSupplierRef($objref)
Return supplier ref for screen output.
line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
clearFieldError($fieldKey)
clear validation message result for a field
deleteEcmFiles($mode=0)
Delete related files of object in database.
getLastMainDocLink($modulepart, $initsharekey=0, $relativelink=0)
Return the link of last main doc file for direct public download.
liste_type_contact($source='internal', $order='position', $option=0, $activeonly=0, $code='')
Return array with list of possible values for type of contacts.
getTooltipContent($params)
getTooltipContent
swapContactStatus($rowid)
Update status of a contact linked to object.
getFieldError($fieldKey)
get field error message
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add an object link into llx_element_element.
updateObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $f_user=null, $notrigger=0)
Update object linked of a current object.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
liste_contact($statusoflink=-1, $source='external', $list=0, $code='', $status=-1, $arrayoftcids=array())
Get array of all contacts for an object.
fetchObjectFrom($table, $field, $key, $element=null)
Load object from specific field.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this->socid or $this->fk_soc, into this->thirdparty.
deleteLineCommon(User $user, $idline, $notrigger=false)
Delete a line of object in database.
static deleteAllItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
Function used to remove all items linked to an object id in association table.
deleteByParentField($parentId=0, $parentField='', $filter=array(), $filtermode="AND")
Delete all child object from a parent ID.
setFieldError($fieldKey, $msg='')
set validation error message a field
validateField($fields, $fieldKey, $fieldValue)
Return validation test result for a field.
setMulticurrencyCode($code)
Change the multicurrency code.
add_element_resource($resource_id, $resource_type, $busy=0, $mandatory=0)
Add resources to the current object : add entry into llx_element_resources Need $this->element & $thi...
fetch_projet()
Load the project with id $this->fk_project into this->project.
getIdContact($source, $code, $status=0)
Return id of contacts for a source and a contact code.
setDocModel($user, $modelpdf)
Set last model used by doc generator.
updateExtraField($key, $trigger=null, $userused=null)
Update 1 extra field value for the current object.
setPaymentTerms($id, $deposit_percent=null)
Change the payments terms.
isFloat($info)
Function test if type is float.
setExtraParameters()
Set extra parameters.
setErrorsFromObject($object)
setErrorsFromObject
update_note_public($note)
Update public note (kept for backward compatibility)
getSpecialCode($lineid)
Get special code of a line.
fetchCommon($id, $ref=null, $morewhere='')
Load object in memory from the database.
clearObjectLinkedCache()
Clear the cache saying that all linked object were already loaded.
update_ref_ext($ref_ext)
Update external ref of element.
fetchOneLike($ref)
Looks for an object with ref matching the wildcard provided It does only work when $this->table_ref_f...
showOptionals($extrafields, $mode='view', $params=null, $keysuffix='', $keyprefix='', $onetrtd=0, $display_type='card')
Function to show lines of extrafields with output datas.
hasProductsOrServices($predefined=-1)
Function to say how many lines object contains.
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check an object id/ref exists If you don't need/want to instantiate object and just need to know if o...
isObjectUsed($id=0, $entity=0)
Function to check if an object is used by others (by children).
createCommon(User $user, $notrigger=false)
Create object into database.
updateRangOfLine($rowid, $rang)
Update position of line (rang)
deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='', $f_user=null, $notrigger=0)
Delete all links between an object $this.
getDefaultCreateValueFor($fieldname, $alternatevalue=null, $type='alphanohtml')
Return the default value to use for a field when showing the create form of object.
getTotalWeightVolume()
Return into unit=0, the calculated total of weight and volume of all lines * qty Calculate by adding ...
deleteCommon(User $user, $notrigger=false, $forcechilddeletion=0)
Delete object in database.
update_note($note, $suffix='', $notrigger=0)
Update note of element.
getFullAddress($withcountry=0, $sep="\n", $withregion=0, $extralangcode='')
Return full address of contact.
fetch_project()
Load the project with id $this->fk_project into this->project.
setStatut($status, $elementId=null, $elementType='', $trigkey='', $fieldstatus='fk_statut')
Set status of an object.
update_price($exclspec=0, $roundingadjust='none', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines).
getFieldList($alias='', $excludefields=array())
Function to concat keys of fields.
getNbComments()
Return nb comments already posted.
setVarsFromFetchObj(&$obj)
Function to load data from a SQL pointer into properties of current object $this.
printObjectLines($action, $seller, $buyer, $selected=0, $dateSelector=0, $defaulttpldir='/core/tpl')
Return HTML table for object lines TODO Move this into an output class file (htmlline....
getChildrenOfLine($id, $includealltree=0)
Get children of line.
updateExtraLanguages($key, $trigger=null, $userused=null)
Update an extra language value for the current object.
deleteExtraFields()
Delete all extra fields values for the current object.
setStatusCommon($user, $status, $notrigger=0, $triggercode='')
Set to a status.
addThumbs($file)
Build thumb.
setSaveQuery()
Function to return the array of data key-value from the ->fields and all the ->properties of an objec...
setValuesForExtraLanguages($onlykey='')
Fill array_options property of object by extrafields value (using for data sent by forms)
insertExtraLanguages($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
setTransportMode($id)
Change the transport mode methods.
isArray($info)
Function test if type is array.
isInt($info)
Function test if type is integer.
fetchObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $clause='OR', $alsosametype=1, $orderby='sourcetype', $loadalsoobjects=1)
Fetch array of objects linked to current object (object of enabled modules only).
delete_contact($rowid, $notrigger=0)
Delete a link to contact line.
updateLineUp($rowid, $rang)
Update position of line up (rang)
fetch_user($userid)
Load the user with id $userid into this->user.
errorsToString()
Method to output saved errors.
setBankAccount($fk_account, $notrigger=false, $userused=null)
Change the bank account.
getListContactId($source='external')
Return list of id of contacts of object.
setWarehouse($warehouse_id)
Change the warehouse.
setDeliveryAddress($id)
Define delivery address.
getTotalDiscount()
Function that returns the total amount HT of discounts applied for all lines.
setShippingMethod($shipping_method_id, $notrigger=false, $userused=null)
Change the shipping method.
initAsSpecimenCommon()
Initialise object with example values Id must be 0 if object instance is a specimen.
printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected=0, $extrafields=null, $defaulttpldir='/core/tpl')
Return HTML content of a detail line TODO Move this into an output class file (htmlline....
copy_linked_contact($objFrom, $source='internal')
Copy contact from one element to current.
static commonReplaceThirdparty(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
getValueFrom($table, $id, $field)
Getter generic.
trimParameters($parameters)
Trim object parameters.
fetch_product()
Load the product with id $this->fk_product into this->product.
isIndex($info)
Function test if is indexed.
quote($value, $fieldsentry)
Add quote to field value if necessary.
formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir='/core/tpl')
Show add free and predefined products/services form.
fetchComments()
Load comments linked with current task.
line_down($rowid, $fk_parent_line=true)
Update a line to have a higher rank.
setValueFrom($field, $value, $table='', $id=null, $format='', $id_field='', $fuser=null, $trigkey='', $fk_user_field='fk_user_modif')
Setter generic.
fetch_contact($contactid=null)
Load object contact with id=$this->contact_id into $this->contact.
setRetainedWarrantyPaymentTerms($id)
Change the retained warranty payments terms.
delThumbs($file)
Delete thumbs.
show_photos($modulepart, $sdir, $size=0, $nbmax=0, $nbbyrow=5, $showfilename=0, $showaction=0, $maxHeight=120, $maxWidth=160, $nolink=0, $overwritetitle=0, $usesharelink=0, $cache='', $addphotorefcss='photoref')
Show photos of an object (nbmax maximum), into several columns.
updateCommon(User $user, $notrigger=false)
Update object into database.
static commonReplaceProduct(DoliDB $dbs, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
getRangOfLine($rowid)
Get position of line (rang)
showInputField($val, $key, $value, $moreparam='', $keysuffix='', $keyprefix='', $morecss=0, $nonewbutton=0)
Return HTML string to put an input field into a page Code very similar with showInputField of extra f...
delete_resource($rowid, $element, $notrigger=0)
Delete a link to resource line.
update_contact($rowid, $statut, $type_contact_id=0, $fk_socpeople=0)
Update a link to contact line.
load_previous_next_ref($filter, $fieldid, $nodbprefix=0)
Load properties id_previous and id_next by comparing $fieldid with $this->ref.
setCategoriesCommon($categories, $type_categ='', $remove_existing=true)
Sets object to given categories.
fetchValuesForExtraLanguages()
Function to get alternative languages of a data into $this->array_languages This method is NOT called...
fetchNoCompute($id)
Function to make a fetch but set environment to avoid to load computed values before.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
getJSListDependancies($type='_extra')
setProject($projectid, $notrigger=0)
Link element with a project.
isForcedToNullIfZero($info)
Function test if field is forced to null if zero or empty.
line_ajaxorder($rows)
Update position of line with ajax (rang)
printOriginLinesList($restrictlist='', $selectedLines=array())
Return HTML table table of source object lines TODO Move this and previous function into output html ...
fetchLinesCommon($morewhere='')
Load object in memory from the database.
fetch_origin()
Read linked origin object.
getIdOfLine($rang)
Get rowid of the line relative to its position.
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
static getAllItemsLinkedByObjectID($fk_object_where, $field_select, $field_where, $table_element)
Function used to get an array with all items linked to an object id in association table.
__clone()
Overwrite magic function to solve problem of cloning object that are kept as references.
setPaymentMethods($id)
Change the payments methods.
updateLineDown($rowid, $rang, $max)
Update position of line down (rang)
delete_linked_contact($source='', $code='')
Delete all links between an object $this and all its contacts in llx_element_contact.
getFormatedCustomerRef($objref)
Return customer ref for screen output.
getTooltipContentArray($params)
Return array of datas to show into a tooltip.
isText($info)
Function test if type is text.
isDate($info)
Function test if type is date.
printOriginLine($line, $var, $restrictlist='', $defaulttpldir='/core/tpl', $selectedLines=array())
Return HTML with a line of table array of source object lines TODO Move this and previous function in...
getCanvas($id=0, $ref='')
Load type of canvas of an object if it exists.
line_up($rowid, $fk_parent_line=true)
Update a line to have a lower rank.
isDuration($info)
Function test if type is duration.
canBeNull($info)
Function test if field can be null.
listeTypeContacts($source='internal', $option=0, $activeonly=0, $code='', $element='', $excludeelement='')
Return array with list of possible values for type of contacts.
call_trigger($triggerName, $user)
Call trigger based on this instance.
getRights()
Returns the rights used for this class.
add_contact($fk_socpeople, $type_contact, $source='external', $notrigger=0)
Add a link between element $this->element and a contact.
cloneCategories($fromId, $toId, $type='')
Copy related categories to another object.
fetch_barcode()
Load data for barcode into properties ->barcode_type* Properties ->barcode_type that is id of barcode...
Class to manage contact/addresses.
Class to manage absolute discounts.
Class to manage a WYSIWYG editor.
Class to manage Dolibarr database access.
Class to manage ECM files.
Class to manage standard extra fields.
Class to manage generation of HTML components Only common components must be here.
Class to manage triggers.
static getIdAndTxFromCode($dbs, $code, $date_document='')
Get id and rate of currency from code.
Class to manage predefined suppliers products.
Class to manage products or services.
const TYPE_SERVICE
Service.
Class to manage projects.
Class to manage third parties objects (customers, suppliers, prospects...)
Class to manage translations.
Class to manage Dolibarr users.
Class toolbox to validate values.
print $langs trans("Ref").' m m m statut
Definition index.php:152
getCountry($searchkey, $withcode='', $dbtouse=0, $outputlangs='', $entconv=1, $searchlabel='')
Return country label, code or id from an id, code or label.
getState($id, $withcode='', $dbtouse=0, $withregion=0, $outputlangs='', $entconv=1)
Return state translated from an id.
convertSecondToTime($iSecond, $format='all', $lengthOfDay=86400, $lengthOfWeek=7)
Return, in clear text, value of a number of seconds in days, hours and minutes.
Definition date.lib.php:241
dol_meta_create($object)
Create a meta file with document file into same directory.
dol_delete_file($file, $disableglob=0, $nophperrors=0, $nohook=0, $object=null, $allowdotdot=false, $indexdatabase=1, $nolog=0)
Remove a file or several files with a mask.
dol_is_file($pathoffile)
Return if path is a file.
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0, $nbsecondsold=0)
Scan a directory and return a list of files/directories.
Definition files.lib.php:62
completeFileArrayWithDatabaseInfo(&$filearray, $relativedir)
Complete $filearray with data from database.
dol_delete_preview($object)
Delete all preview files linked to object instance.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed informations (by default a local PHP server timestamp) Re...
vatrate($rate, $addpercent=false, $info_bits=0, $usestarfornpr=0, $html=0)
Return a string with VAT rate label formated for view output Used into pdf and HTML pages.
dol_print_ip($ip, $mode=0)
Return an IP formated to be shown on screen.
img_delete($titlealt='default', $other='class="pictodelete"', $morecss='')
Show delete logo.
dol_osencode($str)
Return a string encoded into OS filesystem encoding.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
get_date_range($date_start, $date_end, $format='', $outputlangs='', $withparenthesis=1)
Format output for start and end date.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
img_object($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
dol_format_address($object, $withcountry=0, $sep="\n", $outputlangs='', $mode=0, $extralangcode='')
Return a formated address (part address/zip/town/state) according to country rules.
dol_string_nospecial($str, $newstr='_', $badcharstoreplace='', $badcharstoremove='', $keepspaces=0)
Clean a string from all punctuation characters to use it as a ref or login.
price($amount, $form=0, $outlangs='', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code='')
Function to format a value into an amount for visual output Function used into PDF and HTML pages.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
dol_print_email($email, $cid=0, $socid=0, $addlink=0, $max=64, $showinvalid=1, $withpicto=0)
Show EMail link formatted for HTML output.
dol_print_phone($phone, $countrycode='', $cid=0, $socid=0, $addlink='', $separ="&nbsp;", $withpicto='', $titlealt='', $adddivfloat=0)
Format phone numbers according to country.
dol_now($mode='auto')
Return date for now.
getDolGlobalInt($key, $default=0)
Return dolibarr global constant int value.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
dol_sort_array(&$array, $index, $order='asc', $natsort=0, $case_sensitive=0, $keepindex=0)
Advanced sort array by second index function, which produces ascending (default) or descending output...
if(!function_exists( 'dol_getprefix')) dol_include_once($relpath, $classname='')
Make an include_once using default root and alternate root if it fails.
dol_eval($s, $returnvalue=0, $hideerrors=1, $onlysimplestring='1')
Replace eval function to add more security.
getImageFileNameForSize($file, $extName, $extImgTarget='')
Return the filename of file to get the thumbs.
getAdvancedPreviewUrl($modulepart, $relativepath, $alldata=0, $param='')
Return URL we can use for advanced preview links.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0)
Set event messages in dol_events session object.
dol_print_url($url, $target='_blank', $max=32, $withpicto=0, $morecss='float')
Show Url link.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
dol_trunc($string, $size=40, $trunc='right', $stringencoding='UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding '…' if string larger than length.
dol_htmlentitiesbr($stringtoencode, $nl2brmode=0, $pagecodefrom='UTF-8', $removelasteolbr=1)
This function is called to encode a string into a HTML string but differs from htmlentities because a...
getDolGlobalString($key, $default='')
Return dolibarr global constant string value.
getDictionaryValue($tablename, $field, $id, $checkentity=false, $rowidfield='rowid')
Return the value of a filed into a dictionary for the record $id.
get_exdir($num, $level, $alpha, $withoutslash, $object, $modulepart='')
Return a path to have a the directory according to object where files are stored.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...
vignette($file, $maxWidth=160, $maxHeight=120, $extName='_small', $quality=50, $outdir='thumbs', $targetformat=0)
Create a thumbnail from an image file (Supported extensions are gif, jpg, png and bmp).
getDefaultImageSizes()
Return default values for image sizes.
dol_getImageSize($file, $url=false)
Return size of image file on disk (Supported extensions are gif, jpg, png, bmp and webp)
image_format_supported($file, $acceptsvg=0)
Return if a filename is file name of a supported image format.
query($query, $usesavepoint=0, $type='auto', $result_mode=0)
Execute a SQL request and return the resultset.
publicphonebutton2 phonegreen basiclayout basiclayout TotalHT VATCode TotalVAT TotalLT1 TotalLT2 TotalTTC TotalHT clearboth nowraponall right right takeposterminal SELECT e rowid
Definition invoice.php:1632
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller='', $localtaxes_array='', $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:86
$conf db user
Definition repair.php:124
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:120
getRandomPassword($generic=false, $replaceambiguouschars=null, $length=32)
Return a generated password using default module.
dol_hash($chain, $type='0')
Returns a hash (non reversible encryption) of a string.