dolibarr  16.0.5
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-2019 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-2021 Frédéric France <frederic.france@netlogic.fr>
16  * Copyright (C) 2018 Josep Lluís Amador <joseplluis@lliuretic.cat>
17  * Copyright (C) 2021 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
18  * Copyright (C) 2021 Grégory Blémand <gregory.blemand@atm-consulting.fr>
19  *
20  * This program is free software; you can redistribute it and/or modify
21  * it under the terms of the GNU General Public License as published by
22  * the Free Software Foundation; either version 3 of the License, or
23  * (at your option) any later version.
24  *
25  * This program is distributed in the hope that it will be useful,
26  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28  * GNU General Public License for more details.
29  *
30  * You should have received a copy of the GNU General Public License
31  * along with this program. If not, see <https://www.gnu.org/licenses/>.
32  */
33 
44 abstract class CommonObject
45 {
46  const TRIGGER_PREFIX = ''; // to be overriden in child class implementations, i.e. 'BILL', 'TASK', 'PROPAL', etc.
50  public $db;
51 
55  public $id;
56 
60  public $entity;
61 
66  public $error;
67 
71  public $errorhidden;
72 
76  public $errors = array();
77 
81  public $validateFieldsErrors = array();
82 
86  public $element;
87 
91  public $element_for_permission;
92 
96  public $table_element;
97 
101  public $table_element_line = '';
102 
106  public $import_key;
107 
111  public $array_options = array();
112 
116  public $array_languages = null; // Value is array() when load already tried
117 
121  public $contacts_ids;
122 
126  public $linked_objects;
127 
131  public $linkedObjectsIds;
132 
136  public $linkedObjects;
137 
141  public $linkedObjectsFullLoaded = array();
142 
146  public $oldcopy;
147 
151  protected $table_ref_field = '';
152 
156  public $restrictiononfksoc = 0;
157 
158 
159  // Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them.
160 
164  public $context = array();
165 
169  public $canvas;
170 
175  public $project;
176 
181  public $fk_project;
182 
187  public $projet;
188 
193  public $fk_projet;
194 
199  public $contact;
200 
205  public $contact_id;
206 
211  public $thirdparty;
212 
217  public $user;
218 
223  public $origin;
224 
229  public $origin_id;
230 
234  public $ref;
235 
239  public $ref_ext;
240 
244  public $ref_previous;
245 
249  public $ref_next;
250 
254  public $newref;
255 
260  public $statut;
261 
266  public $status;
267 
268 
273  public $country;
274 
279  public $country_id;
280 
285  public $country_code;
286 
291  public $state;
292 
297  public $state_id;
298 
303  public $state_code;
304 
309  public $region_id;
310 
315  public $region_code;
316 
321  public $region;
322 
323 
328  public $barcode_type;
329 
334  public $barcode_type_code;
335 
340  public $barcode_type_label;
341 
346  public $barcode_type_coder;
347 
352  public $mode_reglement_id;
353 
358  public $cond_reglement_id;
359 
363  public $demand_reason_id;
364 
369  public $transport_mode_id;
370 
376  public $cond_reglement;
377 
383  public $fk_delivery_address;
384 
389  public $shipping_method_id;
390 
395  public $model_pdf;
396 
402  public $modelpdf;
403 
408  public $last_main_doc;
409 
415  public $fk_bank;
416 
421  public $fk_account;
422 
426  public $openid;
427 
432  public $note_public;
433 
438  public $note_private;
439 
444  public $note;
445 
450  public $total_ht;
451 
456  public $total_tva;
457 
462  public $total_localtax1;
463 
468  public $total_localtax2;
469 
474  public $total_ttc;
475 
479  public $lines;
480 
485  public $comments = array();
486 
490  public $name;
491 
495  public $lastname;
496 
500  public $firstname;
501 
505  public $civility_id;
506 
507  // Dates
511  public $date_creation;
512 
516  public $date_validation; // Date validation
517 
521  public $date_modification; // Date last change (tms field)
522 
526  public $date_cloture; // Date closing (tms field)
527 
532  public $user_author;
537  public $user_creation;
541  public $user_creation_id;
542 
547  public $user_valid;
552  public $user_validation;
556  public $user_validation_id;
560  public $user_closing_id;
561 
566  public $user_modification;
570  public $user_modification_id;
571 
572 
573  public $next_prev_filter;
574 
578  public $specimen = 0;
579 
583  public $sendtoid;
584 
588  public $alreadypaid;
589 
590 
594  protected $childtables = array();
595 
601  protected $childtablesoncascade = array();
602 
603 
604  // No constructor as it is an abstract class
605 
606 
617  public static function isExistingObject($element, $id, $ref = '', $ref_ext = '')
618  {
619  global $db, $conf;
620 
621  $sql = "SELECT rowid, ref, ref_ext";
622  $sql .= " FROM ".$db->prefix().$element;
623  $sql .= " WHERE entity IN (".getEntity($element).")";
624 
625  if ($id > 0) {
626  $sql .= " AND rowid = ".((int) $id);
627  } elseif ($ref) {
628  $sql .= " AND ref = '".$db->escape($ref)."'";
629  } elseif ($ref_ext) {
630  $sql .= " AND ref_ext = '".$db->escape($ref_ext)."'";
631  } else {
632  $error = 'ErrorWrongParameters';
633  dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
634  return -1;
635  }
636  if ($ref || $ref_ext) { // Because the same ref can exists in 2 different entities, we force the current one in priority
637  $sql .= " AND entity = ".((int) $conf->entity);
638  }
639 
640  dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
641  $resql = $db->query($sql);
642  if ($resql) {
643  $num = $db->num_rows($resql);
644  if ($num > 0) {
645  return 1;
646  } else {
647  return 0;
648  }
649  }
650  return -1;
651  }
652 
653 
659  public function errorsToString()
660  {
661  return $this->error.(is_array($this->errors) ? (($this->error != '' ? ', ' : '').join(', ', $this->errors)) : '');
662  }
663 
664 
671  public function getFormatedCustomerRef($objref)
672  {
673  global $hookmanager;
674 
675  $parameters = array('objref'=>$objref);
676  $action = '';
677  $reshook = $hookmanager->executeHooks('getFormatedCustomerRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
678  if ($reshook > 0) {
679  return $hookmanager->resArray['objref'];
680  }
681  return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
682  }
683 
690  public function getFormatedSupplierRef($objref)
691  {
692  global $hookmanager;
693 
694  $parameters = array('objref'=>$objref);
695  $action = '';
696  $reshook = $hookmanager->executeHooks('getFormatedSupplierRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
697  if ($reshook > 0) {
698  return $hookmanager->resArray['objref'];
699  }
700  return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
701  }
702 
712  public function getFullName($langs, $option = 0, $nameorder = -1, $maxlen = 0)
713  {
714  //print "lastname=".$this->lastname." name=".$this->name." nom=".$this->nom."<br>\n";
715  $lastname = $this->lastname;
716  $firstname = $this->firstname;
717  if (empty($lastname)) {
718  $lastname = (isset($this->lastname) ? $this->lastname : (isset($this->name) ? $this->name : (isset($this->nom) ? $this->nom : (isset($this->societe) ? $this->societe : (isset($this->company) ? $this->company : '')))));
719  }
720 
721  $ret = '';
722  if (!empty($option) && !empty($this->civility_code)) {
723  if ($langs->transnoentitiesnoconv("Civility".$this->civility_code) != "Civility".$this->civility_code) {
724  $ret .= $langs->transnoentitiesnoconv("Civility".$this->civility_code).' ';
725  } else {
726  $ret .= $this->civility_code.' ';
727  }
728  }
729 
730  $ret .= dolGetFirstLastname($firstname, $lastname, $nameorder);
731 
732  return dol_trunc($ret, $maxlen);
733  }
734 
740  public function setUpperOrLowerCase()
741  {
742  global $conf;
743  if (!empty($conf->global->MAIN_FIRST_TO_UPPER)) {
744  $this->lastname = dol_ucwords(dol_strtolower($this->lastname));
745  $this->firstname = dol_ucwords(dol_strtolower($this->firstname));
746  $this->name = dol_ucwords(dol_strtolower($this->name));
747  $this->name_alias = dol_ucwords(dol_strtolower($this->name_alias));
748  }
749  if (!empty($conf->global->MAIN_ALL_TO_UPPER)) {
750  $this->lastname = dol_strtoupper($this->lastname);
751  $this->name = dol_strtoupper($this->name);
752  $this->name_alias = dol_strtoupper($this->name_alias);
753  }
754  if (!empty($conf->global->MAIN_ALL_TOWN_TO_UPPER)) {
755  $this->address = dol_strtoupper($this->address);
756  $this->town = dol_strtoupper($this->town);
757  }
758  }
759 
766  public function getKanbanView($option = '')
767  {
768  $return = '<div class="box-flex-item box-flex-grow-zero">';
769  $return .= '<div class="info-box info-box-sm">';
770  $return .= '<span class="info-box-icon bg-infobox-action">';
771  $return .= img_picto('', $this->picto);
772  //$return .= '<i class="fa fa-dol-action"></i>'; // Can be image
773  $return .= '</span>';
774  $return .= '<div class="info-box-content">';
775  $return .= '<span class="info-box-ref">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
776  if (property_exists($this, 'label')) {
777  $return .= '<br><span class="info-box-label opacitymedium">'.$this->label.'</span>';
778  }
779  if (method_exists($this, 'getLibStatut')) {
780  $return .= '<br><div class="info-box-status margintoponly">'.$this->getLibStatut(5).'</div>';
781  }
782  $return .= '</div>';
783  $return .= '</div>';
784  $return .= '</div>';
785 
786  return $return;
787  }
788 
798  public function getFullAddress($withcountry = 0, $sep = "\n", $withregion = 0, $extralangcode = '')
799  {
800  if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country))) {
801  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
802  $tmparray = getCountry($this->country_id, 'all');
803  $this->country_code = $tmparray['code'];
804  $this->country = $tmparray['label'];
805  }
806 
807  if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_code))) {
808  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
809  $tmparray = getState($this->state_id, 'all', 0, 1);
810  $this->state_code = $tmparray['code'];
811  $this->state = $tmparray['label'];
812  $this->region_code = $tmparray['region_code'];
813  $this->region = $tmparray['region'];
814  }
815 
816  return dol_format_address($this, $withcountry, $sep, '', 0, $extralangcode);
817  }
818 
819 
827  public function getBannerAddress($htmlkey, $object)
828  {
829  global $conf, $langs, $form, $extralanguages;
830 
831  $countriesusingstate = array('AU', 'US', 'IN', 'GB', 'ES', 'UK', 'TR'); // See also option MAIN_FORCE_STATE_INTO_ADDRESS
832 
833  $contactid = 0;
834  $thirdpartyid = 0;
835  $elementforaltlanguage = $this->element;
836  if ($this->element == 'societe') {
837  $thirdpartyid = $this->id;
838  }
839  if ($this->element == 'contact') {
840  $contactid = $this->id;
841  $thirdpartyid = empty($object->fk_soc) ? 0 : $object->fk_soc;
842  }
843  if ($this->element == 'user') {
844  $contactid = $this->contact_id;
845  $thirdpartyid = empty($object->fk_soc) ? 0 : $object->fk_soc;
846  }
847 
848  $out = '';
849 
850  $outdone = 0;
851  $coords = $this->getFullAddress(1, ', ', (!empty($conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT) ? $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT : 0));
852  if ($coords) {
853  if (!empty($conf->use_javascript_ajax)) {
854  // Add picto with tooltip on map
855  $namecoords = '';
856  if ($this->element == 'contact' && !empty($conf->global->MAIN_SHOW_COMPANY_NAME_IN_BANNER_ADDRESS)) {
857  $namecoords .= $object->name.'<br>';
858  }
859  $namecoords .= $this->getFullName($langs, 1).'<br>'.$coords;
860  // hideonsmatphone because copyToClipboard call jquery dialog that does not work with jmobile
861  $out .= '<a href="#" class="hideonsmartphone" onclick="return copyToClipboard(\''.dol_escape_js($namecoords).'\',\''.dol_escape_js($langs->trans("HelpCopyToClipboard")).'\');">';
862  $out .= img_picto($langs->trans("Address"), 'map-marker-alt');
863  $out .= '</a> ';
864  }
865  $out .= dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', ');
866  $outdone++;
867  $outdone++;
868 
869  // List of extra languages
870  $arrayoflangcode = array();
871  if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) {
872  $arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE;
873  }
874 
875  if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
876  if (!is_object($extralanguages)) {
877  include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php';
878  $extralanguages = new ExtraLanguages($this->db);
879  }
880  $extralanguages->fetch_name_extralanguages($elementforaltlanguage);
881 
882  if (!empty($extralanguages->attributes[$elementforaltlanguage]['address']) || !empty($extralanguages->attributes[$elementforaltlanguage]['town'])) {
883  $out .= "<!-- alternatelanguage for '".$elementforaltlanguage."' set to fields '".join(',', $extralanguages->attributes[$elementforaltlanguage])."' -->\n";
884  $this->fetchValuesForExtraLanguages();
885  if (!is_object($form)) {
886  $form = new Form($this->db);
887  }
888  $htmltext = '';
889  // If there is extra languages
890  foreach ($arrayoflangcode as $extralangcode) {
891  $s = picto_from_langcode($extralangcode, 'class="pictoforlang paddingright"');
892  $coords = $this->getFullAddress(1, ', ', $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT, $extralangcode);
893  $htmltext .= $s.dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', ');
894  }
895  $out .= $form->textwithpicto('', $htmltext, -1, 'language', 'opacitymedium paddingleft');
896  }
897  }
898  }
899 
900  if (!in_array($this->country_code, $countriesusingstate) && empty($conf->global->MAIN_FORCE_STATE_INTO_ADDRESS) // If MAIN_FORCE_STATE_INTO_ADDRESS is on, state is already returned previously with getFullAddress
901  && empty($conf->global->SOCIETE_DISABLE_STATE) && $this->state) {
902  if (!empty($conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT) && $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT == 1 && $this->region) {
903  $out .= ($outdone ? ' - ' : '').$this->region.' - '.$this->state;
904  } else {
905  $out .= ($outdone ? ' - ' : '').$this->state;
906  }
907  $outdone++;
908  }
909 
910  if (!empty($this->phone) || !empty($this->phone_pro) || !empty($this->phone_mobile) || !empty($this->phone_perso) || !empty($this->fax) || !empty($this->office_phone) || !empty($this->user_mobile) || !empty($this->office_fax)) {
911  $out .= ($outdone ? '<br>' : '');
912  }
913  if (!empty($this->phone) && empty($this->phone_pro)) { // For objects that store pro phone into ->phone
914  $out .= dol_print_phone($this->phone, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro"));
915  $outdone++;
916  }
917  if (!empty($this->phone_pro)) {
918  $out .= dol_print_phone($this->phone_pro, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro"));
919  $outdone++;
920  }
921  if (!empty($this->phone_mobile)) {
922  $out .= dol_print_phone($this->phone_mobile, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'mobile', $langs->trans("PhoneMobile"));
923  $outdone++;
924  }
925  if (!empty($this->phone_perso)) {
926  $out .= dol_print_phone($this->phone_perso, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePerso"));
927  $outdone++;
928  }
929  if (!empty($this->office_phone)) {
930  $out .= dol_print_phone($this->office_phone, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro"));
931  $outdone++;
932  }
933  if (!empty($this->user_mobile)) {
934  $out .= dol_print_phone($this->user_mobile, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'mobile', $langs->trans("PhoneMobile"));
935  $outdone++;
936  }
937  if (!empty($this->fax)) {
938  $out .= dol_print_phone($this->fax, $this->country_code, $contactid, $thirdpartyid, 'AC_FAX', '&nbsp;', 'fax', $langs->trans("Fax"));
939  $outdone++;
940  }
941  if (!empty($this->office_fax)) {
942  $out .= dol_print_phone($this->office_fax, $this->country_code, $contactid, $thirdpartyid, 'AC_FAX', '&nbsp;', 'fax', $langs->trans("Fax"));
943  $outdone++;
944  }
945 
946  if ($out) {
947  $out .= '<div style="clear: both;"></div>';
948  }
949  $outdone = 0;
950  if (!empty($this->email)) {
951  $out .= dol_print_email($this->email, $this->id, $object->id, 'AC_EMAIL', 0, 0, 1);
952  $outdone++;
953  }
954  if (!empty($this->url)) {
955  //$out.=dol_print_url($this->url,'_goout',0,1);//steve changed to blank
956  $out .= dol_print_url($this->url, '_blank', 0, 1);
957  $outdone++;
958  }
959 
960  if (!empty($conf->socialnetworks->enabled)) {
961  $outsocialnetwork = '';
962 
963  if (!empty($this->socialnetworks) && is_array($this->socialnetworks) && count($this->socialnetworks) > 0) {
964  $socialnetworksdict = getArrayOfSocialNetworks();
965  foreach ($this->socialnetworks as $key => $value) {
966  if ($value) {
967  $outsocialnetwork .= dol_print_socialnetworks($value, $this->id, $object->id, $key, $socialnetworksdict);
968  }
969  $outdone++;
970  }
971  } else { // Old code to remove
972  if (!empty($this->skype)) {
973  $outsocialnetwork .= dol_print_socialnetworks($this->skype, $this->id, $object->id, 'skype');
974  }
975  $outdone++;
976  if (!empty($this->jabberid)) {
977  $outsocialnetwork .= dol_print_socialnetworks($this->jabberid, $this->id, $object->id, 'jabber');
978  }
979  $outdone++;
980  if (!empty($this->twitter)) {
981  $outsocialnetwork .= dol_print_socialnetworks($this->twitter, $this->id, $object->id, 'twitter');
982  }
983  $outdone++;
984  if (!empty($this->facebook)) {
985  $outsocialnetwork .= dol_print_socialnetworks($this->facebook, $this->id, $object->id, 'facebook');
986  }
987  $outdone++;
988  if (!empty($this->linkedin)) {
989  $outsocialnetwork .= dol_print_socialnetworks($this->linkedin, $this->id, $object->id, 'linkedin');
990  }
991  $outdone++;
992  }
993 
994  if ($outsocialnetwork) {
995  $out .= '<div style="clear: both;">'.$outsocialnetwork.'</div>';
996  }
997  }
998 
999  if ($out) {
1000  return '<!-- BEGIN part to show address block -->'."\n".$out.'<!-- END Part to show address block -->'."\n";
1001  } else {
1002  return '';
1003  }
1004  }
1005 
1014  public function getLastMainDocLink($modulepart, $initsharekey = 0, $relativelink = 0)
1015  {
1016  global $user, $dolibarr_main_url_root;
1017 
1018  if (empty($this->last_main_doc)) {
1019  return ''; // No way to known which document name to use
1020  }
1021 
1022  include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
1023  $ecmfile = new EcmFiles($this->db);
1024  $result = $ecmfile->fetch(0, '', $this->last_main_doc);
1025  if ($result < 0) {
1026  $this->error = $ecmfile->error;
1027  $this->errors = $ecmfile->errors;
1028  return -1;
1029  }
1030 
1031  if (empty($ecmfile->id)) {
1032  // Add entry into index
1033  if ($initsharekey) {
1034  require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
1035 
1036  // 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
1037  /*
1038  $ecmfile->filepath = $rel_dir;
1039  $ecmfile->filename = $filename;
1040  $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
1041  $ecmfile->fullpath_orig = '';
1042  $ecmfile->gen_or_uploaded = 'generated';
1043  $ecmfile->description = ''; // indexed content
1044  $ecmfile->keywords = ''; // keyword content
1045  $ecmfile->share = getRandomPassword(true);
1046  $result = $ecmfile->create($user);
1047  if ($result < 0)
1048  {
1049  $this->error = $ecmfile->error;
1050  $this->errors = $ecmfile->errors;
1051  }
1052  */
1053  } else {
1054  return '';
1055  }
1056  } elseif (empty($ecmfile->share)) {
1057  // Add entry into index
1058  if ($initsharekey) {
1059  require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
1060  $ecmfile->share = getRandomPassword(true);
1061  $ecmfile->update($user);
1062  } else {
1063  return '';
1064  }
1065  }
1066  // Define $urlwithroot
1067  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
1068  // This is to use external domain name found into config file
1069  //if (DOL_URL_ROOT && ! preg_match('/\/$/', $urlwithouturlroot) && ! preg_match('/^\//', DOL_URL_ROOT)) $urlwithroot=$urlwithouturlroot.'/'.DOL_URL_ROOT;
1070  //else
1071  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT;
1072  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
1073 
1074  $forcedownload = 0;
1075 
1076  $paramlink = '';
1077  //if (! empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart; // For sharing with hash (so public files), modulepart is not required.
1078  //if (! empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; // For sharing with hash (so public files), entity is not required.
1079  //$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath); // No need of name of file for public link, we will use the hash
1080  if (!empty($ecmfile->share)) {
1081  $paramlink .= ($paramlink ? '&' : '').'hashp='.$ecmfile->share; // Hash for public share
1082  }
1083  if ($forcedownload) {
1084  $paramlink .= ($paramlink ? '&' : '').'attachment=1';
1085  }
1086 
1087  if ($relativelink) {
1088  $linktoreturn = 'document.php'.($paramlink ? '?'.$paramlink : '');
1089  } else {
1090  $linktoreturn = $urlwithroot.'/document.php'.($paramlink ? '?'.$paramlink : '');
1091  }
1092 
1093  // Here $ecmfile->share is defined
1094  return $linktoreturn;
1095  }
1096 
1097 
1098  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1108  public function add_contact($fk_socpeople, $type_contact, $source = 'external', $notrigger = 0)
1109  {
1110  // phpcs:enable
1111  global $user, $langs;
1112 
1113 
1114  dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
1115 
1116  // Check parameters
1117  if ($fk_socpeople <= 0) {
1118  $langs->load("errors");
1119  $this->error = $langs->trans("ErrorWrongValueForParameterX", "1");
1120  dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
1121  return -1;
1122  }
1123  if (!$type_contact) {
1124  $langs->load("errors");
1125  $this->error = $langs->trans("ErrorWrongValueForParameterX", "2");
1126  dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
1127  return -2;
1128  }
1129 
1130  $id_type_contact = 0;
1131  if (is_numeric($type_contact)) {
1132  $id_type_contact = $type_contact;
1133  } else {
1134  // We look for id type_contact
1135  $sql = "SELECT tc.rowid";
1136  $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1137  $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1138  $sql .= " AND tc.source='".$this->db->escape($source)."'";
1139  $sql .= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
1140  //print $sql;
1141  $resql = $this->db->query($sql);
1142  if ($resql) {
1143  $obj = $this->db->fetch_object($resql);
1144  if ($obj) {
1145  $id_type_contact = $obj->rowid;
1146  }
1147  }
1148  }
1149 
1150  if ($id_type_contact == 0) {
1151  $this->error = 'CODE_NOT_VALID_FOR_THIS_ELEMENT';
1152  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");
1153  return -3;
1154  }
1155 
1156  $datecreate = dol_now();
1157 
1158  // Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
1159  $TListeContacts = $this->liste_contact(-1, $source);
1160  $already_added = false;
1161  if (is_array($TListeContacts) && !empty($TListeContacts)) {
1162  foreach ($TListeContacts as $array_contact) {
1163  if ($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
1164  $already_added = true;
1165  break;
1166  }
1167  }
1168  }
1169 
1170  if (!$already_added) {
1171  $this->db->begin();
1172 
1173  // Insert into database
1174  $sql = "INSERT INTO ".$this->db->prefix()."element_contact";
1175  $sql .= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
1176  $sql .= " VALUES (".$this->id.", ".((int) $fk_socpeople)." , ";
1177  $sql .= "'".$this->db->idate($datecreate)."'";
1178  $sql .= ", 4, ".((int) $id_type_contact);
1179  $sql .= ")";
1180 
1181  $resql = $this->db->query($sql);
1182  if ($resql) {
1183  if (!$notrigger) {
1184  $result = $this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
1185  if ($result < 0) {
1186  $this->db->rollback();
1187  return -1;
1188  }
1189  }
1190 
1191  $this->db->commit();
1192  return 1;
1193  } else {
1194  if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1195  $this->error = $this->db->errno();
1196  $this->db->rollback();
1197  return -2;
1198  } else {
1199  $this->error = $this->db->lasterror();
1200  $this->db->rollback();
1201  return -1;
1202  }
1203  }
1204  } else {
1205  return 0;
1206  }
1207  }
1208 
1209  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1217  public function copy_linked_contact($objFrom, $source = 'internal')
1218  {
1219  // phpcs:enable
1220  $contacts = $objFrom->liste_contact(-1, $source);
1221  foreach ($contacts as $contact) {
1222  if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0) {
1223  return -1;
1224  }
1225  }
1226  return 1;
1227  }
1228 
1229  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1239  public function update_contact($rowid, $statut, $type_contact_id = 0, $fk_socpeople = 0)
1240  {
1241  // phpcs:enable
1242  // Insert into database
1243  $sql = "UPDATE ".$this->db->prefix()."element_contact set";
1244  $sql .= " statut = ".$statut;
1245  if ($type_contact_id) {
1246  $sql .= ", fk_c_type_contact = ".((int) $type_contact_id);
1247  }
1248  if ($fk_socpeople) {
1249  $sql .= ", fk_socpeople = ".((int) $fk_socpeople);
1250  }
1251  $sql .= " where rowid = ".((int) $rowid);
1252  $resql = $this->db->query($sql);
1253  if ($resql) {
1254  return 0;
1255  } else {
1256  $this->error = $this->db->lasterror();
1257  return -1;
1258  }
1259  }
1260 
1261  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1269  public function delete_contact($rowid, $notrigger = 0)
1270  {
1271  // phpcs:enable
1272  global $user;
1273 
1274 
1275  $this->db->begin();
1276 
1277  $sql = "DELETE FROM ".$this->db->prefix()."element_contact";
1278  $sql .= " WHERE rowid = ".((int) $rowid);
1279 
1280  dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
1281  if ($this->db->query($sql)) {
1282  if (!$notrigger) {
1283  $result = $this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
1284  if ($result < 0) {
1285  $this->db->rollback();
1286  return -1;
1287  }
1288  }
1289 
1290  $this->db->commit();
1291  return 1;
1292  } else {
1293  $this->error = $this->db->lasterror();
1294  $this->db->rollback();
1295  return -1;
1296  }
1297  }
1298 
1299  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1307  public function delete_linked_contact($source = '', $code = '')
1308  {
1309  // phpcs:enable
1310  $listId = '';
1311  $temp = array();
1312  $typeContact = $this->liste_type_contact($source, '', 0, 0, $code);
1313 
1314  if (!empty($typeContact)) {
1315  foreach ($typeContact as $key => $value) {
1316  array_push($temp, $key);
1317  }
1318  $listId = implode(",", $temp);
1319  }
1320 
1321  // If $listId is empty, we have not criteria on fk_c_type_contact so we will delete record on element_id for
1322  // any type or record instead of only the ones of the current object. So we do nothing in such a case.
1323  if (empty($listId)) {
1324  return 0;
1325  }
1326 
1327  $sql = "DELETE FROM ".$this->db->prefix()."element_contact";
1328  $sql .= " WHERE element_id = ".((int) $this->id);
1329  $sql .= " AND fk_c_type_contact IN (".$this->db->sanitize($listId).")";
1330 
1331  dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
1332  if ($this->db->query($sql)) {
1333  return 1;
1334  } else {
1335  $this->error = $this->db->lasterror();
1336  return -1;
1337  }
1338  }
1339 
1340  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1350  public function liste_contact($status = -1, $source = 'external', $list = 0, $code = '')
1351  {
1352  // phpcs:enable
1353  global $langs;
1354 
1355  $tab = array();
1356 
1357  $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
1358  if ($source == 'internal') {
1359  $sql .= ", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
1360  }
1361  if ($source == 'external' || $source == 'thirdparty') {
1362  $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
1363  }
1364  $sql .= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
1365  $sql .= ", tc.source, tc.element, tc.code, tc.libelle";
1366  $sql .= " FROM ".$this->db->prefix()."c_type_contact tc";
1367  $sql .= ", ".$this->db->prefix()."element_contact ec";
1368  if ($source == 'internal') {
1369  $sql .= " LEFT JOIN ".$this->db->prefix()."user t on ec.fk_socpeople = t.rowid";
1370  }
1371  if ($source == 'external' || $source == 'thirdparty') {
1372  $sql .= " LEFT JOIN ".$this->db->prefix()."socpeople t on ec.fk_socpeople = t.rowid";
1373  }
1374  $sql .= " WHERE ec.element_id = ".((int) $this->id);
1375  $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1376  $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1377  if ($code) {
1378  $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1379  }
1380  if ($source == 'internal') {
1381  $sql .= " AND tc.source = 'internal'";
1382  }
1383  if ($source == 'external' || $source == 'thirdparty') {
1384  $sql .= " AND tc.source = 'external'";
1385  }
1386  $sql .= " AND tc.active=1";
1387  if ($status >= 0) {
1388  $sql .= " AND ec.statut = ".((int) $status);
1389  }
1390  $sql .= " ORDER BY t.lastname ASC";
1391 
1392  dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
1393  $resql = $this->db->query($sql);
1394  if ($resql) {
1395  $num = $this->db->num_rows($resql);
1396  $i = 0;
1397  while ($i < $num) {
1398  $obj = $this->db->fetch_object($resql);
1399 
1400  if (!$list) {
1401  $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
1402  $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1403  $tab[$i] = array(
1404  'source' => $obj->source,
1405  'socid' => $obj->socid,
1406  'id' => $obj->id,
1407  'nom' => $obj->lastname, // For backward compatibility
1408  'civility' => $obj->civility,
1409  'lastname' => $obj->lastname,
1410  'firstname' => $obj->firstname,
1411  'email'=>$obj->email,
1412  'login'=> (empty($obj->login) ? '' : $obj->login),
1413  'photo' => (empty($obj->photo) ? '' : $obj->photo),
1414  'statuscontact' => $obj->statuscontact,
1415  'rowid' => $obj->rowid,
1416  'code' => $obj->code,
1417  'libelle' => $libelle_type,
1418  'status' => $obj->statuslink,
1419  'fk_c_type_contact' => $obj->fk_c_type_contact
1420  );
1421  } else {
1422  $tab[$i] = $obj->id;
1423  }
1424 
1425  $i++;
1426  }
1427 
1428  return $tab;
1429  } else {
1430  $this->error = $this->db->lasterror();
1431  dol_print_error($this->db);
1432  return -1;
1433  }
1434  }
1435 
1436 
1443  public function swapContactStatus($rowid)
1444  {
1445  $sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1446  $sql .= " tc.code, tc.libelle";
1447  $sql .= " FROM (".$this->db->prefix()."element_contact as ec, ".$this->db->prefix()."c_type_contact as tc)";
1448  $sql .= " WHERE ec.rowid =".((int) $rowid);
1449  $sql .= " AND ec.fk_c_type_contact=tc.rowid";
1450  $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1451 
1452  dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1453  $resql = $this->db->query($sql);
1454  if ($resql) {
1455  $obj = $this->db->fetch_object($resql);
1456  $newstatut = ($obj->statut == 4) ? 5 : 4;
1457  $result = $this->update_contact($rowid, $newstatut);
1458  $this->db->free($resql);
1459  return $result;
1460  } else {
1461  $this->error = $this->db->error();
1462  dol_print_error($this->db);
1463  return -1;
1464  }
1465  }
1466 
1467  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1478  public function liste_type_contact($source = 'internal', $order = 'position', $option = 0, $activeonly = 0, $code = '')
1479  {
1480  // phpcs:enable
1481  global $langs;
1482 
1483  if (empty($order)) {
1484  $order = 'position';
1485  }
1486  if ($order == 'position') {
1487  $order .= ',code';
1488  }
1489 
1490  $tab = array();
1491  $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position";
1492  $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1493  $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1494  if ($activeonly == 1) {
1495  $sql .= " AND tc.active=1"; // only the active types
1496  }
1497  if (!empty($source) && $source != 'all') {
1498  $sql .= " AND tc.source='".$this->db->escape($source)."'";
1499  }
1500  if (!empty($code)) {
1501  $sql .= " AND tc.code='".$this->db->escape($code)."'";
1502  }
1503  $sql .= $this->db->order($order, 'ASC');
1504 
1505  //print "sql=".$sql;
1506  $resql = $this->db->query($sql);
1507  if ($resql) {
1508  $num = $this->db->num_rows($resql);
1509  $i = 0;
1510  while ($i < $num) {
1511  $obj = $this->db->fetch_object($resql);
1512 
1513  $transkey = "TypeContact_".$this->element."_".$source."_".$obj->code;
1514  $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1515  if (empty($option)) {
1516  $tab[$obj->rowid] = $libelle_type;
1517  } else {
1518  $tab[$obj->code] = $libelle_type;
1519  }
1520  $i++;
1521  }
1522  return $tab;
1523  } else {
1524  $this->error = $this->db->lasterror();
1525  //dol_print_error($this->db);
1526  return null;
1527  }
1528  }
1529 
1530  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1542  public function listeTypeContacts($source = 'internal', $option = 0, $activeonly = 0, $code = '', $element = '', $excludeelement = '')
1543  {
1544  // phpcs:enable
1545  global $langs, $conf;
1546 
1547  $langs->loadLangs(array('bills', 'contracts', 'interventions', 'orders', 'projects', 'propal', 'ticket', 'agenda'));
1548 
1549  $tab = array();
1550 
1551  $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position, tc.element";
1552  $sql .= " FROM ".$this->db->prefix()."c_type_contact as tc";
1553 
1554  $sqlWhere = array();
1555  if (!empty($element)) {
1556  $sqlWhere[] = " tc.element='".$this->db->escape($element)."'";
1557  }
1558  if (!empty($excludeelement)) {
1559  $sqlWhere[] = " tc.element <> '".$this->db->escape($excludeelement)."'";
1560  }
1561 
1562  if ($activeonly == 1) {
1563  $sqlWhere[] = " tc.active=1"; // only the active types
1564  }
1565 
1566  if (!empty($source) && $source != 'all') {
1567  $sqlWhere[] = " tc.source='".$this->db->escape($source)."'";
1568  }
1569 
1570  if (!empty($code)) {
1571  $sqlWhere[] = " tc.code='".$this->db->escape($code)."'";
1572  }
1573 
1574  if (count($sqlWhere) > 0) {
1575  $sql .= " WHERE ".implode(' AND ', $sqlWhere);
1576  }
1577 
1578  $sql .= $this->db->order('tc.element, tc.position', 'ASC');
1579 
1580  dol_syslog(__METHOD__, LOG_DEBUG);
1581  $resql = $this->db->query($sql);
1582  if ($resql) {
1583  $num = $this->db->num_rows($resql);
1584  if ($num > 0) {
1585  $langs->loadLangs(array("propal", "orders", "bills", "suppliers", "contracts", "supplier_proposal"));
1586 
1587  while ($obj = $this->db->fetch_object($resql)) {
1588  $modulename = $obj->element;
1589  if (strpos($obj->element, 'project') !== false) {
1590  $modulename = 'projet';
1591  } elseif ($obj->element == 'contrat') {
1592  $element = 'contract';
1593  } elseif ($obj->element == 'action') {
1594  $modulename = 'agenda';
1595  } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1596  $modulename = 'fournisseur';
1597  } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1598  $modulename = 'fournisseur';
1599  }
1600  if (!empty($conf->{$modulename}->enabled)) {
1601  $libelle_element = $langs->trans('ContactDefault_'.$obj->element);
1602  $tmpelement = $obj->element;
1603  $transkey = "TypeContact_".$tmpelement."_".$source."_".$obj->code;
1604  $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1605  if (empty($option)) {
1606  $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1607  } else {
1608  $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1609  }
1610  }
1611  }
1612  }
1613  return $tab;
1614  } else {
1615  $this->error = $this->db->lasterror();
1616  return null;
1617  }
1618  }
1619 
1631  public function getIdContact($source, $code, $status = 0)
1632  {
1633  global $conf;
1634 
1635  $result = array();
1636  $i = 0;
1637  //cas particulier pour les expeditions
1638  if ($this->element == 'shipping' && $this->origin_id != 0) {
1639  $id = $this->origin_id;
1640  $element = 'commande';
1641  } elseif ($this->element == 'reception' && $this->origin_id != 0) {
1642  $id = $this->origin_id;
1643  $element = 'order_supplier';
1644  } else {
1645  $id = $this->id;
1646  $element = $this->element;
1647  }
1648 
1649  $sql = "SELECT ec.fk_socpeople";
1650  $sql .= " FROM ".$this->db->prefix()."element_contact as ec,";
1651  if ($source == 'internal') {
1652  $sql .= " ".$this->db->prefix()."user as c,";
1653  }
1654  if ($source == 'external') {
1655  $sql .= " ".$this->db->prefix()."socpeople as c,";
1656  }
1657  $sql .= " ".$this->db->prefix()."c_type_contact as tc";
1658  $sql .= " WHERE ec.element_id = ".((int) $id);
1659  $sql .= " AND ec.fk_socpeople = c.rowid";
1660  if ($source == 'internal') {
1661  $sql .= " AND c.entity IN (".getEntity('user').")";
1662  }
1663  if ($source == 'external') {
1664  $sql .= " AND c.entity IN (".getEntity('societe').")";
1665  }
1666  $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1667  $sql .= " AND tc.element = '".$this->db->escape($element)."'";
1668  $sql .= " AND tc.source = '".$this->db->escape($source)."'";
1669  if ($code) {
1670  $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1671  }
1672  $sql .= " AND tc.active = 1";
1673  if ($status) {
1674  $sql .= " AND ec.statut = ".((int) $status);
1675  }
1676 
1677  dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1678  $resql = $this->db->query($sql);
1679  if ($resql) {
1680  while ($obj = $this->db->fetch_object($resql)) {
1681  $result[$i] = $obj->fk_socpeople;
1682  $i++;
1683  }
1684  } else {
1685  $this->error = $this->db->error();
1686  return null;
1687  }
1688 
1689  return $result;
1690  }
1691 
1692  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1699  public function fetch_contact($contactid = null)
1700  {
1701  // phpcs:enable
1702  if (empty($contactid)) {
1703  $contactid = $this->contact_id;
1704  }
1705 
1706  if (empty($contactid)) {
1707  return 0;
1708  }
1709 
1710  require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1711  $contact = new Contact($this->db);
1712  $result = $contact->fetch($contactid);
1713  $this->contact = $contact;
1714  return $result;
1715  }
1716 
1717  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1724  public function fetch_thirdparty($force_thirdparty_id = 0)
1725  {
1726  // phpcs:enable
1727  global $conf;
1728 
1729  if (empty($this->socid) && empty($this->fk_soc) && empty($force_thirdparty_id)) {
1730  return 0;
1731  }
1732 
1733  require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1734 
1735  $idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : 0);
1736  if ($force_thirdparty_id) {
1737  $idtofetch = $force_thirdparty_id;
1738  }
1739 
1740  if ($idtofetch) {
1741  $thirdparty = new Societe($this->db);
1742  $result = $thirdparty->fetch($idtofetch);
1743  if ($result<0) {
1744  $this->errors=array_merge($this->errors, $thirdparty->errors);
1745  }
1746  $this->thirdparty = $thirdparty;
1747 
1748  // Use first price level if level not defined for third party
1749  if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
1750  $this->thirdparty->price_level = 1;
1751  }
1752 
1753  return $result;
1754  } else {
1755  return -1;
1756  }
1757  }
1758 
1759 
1767  public function fetchOneLike($ref)
1768  {
1769  if (!$this->table_ref_field) {
1770  return 0;
1771  }
1772 
1773  $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element." WHERE ".$this->table_ref_field." LIKE '".$this->db->escape($ref)."' LIMIT 1";
1774 
1775  $query = $this->db->query($sql);
1776 
1777  if (!$this->db->num_rows($query)) {
1778  return 0;
1779  }
1780 
1781  $result = $this->db->fetch_object($query);
1782 
1783  return $this->fetch($result->rowid);
1784  }
1785 
1786  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1794  public function fetch_barcode()
1795  {
1796  // phpcs:enable
1797  global $conf;
1798 
1799  dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1800 
1801  $idtype = $this->barcode_type;
1802  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
1803  if ($this->element == 'product' && !empty($conf->global->PRODUIT_DEFAULT_BARCODE_TYPE)) {
1804  $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1805  } elseif ($this->element == 'societe') {
1806  $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1807  } else {
1808  dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1809  }
1810  }
1811 
1812  if ($idtype > 0) {
1813  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
1814  $sql = "SELECT rowid, code, libelle as label, coder";
1815  $sql .= " FROM ".$this->db->prefix()."c_barcode_type";
1816  $sql .= " WHERE rowid = ".((int) $idtype);
1817  dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1818  $resql = $this->db->query($sql);
1819  if ($resql) {
1820  $obj = $this->db->fetch_object($resql);
1821  $this->barcode_type = $obj->rowid;
1822  $this->barcode_type_code = $obj->code;
1823  $this->barcode_type_label = $obj->label;
1824  $this->barcode_type_coder = $obj->coder;
1825  return 1;
1826  } else {
1827  dol_print_error($this->db);
1828  return -1;
1829  }
1830  }
1831  }
1832  return 0;
1833  }
1834 
1835  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1841  public function fetch_project()
1842  {
1843  // phpcs:enable
1844  return $this->fetch_projet();
1845  }
1846 
1847  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1853  public function fetch_projet()
1854  {
1855  // phpcs:enable
1856  include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1857 
1858  if (empty($this->fk_project) && !empty($this->fk_projet)) {
1859  $this->fk_project = $this->fk_projet; // For backward compatibility
1860  }
1861  if (empty($this->fk_project)) {
1862  return 0;
1863  }
1864 
1865  $project = new Project($this->db);
1866  $result = $project->fetch($this->fk_project);
1867 
1868  $this->projet = $project; // deprecated
1869  $this->project = $project;
1870  return $result;
1871  }
1872 
1873  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1879  public function fetch_product()
1880  {
1881  // phpcs:enable
1882  include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1883 
1884  if (empty($this->fk_product)) {
1885  return 0;
1886  }
1887 
1888  $product = new Product($this->db);
1889  $result = $product->fetch($this->fk_product);
1890 
1891  $this->product = $product;
1892  return $result;
1893  }
1894 
1895  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1902  public function fetch_user($userid)
1903  {
1904  // phpcs:enable
1905  $user = new User($this->db);
1906  $result = $user->fetch($userid);
1907  $this->user = $user;
1908  return $result;
1909  }
1910 
1911  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1917  public function fetch_origin()
1918  {
1919  // phpcs:enable
1920  if ($this->origin == 'shipping') {
1921  $this->origin = 'expedition';
1922  }
1923  if ($this->origin == 'delivery') {
1924  $this->origin = 'livraison';
1925  }
1926  if ($this->origin == 'order_supplier') {
1927  $this->origin = 'commandeFournisseur';
1928  }
1929 
1930  $origin = $this->origin;
1931 
1932  $classname = ucfirst($origin);
1933  $this->$origin = new $classname($this->db);
1934  $this->$origin->fetch($this->origin_id);
1935  }
1936 
1946  public function fetchObjectFrom($table, $field, $key, $element = null)
1947  {
1948  global $conf;
1949 
1950  $result = false;
1951 
1952  $sql = "SELECT rowid FROM ".$this->db->prefix().$table;
1953  $sql .= " WHERE ".$field." = '".$this->db->escape($key)."'";
1954  if (!empty($element)) {
1955  $sql .= " AND entity IN (".getEntity($element).")";
1956  } else {
1957  $sql .= " AND entity = ".((int) $conf->entity);
1958  }
1959 
1960  dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
1961  $resql = $this->db->query($sql);
1962  if ($resql) {
1963  $row = $this->db->fetch_row($resql);
1964  // Test for avoid error -1
1965  if ($row[0] > 0) {
1966  $result = $this->fetch($row[0]);
1967  }
1968  }
1969 
1970  return $result;
1971  }
1972 
1981  public function getValueFrom($table, $id, $field)
1982  {
1983  $result = false;
1984  if (!empty($id) && !empty($field) && !empty($table)) {
1985  $sql = "SELECT ".$field." FROM ".$this->db->prefix().$table;
1986  $sql .= " WHERE rowid = ".((int) $id);
1987 
1988  dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
1989  $resql = $this->db->query($sql);
1990  if ($resql) {
1991  $row = $this->db->fetch_row($resql);
1992  $result = $row[0];
1993  }
1994  }
1995  return $result;
1996  }
1997 
2014  public function setValueFrom($field, $value, $table = '', $id = null, $format = '', $id_field = '', $fuser = null, $trigkey = '', $fk_user_field = 'fk_user_modif')
2015  {
2016  global $user, $langs, $conf;
2017 
2018  if (empty($table)) {
2019  $table = $this->table_element;
2020  }
2021  if (empty($id)) {
2022  $id = $this->id;
2023  }
2024  if (empty($format)) {
2025  $format = 'text';
2026  }
2027  if (empty($id_field)) {
2028  $id_field = 'rowid';
2029  }
2030 
2031  $error = 0;
2032 
2033  $this->db->begin();
2034 
2035  // Special case
2036  if ($table == 'product' && $field == 'note_private') {
2037  $field = 'note';
2038  }
2039  if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
2040  $fk_user_field = 'fk_user_mod';
2041  }
2042 
2043  $sql = "UPDATE ".$this->db->prefix().$table." SET ";
2044 
2045  if ($format == 'text') {
2046  $sql .= $field." = '".$this->db->escape($value)."'";
2047  } elseif ($format == 'int') {
2048  $sql .= $field." = ".((int) $value);
2049  } elseif ($format == 'date') {
2050  $sql .= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
2051  }
2052 
2053  if ($fk_user_field) {
2054  if (!empty($fuser) && is_object($fuser)) {
2055  $sql .= ", ".$fk_user_field." = ".((int) $fuser->id);
2056  } elseif (empty($fuser) || $fuser != 'none') {
2057  $sql .= ", ".$fk_user_field." = ".((int) $user->id);
2058  }
2059  }
2060 
2061  $sql .= " WHERE ".$id_field." = ".((int) $id);
2062 
2063  dol_syslog(__METHOD__."", LOG_DEBUG);
2064  $resql = $this->db->query($sql);
2065  if ($resql) {
2066  if ($trigkey) {
2067  // call trigger with updated object values
2068  if (empty($this->fields) && method_exists($this, 'fetch')) {
2069  $result = $this->fetch($id);
2070  } else {
2071  $result = $this->fetchCommon($id);
2072  }
2073  if ($result >= 0) {
2074  $result = $this->call_trigger($trigkey, (!empty($fuser) && is_object($fuser)) ? $fuser : $user); // This may set this->errors
2075  }
2076  if ($result < 0) {
2077  $error++;
2078  }
2079  }
2080 
2081  if (!$error) {
2082  if (property_exists($this, $field)) {
2083  $this->$field = $value;
2084  }
2085  $this->db->commit();
2086  return 1;
2087  } else {
2088  $this->db->rollback();
2089  return -2;
2090  }
2091  } else {
2092  if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
2093  $this->error = 'DB_ERROR_RECORD_ALREADY_EXISTS';
2094  } else {
2095  $this->error = $this->db->lasterror();
2096  }
2097  $this->db->rollback();
2098  return -1;
2099  }
2100  }
2101 
2102  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2111  public function load_previous_next_ref($filter, $fieldid, $nodbprefix = 0)
2112  {
2113  // phpcs:enable
2114  global $conf, $user;
2115 
2116  if (!$this->table_element) {
2117  dol_print_error('', get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
2118  return -1;
2119  }
2120  if ($fieldid == 'none') {
2121  return 1;
2122  }
2123 
2124  // For backward compatibility
2125  if ($this->table_element == 'facture_rec' && $fieldid == 'title') {
2126  $fieldid = 'titre';
2127  }
2128 
2129  // Security on socid
2130  $socid = 0;
2131  if ($user->socid > 0) {
2132  $socid = $user->socid;
2133  }
2134 
2135  // this->ismultientitymanaged contains
2136  // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
2137  $aliastablesociete = 's';
2138  if ($this->element == 'societe') {
2139  $aliastablesociete = 'te'; // te as table_element
2140  }
2141  $restrictiononfksoc = empty($this->restrictiononfksoc) ? 0 : $this->restrictiononfksoc;
2142  $sql = "SELECT MAX(te.".$fieldid.")";
2143  $sql .= " FROM ".(empty($nodbprefix) ?$this->db->prefix():'').$this->table_element." as te";
2144  if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2145  $sql .= ",".$this->db->prefix()."usergroup_user as ug";
2146  }
2147  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2148  $tmparray = explode('@', $this->ismultientitymanaged);
2149  $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
2150  } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2151  $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
2152  } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2153  $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
2154  }
2155  if ($restrictiononfksoc && empty($user->rights->societe->client->voir) && !$socid) {
2156  $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
2157  }
2158  $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)
2159  if ($restrictiononfksoc == 1 && empty($user->rights->societe->client->voir) && !$socid) {
2160  $sql .= " AND sc.fk_user = ".((int) $user->id);
2161  }
2162  if ($restrictiononfksoc == 2 && empty($user->rights->societe->client->voir) && !$socid) {
2163  $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
2164  }
2165  if (!empty($filter)) {
2166  if (!preg_match('/^\s*AND/i', $filter)) {
2167  $sql .= " AND "; // For backward compatibility
2168  }
2169  $sql .= $filter;
2170  }
2171  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2172  $tmparray = explode('@', $this->ismultientitymanaged);
2173  $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
2174  } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2175  $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
2176  }
2177  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2178  if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2179  if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
2180  $sql .= " AND te.entity IS NOT NULL"; // Show all users
2181  } else {
2182  $sql .= " AND ug.fk_user = te.rowid";
2183  $sql .= " AND ug.entity IN (".getEntity('usergroup').")";
2184  }
2185  } else {
2186  $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
2187  }
2188  }
2189  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
2190  $tmparray = explode('@', $this->ismultientitymanaged);
2191  $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
2192  }
2193  if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
2194  $sql .= ' AND te.fk_soc = '.((int) $socid);
2195  }
2196  if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
2197  $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
2198  }
2199  if ($restrictiononfksoc && $socid && $this->element == 'societe') {
2200  $sql .= ' AND te.rowid = '.((int) $socid);
2201  }
2202  //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
2203 
2204  $result = $this->db->query($sql);
2205  if (!$result) {
2206  $this->error = $this->db->lasterror();
2207  return -1;
2208  }
2209  $row = $this->db->fetch_row($result);
2210  $this->ref_previous = $row[0];
2211 
2212  $sql = "SELECT MIN(te.".$fieldid.")";
2213  $sql .= " FROM ".(empty($nodbprefix) ?$this->db->prefix():'').$this->table_element." as te";
2214  if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2215  $sql .= ",".$this->db->prefix()."usergroup_user as ug";
2216  }
2217  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2218  $tmparray = explode('@', $this->ismultientitymanaged);
2219  $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
2220  } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2221  $sql .= ", ".$this->db->prefix()."societe as s"; // If we need to link to societe to limit select to socid
2222  } elseif ($restrictiononfksoc == 2 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2223  $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
2224  }
2225  if ($restrictiononfksoc && empty($user->rights->societe->client->voir) && !$socid) {
2226  $sql .= " LEFT JOIN ".$this->db->prefix()."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
2227  }
2228  $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)
2229  if ($restrictiononfksoc == 1 && empty($user->rights->societe->client->voir) && !$socid) {
2230  $sql .= " AND sc.fk_user = ".((int) $user->id);
2231  }
2232  if ($restrictiononfksoc == 2 && empty($user->rights->societe->client->voir) && !$socid) {
2233  $sql .= " AND (sc.fk_user = ".((int) $user->id).' OR te.fk_soc IS NULL)';
2234  }
2235  if (!empty($filter)) {
2236  if (!preg_match('/^\s*AND/i', $filter)) {
2237  $sql .= " AND "; // For backward compatibility
2238  }
2239  $sql .= $filter;
2240  }
2241  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
2242  $tmparray = explode('@', $this->ismultientitymanaged);
2243  $sql .= " AND te.".$tmparray[0]." = ".($tmparray[1] == "societe" ? "s" : "parenttable").".rowid"; // If we need to link to this table to limit select to entity
2244  } elseif ($restrictiononfksoc == 1 && $this->element != 'societe' && empty($user->rights->societe->client->voir) && !$socid) {
2245  $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
2246  }
2247  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
2248  if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
2249  if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
2250  $sql .= " AND te.entity IS NOT NULL"; // Show all users
2251  } else {
2252  $sql .= " AND ug.fk_user = te.rowid";
2253  $sql .= " AND ug.entity IN (".getEntity('usergroup').")";
2254  }
2255  } else {
2256  $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
2257  }
2258  }
2259  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
2260  $tmparray = explode('@', $this->ismultientitymanaged);
2261  $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
2262  }
2263  if ($restrictiononfksoc == 1 && $socid && $this->element != 'societe') {
2264  $sql .= ' AND te.fk_soc = '.((int) $socid);
2265  }
2266  if ($restrictiononfksoc == 2 && $socid && $this->element != 'societe') {
2267  $sql .= ' AND (te.fk_soc = '.((int) $socid).' OR te.fk_soc IS NULL)';
2268  }
2269  if ($restrictiononfksoc && $socid && $this->element == 'societe') {
2270  $sql .= ' AND te.rowid = '.((int) $socid);
2271  }
2272  //print 'socid='.$socid.' restrictiononfksoc='.$restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
2273  // 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
2274 
2275  $result = $this->db->query($sql);
2276  if (!$result) {
2277  $this->error = $this->db->lasterror();
2278  return -2;
2279  }
2280  $row = $this->db->fetch_row($result);
2281  $this->ref_next = $row[0];
2282 
2283  return 1;
2284  }
2285 
2286 
2294  public function getListContactId($source = 'external')
2295  {
2296  $contactAlreadySelected = array();
2297  $tab = $this->liste_contact(-1, $source);
2298  $num = count($tab);
2299  $i = 0;
2300  while ($i < $num) {
2301  if ($source == 'thirdparty') {
2302  $contactAlreadySelected[$i] = $tab[$i]['socid'];
2303  } else {
2304  $contactAlreadySelected[$i] = $tab[$i]['id'];
2305  }
2306  $i++;
2307  }
2308  return $contactAlreadySelected;
2309  }
2310 
2311 
2319  public function setProject($projectid, $notrigger = 0)
2320  {
2321  global $user;
2322  $error = 0;
2323 
2324  if (!$this->table_element) {
2325  dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined", LOG_ERR);
2326  return -1;
2327  }
2328 
2329  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2330  if (!empty($this->fields['fk_project'])) { // Common case
2331  if ($projectid) {
2332  $sql .= " SET fk_project = ".((int) $projectid);
2333  } else {
2334  $sql .= " SET fk_project = NULL";
2335  }
2336  $sql .= ' WHERE rowid = '.((int) $this->id);
2337  } elseif ($this->table_element == 'actioncomm') { // Special case for actioncomm
2338  if ($projectid) {
2339  $sql .= " SET fk_project = ".((int) $projectid);
2340  } else {
2341  $sql .= " SET fk_project = NULL";
2342  }
2343  $sql .= ' WHERE id = '.((int) $this->id);
2344  } else // Special case for old architecture objects
2345  {
2346  if ($projectid) {
2347  $sql .= ' SET fk_projet = '.((int) $projectid);
2348  } else {
2349  $sql .= ' SET fk_projet = NULL';
2350  }
2351  $sql .= " WHERE rowid = ".((int) $this->id);
2352  }
2353 
2354  $this->db->begin();
2355 
2356  dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
2357  if ($this->db->query($sql)) {
2358  $this->fk_project = ((int) $projectid);
2359  } else {
2360  dol_print_error($this->db);
2361  $error++;
2362  }
2363 
2364  // Triggers
2365  if (!$error && !$notrigger) {
2366  // Call triggers
2367  $result = $this->call_trigger(strtoupper($this->element) . '_MODIFY', $user);
2368  if ($result < 0) {
2369  $error++;
2370  } //Do also here what you must do to rollback action if trigger fail
2371  // End call triggers
2372  }
2373 
2374  // Commit or rollback
2375  if ($error) {
2376  $this->db->rollback();
2377  return -1;
2378  } else {
2379  $this->db->commit();
2380  return 1;
2381  }
2382  }
2383 
2390  public function setPaymentMethods($id)
2391  {
2392  global $user;
2393 
2394  $error = 0; $notrigger = 0;
2395 
2396  dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
2397 
2398  if ($this->statut >= 0 || $this->element == 'societe') {
2399  // TODO uniformize field name
2400  $fieldname = 'fk_mode_reglement';
2401  if ($this->element == 'societe') {
2402  $fieldname = 'mode_reglement';
2403  }
2404  if (get_class($this) == 'Fournisseur') {
2405  $fieldname = 'mode_reglement_supplier';
2406  }
2407  if (get_class($this) == 'Tva') {
2408  $fieldname = 'fk_typepayment';
2409  }
2410  if (get_class($this) == 'Salary') {
2411  $fieldname = 'fk_typepayment';
2412  }
2413 
2414  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2415  $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2416  $sql .= ' WHERE rowid='.((int) $this->id);
2417 
2418  if ($this->db->query($sql)) {
2419  $this->mode_reglement_id = $id;
2420  // for supplier
2421  if (get_class($this) == 'Fournisseur') {
2422  $this->mode_reglement_supplier_id = $id;
2423  }
2424  // Triggers
2425  if (!$error && !$notrigger) {
2426  // Call triggers
2427  if (get_class($this) == 'Commande') {
2428  $result = $this->call_trigger('ORDER_MODIFY', $user);
2429  } else {
2430  $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
2431  }
2432  if ($result < 0) {
2433  $error++;
2434  }
2435  // End call triggers
2436  }
2437  return 1;
2438  } else {
2439  dol_syslog(get_class($this).'::setPaymentMethods Error '.$this->db->error());
2440  $this->error = $this->db->error();
2441  return -1;
2442  }
2443  } else {
2444  dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
2445  $this->error = 'Status of the object is incompatible '.$this->statut;
2446  return -2;
2447  }
2448  }
2449 
2456  public function setMulticurrencyCode($code)
2457  {
2458  dol_syslog(get_class($this).'::setMulticurrencyCode('.$code.')');
2459  if ($this->statut >= 0 || $this->element == 'societe') {
2460  $fieldname = 'multicurrency_code';
2461 
2462  $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2463  $sql .= " SET ".$fieldname." = '".$this->db->escape($code)."'";
2464  $sql .= ' WHERE rowid='.((int) $this->id);
2465 
2466  if ($this->db->query($sql)) {
2467  $this->multicurrency_code = $code;
2468 
2469  list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
2470  if ($rate) {
2471  $this->setMulticurrencyRate($rate, 2);
2472  }
2473 
2474  return 1;
2475  } else {
2476  dol_syslog(get_class($this).'::setMulticurrencyCode Error '.$sql.' - '.$this->db->error());
2477  $this->error = $this->db->error();
2478  return -1;
2479  }
2480  } else {
2481  dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
2482  $this->error = 'Status of the object is incompatible '.$this->statut;
2483  return -2;
2484  }
2485  }
2486 
2494  public function setMulticurrencyRate($rate, $mode = 1)
2495  {
2496  dol_syslog(get_class($this).'::setMulticurrencyRate('.$rate.','.$mode.')');
2497  if ($this->statut >= 0 || $this->element == 'societe') {
2498  $fieldname = 'multicurrency_tx';
2499 
2500  $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2501  $sql .= " SET ".$fieldname." = ".((float) $rate);
2502  $sql .= ' WHERE rowid='.((int) $this->id);
2503 
2504  if ($this->db->query($sql)) {
2505  $this->multicurrency_tx = $rate;
2506 
2507  // Update line price
2508  if (!empty($this->lines)) {
2509  foreach ($this->lines as &$line) {
2510  // Amounts in company currency will be recalculated
2511  if ($mode == 1) {
2512  $line->subprice = 0;
2513  }
2514 
2515  // Amounts in foreign currency will be recalculated
2516  if ($mode == 2) {
2517  $line->multicurrency_subprice = 0;
2518  }
2519 
2520  switch ($this->element) {
2521  case 'propal':
2522  $this->updateline(
2523  $line->id,
2524  $line->subprice,
2525  $line->qty,
2526  $line->remise_percent,
2527  $line->tva_tx,
2528  $line->localtax1_tx,
2529  $line->localtax2_tx,
2530  ($line->description ? $line->description : $line->desc),
2531  'HT',
2532  $line->info_bits,
2533  $line->special_code,
2534  $line->fk_parent_line,
2535  $line->skip_update_total,
2536  $line->fk_fournprice,
2537  $line->pa_ht,
2538  $line->label,
2539  $line->product_type,
2540  $line->date_start,
2541  $line->date_end,
2542  $line->array_options,
2543  $line->fk_unit,
2544  $line->multicurrency_subprice
2545  );
2546  break;
2547  case 'commande':
2548  $this->updateline(
2549  $line->id,
2550  ($line->description ? $line->description : $line->desc),
2551  $line->subprice,
2552  $line->qty,
2553  $line->remise_percent,
2554  $line->tva_tx,
2555  $line->localtax1_tx,
2556  $line->localtax2_tx,
2557  'HT',
2558  $line->info_bits,
2559  $line->date_start,
2560  $line->date_end,
2561  $line->product_type,
2562  $line->fk_parent_line,
2563  $line->skip_update_total,
2564  $line->fk_fournprice,
2565  $line->pa_ht,
2566  $line->label,
2567  $line->special_code,
2568  $line->array_options,
2569  $line->fk_unit,
2570  $line->multicurrency_subprice
2571  );
2572  break;
2573  case 'facture':
2574  $this->updateline(
2575  $line->id,
2576  ($line->description ? $line->description : $line->desc),
2577  $line->subprice,
2578  $line->qty,
2579  $line->remise_percent,
2580  $line->date_start,
2581  $line->date_end,
2582  $line->tva_tx,
2583  $line->localtax1_tx,
2584  $line->localtax2_tx,
2585  'HT',
2586  $line->info_bits,
2587  $line->product_type,
2588  $line->fk_parent_line,
2589  $line->skip_update_total,
2590  $line->fk_fournprice,
2591  $line->pa_ht,
2592  $line->label,
2593  $line->special_code,
2594  $line->array_options,
2595  $line->situation_percent,
2596  $line->fk_unit,
2597  $line->multicurrency_subprice
2598  );
2599  break;
2600  case 'supplier_proposal':
2601  $this->updateline(
2602  $line->id,
2603  $line->subprice,
2604  $line->qty,
2605  $line->remise_percent,
2606  $line->tva_tx,
2607  $line->localtax1_tx,
2608  $line->localtax2_tx,
2609  ($line->description ? $line->description : $line->desc),
2610  'HT',
2611  $line->info_bits,
2612  $line->special_code,
2613  $line->fk_parent_line,
2614  $line->skip_update_total,
2615  $line->fk_fournprice,
2616  $line->pa_ht,
2617  $line->label,
2618  $line->product_type,
2619  $line->array_options,
2620  $line->ref_fourn,
2621  $line->multicurrency_subprice
2622  );
2623  break;
2624  case 'order_supplier':
2625  $this->updateline(
2626  $line->id,
2627  ($line->description ? $line->description : $line->desc),
2628  $line->subprice,
2629  $line->qty,
2630  $line->remise_percent,
2631  $line->tva_tx,
2632  $line->localtax1_tx,
2633  $line->localtax2_tx,
2634  'HT',
2635  $line->info_bits,
2636  $line->product_type,
2637  false,
2638  $line->date_start,
2639  $line->date_end,
2640  $line->array_options,
2641  $line->fk_unit,
2642  $line->multicurrency_subprice,
2643  $line->ref_supplier
2644  );
2645  break;
2646  case 'invoice_supplier':
2647  $this->updateline(
2648  $line->id,
2649  ($line->description ? $line->description : $line->desc),
2650  $line->subprice,
2651  $line->tva_tx,
2652  $line->localtax1_tx,
2653  $line->localtax2_tx,
2654  $line->qty,
2655  0,
2656  'HT',
2657  $line->info_bits,
2658  $line->product_type,
2659  $line->remise_percent,
2660  false,
2661  $line->date_start,
2662  $line->date_end,
2663  $line->array_options,
2664  $line->fk_unit,
2665  $line->multicurrency_subprice,
2666  $line->ref_supplier
2667  );
2668  break;
2669  default:
2670  dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
2671  break;
2672  }
2673  }
2674  }
2675 
2676  return 1;
2677  } else {
2678  dol_syslog(get_class($this).'::setMulticurrencyRate Error '.$sql.' - '.$this->db->error());
2679  $this->error = $this->db->error();
2680  return -1;
2681  }
2682  } else {
2683  dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
2684  $this->error = 'Status of the object is incompatible '.$this->statut;
2685  return -2;
2686  }
2687  }
2688 
2696  public function setPaymentTerms($id, $deposit_percent = null)
2697  {
2698  dol_syslog(get_class($this).'::setPaymentTerms('.$id.', '.var_export($deposit_percent, true).')');
2699  if ($this->statut >= 0 || $this->element == 'societe') {
2700  // TODO uniformize field name
2701  $fieldname = 'fk_cond_reglement';
2702  if ($this->element == 'societe') {
2703  $fieldname = 'cond_reglement';
2704  }
2705  if (get_class($this) == 'Fournisseur') {
2706  $fieldname = 'cond_reglement_supplier';
2707  }
2708 
2709  if (empty($deposit_percent) || $deposit_percent < 0) {
2710  $deposit_percent = getDictionaryValue('c_payment_term', 'deposit_percent', $id);
2711  }
2712 
2713  if ($deposit_percent > 100) {
2714  $deposit_percent = 100;
2715  }
2716 
2717  $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2718  $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2719  if (in_array($this->table_element, array('propal', 'commande'))) {
2720  $sql .= " , deposit_percent = " . (empty($deposit_percent) ? 'NULL' : "'".$this->db->escape($deposit_percent)."'");
2721  }
2722  $sql .= ' WHERE rowid='.((int) $this->id);
2723 
2724  if ($this->db->query($sql)) {
2725  $this->cond_reglement_id = $id;
2726  // for supplier
2727  if (get_class($this) == 'Fournisseur') {
2728  $this->cond_reglement_supplier_id = $id;
2729  }
2730  $this->cond_reglement = $id; // for compatibility
2731  $this->deposit_percent = $deposit_percent;
2732  return 1;
2733  } else {
2734  dol_syslog(get_class($this).'::setPaymentTerms Error '.$sql.' - '.$this->db->error());
2735  $this->error = $this->db->error();
2736  return -1;
2737  }
2738  } else {
2739  dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
2740  $this->error = 'Status of the object is incompatible '.$this->statut;
2741  return -2;
2742  }
2743  }
2744 
2751  public function setTransportMode($id)
2752  {
2753  dol_syslog(get_class($this).'::setTransportMode('.$id.')');
2754  if ($this->statut >= 0 || $this->element == 'societe') {
2755  $fieldname = 'fk_transport_mode';
2756  if ($this->element == 'societe') {
2757  $fieldname = 'transport_mode';
2758  }
2759  if (get_class($this) == 'Fournisseur') {
2760  $fieldname = 'transport_mode_supplier';
2761  }
2762 
2763  $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2764  $sql .= " SET ".$fieldname." = ".(($id > 0 || $id == '0') ? ((int) $id) : 'NULL');
2765  $sql .= ' WHERE rowid='.((int) $this->id);
2766 
2767  if ($this->db->query($sql)) {
2768  $this->transport_mode_id = $id;
2769  // for supplier
2770  if (get_class($this) == 'Fournisseur') {
2771  $this->transport_mode_supplier_id = $id;
2772  }
2773  return 1;
2774  } else {
2775  dol_syslog(get_class($this).'::setTransportMode Error '.$sql.' - '.$this->db->error());
2776  $this->error = $this->db->error();
2777  return -1;
2778  }
2779  } else {
2780  dol_syslog(get_class($this).'::setTransportMode, status of the object is incompatible');
2781  $this->error = 'Status of the object is incompatible '.$this->statut;
2782  return -2;
2783  }
2784  }
2785 
2792  public function setRetainedWarrantyPaymentTerms($id)
2793  {
2794  dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms('.$id.')');
2795  if ($this->statut >= 0 || $this->element == 'societe') {
2796  $fieldname = 'retained_warranty_fk_cond_reglement';
2797 
2798  $sql = 'UPDATE '.$this->db->prefix().$this->table_element;
2799  $sql .= " SET ".$fieldname." = ".((int) $id);
2800  $sql .= ' WHERE rowid='.((int) $this->id);
2801 
2802  if ($this->db->query($sql)) {
2803  $this->retained_warranty_fk_cond_reglement = $id;
2804  return 1;
2805  } else {
2806  dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms Error '.$sql.' - '.$this->db->error());
2807  $this->error = $this->db->error();
2808  return -1;
2809  }
2810  } else {
2811  dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms, status of the object is incompatible');
2812  $this->error = 'Status of the object is incompatible '.$this->statut;
2813  return -2;
2814  }
2815  }
2816 
2824  public function setDeliveryAddress($id)
2825  {
2826  $fieldname = 'fk_delivery_address';
2827  if ($this->element == 'delivery' || $this->element == 'shipping') {
2828  $fieldname = 'fk_address';
2829  }
2830 
2831  $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET ".$fieldname." = ".((int) $id);
2832  $sql .= " WHERE rowid = ".((int) $this->id)." AND fk_statut = 0";
2833 
2834  if ($this->db->query($sql)) {
2835  $this->fk_delivery_address = $id;
2836  return 1;
2837  } else {
2838  $this->error = $this->db->error();
2839  dol_syslog(get_class($this).'::setDeliveryAddress Error '.$this->error);
2840  return -1;
2841  }
2842  }
2843 
2844 
2854  public function setShippingMethod($shipping_method_id, $notrigger = false, $userused = null)
2855  {
2856  global $user;
2857 
2858  if (empty($userused)) {
2859  $userused = $user;
2860  }
2861 
2862  $error = 0;
2863 
2864  if (!$this->table_element) {
2865  dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined", LOG_ERR);
2866  return -1;
2867  }
2868 
2869  $this->db->begin();
2870 
2871  if ($shipping_method_id < 0) {
2872  $shipping_method_id = 'NULL';
2873  }
2874  dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
2875 
2876  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2877  $sql .= " SET fk_shipping_method = ".((int) $shipping_method_id);
2878  $sql .= " WHERE rowid=".((int) $this->id);
2879  $resql = $this->db->query($sql);
2880  if (!$resql) {
2881  dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
2882  $this->error = $this->db->lasterror();
2883  $error++;
2884  } else {
2885  if (!$notrigger) {
2886  // Call trigger
2887  $this->context = array('shippingmethodupdate'=>1);
2888  $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused);
2889  if ($result < 0) {
2890  $error++;
2891  }
2892  // End call trigger
2893  }
2894  }
2895  if ($error) {
2896  $this->db->rollback();
2897  return -1;
2898  } else {
2899  $this->shipping_method_id = ($shipping_method_id == 'NULL') ?null:$shipping_method_id;
2900  $this->db->commit();
2901  return 1;
2902  }
2903  }
2904 
2905 
2912  public function setWarehouse($warehouse_id)
2913  {
2914  if (!$this->table_element) {
2915  dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined", LOG_ERR);
2916  return -1;
2917  }
2918  if ($warehouse_id < 0) {
2919  $warehouse_id = 'NULL';
2920  }
2921  dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
2922 
2923  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2924  $sql .= " SET fk_warehouse = ".((int) $warehouse_id);
2925  $sql .= " WHERE rowid=".((int) $this->id);
2926 
2927  if ($this->db->query($sql)) {
2928  $this->warehouse_id = ($warehouse_id == 'NULL') ?null:$warehouse_id;
2929  return 1;
2930  } else {
2931  dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
2932  $this->error = $this->db->error();
2933  return 0;
2934  }
2935  }
2936 
2937 
2945  public function setDocModel($user, $modelpdf)
2946  {
2947  if (!$this->table_element) {
2948  dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined", LOG_ERR);
2949  return -1;
2950  }
2951 
2952  $newmodelpdf = dol_trunc($modelpdf, 255);
2953 
2954  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
2955  $sql .= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
2956  $sql .= " WHERE rowid = ".((int) $this->id);
2957 
2958  dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
2959  $resql = $this->db->query($sql);
2960  if ($resql) {
2961  $this->model_pdf = $modelpdf;
2962  $this->modelpdf = $modelpdf; // For bakward compatibility
2963  return 1;
2964  } else {
2965  dol_print_error($this->db);
2966  return 0;
2967  }
2968  }
2969 
2970 
2979  public function setBankAccount($fk_account, $notrigger = false, $userused = null)
2980  {
2981  global $user;
2982 
2983  if (empty($userused)) {
2984  $userused = $user;
2985  }
2986 
2987  $error = 0;
2988 
2989  if (!$this->table_element) {
2990  dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined", LOG_ERR);
2991  return -1;
2992  }
2993  $this->db->begin();
2994 
2995  if ($fk_account < 0) {
2996  $fk_account = 'NULL';
2997  }
2998  dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
2999 
3000  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3001  $sql .= " SET fk_account = ".((int) $fk_account);
3002  $sql .= " WHERE rowid=".((int) $this->id);
3003 
3004  $resql = $this->db->query($sql);
3005  if (!$resql) {
3006  dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
3007  $this->error = $this->db->lasterror();
3008  $error++;
3009  } else {
3010  if (!$notrigger) {
3011  // Call trigger
3012  $this->context = array('bankaccountupdate'=>1);
3013  $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused);
3014  if ($result < 0) {
3015  $error++;
3016  }
3017  // End call trigger
3018  }
3019  }
3020  if ($error) {
3021  $this->db->rollback();
3022  return -1;
3023  } else {
3024  $this->fk_account = ($fk_account == 'NULL') ?null:$fk_account;
3025  $this->db->commit();
3026  return 1;
3027  }
3028  }
3029 
3030 
3031  // TODO: Move line related operations to CommonObjectLine?
3032 
3033  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3043  public function line_order($renum = false, $rowidorder = 'ASC', $fk_parent_line = true)
3044  {
3045  // phpcs:enable
3046  if (!$this->table_element_line) {
3047  dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined", LOG_ERR);
3048  return -1;
3049  }
3050  if (!$this->fk_element) {
3051  dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined", LOG_ERR);
3052  return -1;
3053  }
3054 
3055  $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3056  if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3057  $fieldposition = 'position';
3058  }
3059 
3060  // Count number of lines to reorder (according to choice $renum)
3061  $nl = 0;
3062  $sql = "SELECT count(rowid) FROM ".$this->db->prefix().$this->table_element_line;
3063  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3064  if (!$renum) {
3065  $sql .= " AND " . $fieldposition . " = 0";
3066  }
3067  if ($renum) {
3068  $sql .= " AND " . $fieldposition . " <> 0";
3069  }
3070 
3071  dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
3072  $resql = $this->db->query($sql);
3073  if ($resql) {
3074  $row = $this->db->fetch_row($resql);
3075  $nl = $row[0];
3076  } else {
3077  dol_print_error($this->db);
3078  }
3079  if ($nl > 0) {
3080  // The goal of this part is to reorder all lines, with all children lines sharing the same counter that parents.
3081  $rows = array();
3082 
3083  // We first search all lines that are parent lines (for multilevel details lines)
3084  $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3085  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3086  if ($fk_parent_line) {
3087  $sql .= ' AND fk_parent_line IS NULL';
3088  }
3089  $sql .= " ORDER BY " . $fieldposition . " ASC, rowid " . $rowidorder;
3090 
3091  dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
3092  $resql = $this->db->query($sql);
3093  if ($resql) {
3094  $i = 0;
3095  $num = $this->db->num_rows($resql);
3096  while ($i < $num) {
3097  $row = $this->db->fetch_row($resql);
3098  $rows[] = $row[0]; // Add parent line into array rows
3099  $childrens = $this->getChildrenOfLine($row[0]);
3100  if (!empty($childrens)) {
3101  foreach ($childrens as $child) {
3102  array_push($rows, $child);
3103  }
3104  }
3105  $i++;
3106  }
3107 
3108  // Now we set a new number for each lines (parent and children with children included into parent tree)
3109  if (!empty($rows)) {
3110  foreach ($rows as $key => $row) {
3111  $this->updateRangOfLine($row, ($key + 1));
3112  }
3113  }
3114  } else {
3115  dol_print_error($this->db);
3116  }
3117  }
3118  return 1;
3119  }
3120 
3128  public function getChildrenOfLine($id, $includealltree = 0)
3129  {
3130  $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3131  if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3132  $fieldposition = 'position';
3133  }
3134 
3135  $rows = array();
3136 
3137  $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3138  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3139  $sql .= ' AND fk_parent_line = '.((int) $id);
3140  $sql .= " ORDER BY " . $fieldposition . " ASC";
3141 
3142  dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id, LOG_DEBUG);
3143  $resql = $this->db->query($sql);
3144  if ($resql) {
3145  if ($this->db->num_rows($resql) > 0) {
3146  while ($row = $this->db->fetch_row($resql)) {
3147  $rows[] = $row[0];
3148  if (!empty($includealltree)) {
3149  $rows = array_merge($rows, $this->getChildrenOfLine($row[0]), $includealltree);
3150  }
3151  }
3152  }
3153  }
3154  return $rows;
3155  }
3156 
3157  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3165  public function line_up($rowid, $fk_parent_line = true)
3166  {
3167  // phpcs:enable
3168  $this->line_order(false, 'ASC', $fk_parent_line);
3169 
3170  // Get rang of line
3171  $rang = $this->getRangOfLine($rowid);
3172 
3173  // Update position of line
3174  $this->updateLineUp($rowid, $rang);
3175  }
3176 
3177  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3185  public function line_down($rowid, $fk_parent_line = true)
3186  {
3187  // phpcs:enable
3188  $this->line_order(false, 'ASC', $fk_parent_line);
3189 
3190  // Get rang of line
3191  $rang = $this->getRangOfLine($rowid);
3192 
3193  // Get max value for rang
3194  $max = $this->line_max();
3195 
3196  // Update position of line
3197  $this->updateLineDown($rowid, $rang, $max);
3198  }
3199 
3207  public function updateRangOfLine($rowid, $rang)
3208  {
3209  global $hookmanager;
3210  $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
3211  if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3212  $fieldposition = 'position';
3213  }
3214 
3215  $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3216  $sql .= ' WHERE rowid = '.((int) $rowid);
3217 
3218  dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
3219  if (!$this->db->query($sql)) {
3220  dol_print_error($this->db);
3221  return -1;
3222  } else {
3223  $parameters=array('rowid'=>$rowid, 'rang'=>$rang, 'fieldposition' => $fieldposition);
3224  $action='';
3225  $reshook = $hookmanager->executeHooks('afterRankOfLineUpdate', $parameters, $this, $action);
3226  return 1;
3227  }
3228  }
3229 
3230  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3237  public function line_ajaxorder($rows)
3238  {
3239  // phpcs:enable
3240  $num = count($rows);
3241  for ($i = 0; $i < $num; $i++) {
3242  $this->updateRangOfLine($rows[$i], ($i + 1));
3243  }
3244  }
3245 
3253  public function updateLineUp($rowid, $rang)
3254  {
3255  if ($rang > 1) {
3256  $fieldposition = 'rang';
3257  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3258  $fieldposition = 'position';
3259  }
3260 
3261  $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3262  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3263  $sql .= " AND " . $fieldposition . " = " . ((int) ($rang - 1));
3264  if ($this->db->query($sql)) {
3265  $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang - 1));
3266  $sql .= ' WHERE rowid = '.((int) $rowid);
3267  if (!$this->db->query($sql)) {
3268  dol_print_error($this->db);
3269  }
3270  } else {
3271  dol_print_error($this->db);
3272  }
3273  }
3274  }
3275 
3284  public function updateLineDown($rowid, $rang, $max)
3285  {
3286  if ($rang < $max) {
3287  $fieldposition = 'rang';
3288  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3289  $fieldposition = 'position';
3290  }
3291 
3292  $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) $rang);
3293  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3294  $sql .= " AND " . $fieldposition . " = " . ((int) ($rang + 1));
3295  if ($this->db->query($sql)) {
3296  $sql = "UPDATE ".$this->db->prefix().$this->table_element_line." SET ".$fieldposition." = ".((int) ($rang + 1));
3297  $sql .= ' WHERE rowid = '.((int) $rowid);
3298  if (!$this->db->query($sql)) {
3299  dol_print_error($this->db);
3300  }
3301  } else {
3302  dol_print_error($this->db);
3303  }
3304  }
3305  }
3306 
3313  public function getRangOfLine($rowid)
3314  {
3315  $fieldposition = 'rang';
3316  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3317  $fieldposition = 'position';
3318  }
3319 
3320  $sql = "SELECT " . $fieldposition . " FROM ".$this->db->prefix().$this->table_element_line;
3321  $sql .= " WHERE rowid = ".((int) $rowid);
3322 
3323  dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
3324  $resql = $this->db->query($sql);
3325  if ($resql) {
3326  $row = $this->db->fetch_row($resql);
3327  return $row[0];
3328  }
3329  }
3330 
3337  public function getIdOfLine($rang)
3338  {
3339  $fieldposition = 'rang';
3340  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction', 'product_attribute_value'))) {
3341  $fieldposition = 'position';
3342  }
3343 
3344  $sql = "SELECT rowid FROM ".$this->db->prefix().$this->table_element_line;
3345  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3346  $sql .= " AND " . $fieldposition . " = ".((int) $rang);
3347  $resql = $this->db->query($sql);
3348  if ($resql) {
3349  $row = $this->db->fetch_row($resql);
3350  return $row[0];
3351  }
3352  }
3353 
3354  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3361  public function line_max($fk_parent_line = 0)
3362  {
3363  // phpcs:enable
3364  $positionfield = 'rang';
3365  if (in_array($this->table_element, array('bom_bom', 'product_attribute'))) {
3366  $positionfield = 'position';
3367  }
3368 
3369  // Search the last rang with fk_parent_line
3370  if ($fk_parent_line) {
3371  $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
3372  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3373  $sql .= " AND fk_parent_line = ".((int) $fk_parent_line);
3374 
3375  dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
3376  $resql = $this->db->query($sql);
3377  if ($resql) {
3378  $row = $this->db->fetch_row($resql);
3379  if (!empty($row[0])) {
3380  return $row[0];
3381  } else {
3382  return $this->getRangOfLine($fk_parent_line);
3383  }
3384  }
3385  } else {
3386  // If not, search the last rang of element
3387  $sql = "SELECT max(".$positionfield.") FROM ".$this->db->prefix().$this->table_element_line;
3388  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3389 
3390  dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
3391  $resql = $this->db->query($sql);
3392  if ($resql) {
3393  $row = $this->db->fetch_row($resql);
3394  return $row[0];
3395  }
3396  }
3397  }
3398 
3399  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3406  public function update_ref_ext($ref_ext)
3407  {
3408  // phpcs:enable
3409  if (!$this->table_element) {
3410  dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
3411  return -1;
3412  }
3413 
3414  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3415  $sql .= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
3416  $sql .= " WHERE ".(isset($this->table_rowid) ? $this->table_rowid : 'rowid')." = ".((int) $this->id);
3417 
3418  dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
3419  if ($this->db->query($sql)) {
3420  $this->ref_ext = $ref_ext;
3421  return 1;
3422  } else {
3423  $this->error = $this->db->error();
3424  return -1;
3425  }
3426  }
3427 
3428  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3436  public function update_note($note, $suffix = '')
3437  {
3438  // phpcs:enable
3439  global $user;
3440 
3441  if (!$this->table_element) {
3442  $this->error = 'update_note was called on objet with property table_element not defined';
3443  dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
3444  return -1;
3445  }
3446  if (!in_array($suffix, array('', '_public', '_private'))) {
3447  $this->error = 'update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
3448  dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
3449  return -2;
3450  }
3451 
3452  $newsuffix = $suffix;
3453 
3454  // Special cas
3455  if ($this->table_element == 'product' && $newsuffix == '_private') {
3456  $newsuffix = '';
3457  }
3458  if (in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) {
3459  $fieldusermod = "fk_user_mod";
3460  } elseif ($this->table_element == 'ecm_files') {
3461  $fieldusermod = "fk_user_m";
3462  } else {
3463  $fieldusermod = "fk_user_modif";
3464  }
3465  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
3466  $sql .= " SET note".$newsuffix." = ".(!empty($note) ? ("'".$this->db->escape($note)."'") : "NULL");
3467  $sql .= ", ".$fieldusermod." = ".((int) $user->id);
3468  $sql .= " WHERE rowid = ".((int) $this->id);
3469 
3470  dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
3471  if ($this->db->query($sql)) {
3472  if ($suffix == '_public') {
3473  $this->note_public = $note;
3474  } elseif ($suffix == '_private') {
3475  $this->note_private = $note;
3476  } else {
3477  $this->note = $note; // deprecated
3478  $this->note_private = $note;
3479  }
3480  return 1;
3481  } else {
3482  $this->error = $this->db->lasterror();
3483  return -1;
3484  }
3485  }
3486 
3487  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3496  public function update_note_public($note)
3497  {
3498  // phpcs:enable
3499  return $this->update_note($note, '_public');
3500  }
3501 
3502  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3513  public function update_price($exclspec = 0, $roundingadjust = 'none', $nodatabaseupdate = 0, $seller = null)
3514  {
3515  // phpcs:enable
3516  global $conf, $hookmanager, $action;
3517 
3518  $parameters = array('exclspec' => $exclspec, 'roundingadjust' => $roundingadjust, 'nodatabaseupdate' => $nodatabaseupdate, 'seller' => $seller);
3519  $reshook = $hookmanager->executeHooks('updateTotalPrice', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3520  if ($reshook > 0) {
3521  return 1; // replacement code
3522  } elseif ($reshook < 0) {
3523  return -1; // failure
3524  } // reshook = 0 => execute normal code
3525 
3526  // Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
3527  $MODULE = "";
3528  if ($this->element == 'propal') {
3529  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
3530  } elseif ($this->element == 'commande' || $this->element == 'order') {
3531  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
3532  } elseif ($this->element == 'facture' || $this->element == 'invoice') {
3533  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
3534  } elseif ($this->element == 'facture_fourn' || $this->element == 'supplier_invoice' || $this->element == 'invoice_supplier' || $this->element == 'invoice_supplier_rec') {
3535  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
3536  } elseif ($this->element == 'order_supplier' || $this->element == 'supplier_order') {
3537  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
3538  } elseif ($this->element == 'supplier_proposal') {
3539  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
3540  }
3541 
3542  if (!empty($MODULE)) {
3543  if (!empty($conf->global->$MODULE)) {
3544  $modsactivated = explode(',', $conf->global->$MODULE);
3545  foreach ($modsactivated as $mod) {
3546  if ($conf->$mod->enabled) {
3547  return 1; // update was disabled by specific setup
3548  }
3549  }
3550  }
3551  }
3552 
3553  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3554 
3555  if ($roundingadjust == '-1') {
3556  $roundingadjust = 'auto'; // For backward compatibility
3557  }
3558 
3559  $forcedroundingmode = $roundingadjust;
3560  if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) {
3561  $forcedroundingmode = getDolGlobalString('MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND');
3562  } elseif ($forcedroundingmode == 'auto') {
3563  $forcedroundingmode = '0';
3564  }
3565 
3566  $error = 0;
3567 
3568  $multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
3569 
3570  // Define constants to find lines to sum
3571  $fieldtva = 'total_tva';
3572  $fieldlocaltax1 = 'total_localtax1';
3573  $fieldlocaltax2 = 'total_localtax2';
3574  $fieldup = 'subprice';
3575  if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
3576  $fieldtva = 'tva';
3577  $fieldup = 'pu_ht';
3578  }
3579  if ($this->element == 'invoice_supplier_rec') {
3580  $fieldup = 'pu_ht';
3581  }
3582  if ($this->element == 'expensereport') {
3583  $fieldup = 'value_unit';
3584  }
3585 
3586  $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,";
3587  $sql .= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
3588  if ($this->table_element_line == 'facturedet') {
3589  $sql .= ', situation_percent';
3590  }
3591  $sql .= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
3592  $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
3593  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
3594  if ($exclspec) {
3595  $product_field = 'product_type';
3596  if ($this->table_element_line == 'contratdet') {
3597  $product_field = ''; // contratdet table has no product_type field
3598  }
3599  if ($product_field) {
3600  $sql .= " AND ".$product_field." <> 9";
3601  }
3602  }
3603  $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
3604 
3605  dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3606  $resql = $this->db->query($sql);
3607  if ($resql) {
3608  $this->total_ht = 0;
3609  $this->total_tva = 0;
3610  $this->total_localtax1 = 0;
3611  $this->total_localtax2 = 0;
3612  $this->total_ttc = 0;
3613  $total_ht_by_vats = array();
3614  $total_tva_by_vats = array();
3615  $total_ttc_by_vats = array();
3616  $this->multicurrency_total_ht = 0;
3617  $this->multicurrency_total_tva = 0;
3618  $this->multicurrency_total_ttc = 0;
3619 
3620  $num = $this->db->num_rows($resql);
3621  $i = 0;
3622  while ($i < $num) {
3623  $obj = $this->db->fetch_object($resql);
3624 
3625  // Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
3626  $parameters = array('fk_element' => $obj->rowid);
3627  $reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3628 
3629  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'
3630  // This part of code is to fix data. We should not call it too often.
3631  $localtax_array = array($obj->localtax1_type, $obj->localtax1_tx, $obj->localtax2_type, $obj->localtax2_tx);
3632  $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);
3633 
3634  $diff_when_using_price_ht = price2num($tmpcal[1] - $obj->total_tva, 'MT', 1); // If price was set with tax price adn unit price HT has a low number of digits, then we may have a diff on recalculation from unit price HT.
3635  $diff_on_current_total = price2num($obj->total_ttc - $obj->total_ht - $obj->total_tva - $obj->total_localtax1 - $obj->total_localtax2, 'MT', 1);
3636  //var_dump($obj->total_ht.' '.$obj->total_tva.' '.$obj->total_localtax1.' '.$obj->total_localtax2.' =? '.$obj->total_ttc);
3637  //var_dump($diff_when_using_price_ht.' '.$diff_on_current_total);
3638 
3639  if ($diff_when_using_price_ht && $diff_on_current_total) {
3640  $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);
3641  dol_syslog('We found unconsistent 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, LOG_WARNING);
3642  $resqlfix = $this->db->query($sqlfix);
3643  if (!$resqlfix) {
3644  dol_print_error($this->db, 'Failed to update line');
3645  }
3646  $obj->total_tva = $tmpcal[1];
3647  $obj->total_ttc = $tmpcal[2];
3648  }
3649  }
3650 
3651  $this->total_ht += $obj->total_ht; // The field visible at end of line detail
3652  $this->total_tva += $obj->total_tva;
3653  $this->total_localtax1 += $obj->total_localtax1;
3654  $this->total_localtax2 += $obj->total_localtax2;
3655  $this->total_ttc += $obj->total_ttc;
3656  $this->multicurrency_total_ht += $obj->multicurrency_total_ht; // The field visible at end of line detail
3657  $this->multicurrency_total_tva += $obj->multicurrency_total_tva;
3658  $this->multicurrency_total_ttc += $obj->multicurrency_total_ttc;
3659 
3660  if (!isset($total_ht_by_vats[$obj->vatrate])) {
3661  $total_ht_by_vats[$obj->vatrate] = 0;
3662  }
3663  if (!isset($total_tva_by_vats[$obj->vatrate])) {
3664  $total_tva_by_vats[$obj->vatrate] = 0;
3665  }
3666  if (!isset($total_ttc_by_vats[$obj->vatrate])) {
3667  $total_ttc_by_vats[$obj->vatrate] = 0;
3668  }
3669  $total_ht_by_vats[$obj->vatrate] += $obj->total_ht;
3670  $total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
3671  $total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
3672 
3673  if ($forcedroundingmode == '1') { // Check if we need adjustement onto line for vat. TODO This works on the company currency but not on multicurrency
3674  $tmpvat = price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
3675  $diff = price2num($total_tva_by_vats[$obj->vatrate] - $tmpvat, 'MT', 1);
3676  //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";
3677  if ($diff) {
3678  if (abs($diff) > 0.1) {
3679  $errmsg = 'A rounding difference was detected into TOTAL but is too high to be corrected. Some data in your line may be corrupted. Try to edit each line manually.';
3680  dol_syslog($errmsg, LOG_WARNING);
3681  dol_print_error('', $errmsg);
3682  exit;
3683  }
3684  $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);
3685  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);
3686 
3687  $resqlfix = $this->db->query($sqlfix);
3688 
3689  if (!$resqlfix) {
3690  dol_print_error($this->db, 'Failed to update line');
3691  }
3692 
3693  $this->total_tva = (float) price2num($this->total_tva - $diff, '', 1);
3694  $this->total_ttc = (float) price2num($this->total_ttc - $diff, '', 1);
3695  $total_tva_by_vats[$obj->vatrate] = (float) price2num($total_tva_by_vats[$obj->vatrate] - $diff, '', 1);
3696  $total_ttc_by_vats[$obj->vatrate] = (float) price2num($total_ttc_by_vats[$obj->vatrate] - $diff, '', 1);
3697  }
3698  }
3699 
3700  $i++;
3701  }
3702 
3703  // Add revenue stamp to total
3704  $this->total_ttc += isset($this->revenuestamp) ? $this->revenuestamp : 0;
3705  $this->multicurrency_total_ttc += isset($this->revenuestamp) ? ($this->revenuestamp * $multicurrency_tx) : 0;
3706 
3707  // Situations totals
3708  if (!empty($this->situation_cycle_ref) && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits') && $this->type != $this::TYPE_CREDIT_NOTE) {
3709  $prev_sits = $this->get_prev_sits();
3710 
3711  foreach ($prev_sits as $sit) { // $sit is an object Facture loaded with a fetch.
3712  $this->total_ht -= $sit->total_ht;
3713  $this->total_tva -= $sit->total_tva;
3714  $this->total_localtax1 -= $sit->total_localtax1;
3715  $this->total_localtax2 -= $sit->total_localtax2;
3716  $this->total_ttc -= $sit->total_ttc;
3717  $this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
3718  $this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
3719  $this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
3720  }
3721  }
3722 
3723  // Clean total
3724  $this->total_ht = (float) price2num($this->total_ht);
3725  $this->total_tva = (float) price2num($this->total_tva);
3726  $this->total_localtax1 = (float) price2num($this->total_localtax1);
3727  $this->total_localtax2 = (float) price2num($this->total_localtax2);
3728  $this->total_ttc = (float) price2num($this->total_ttc);
3729 
3730  $this->db->free($resql);
3731 
3732  // Now update global fields total_ht, total_ttc, total_tva, total_localtax1, total_localtax2, multicurrency_total_*
3733  $fieldht = 'total_ht';
3734  $fieldtva = 'tva';
3735  $fieldlocaltax1 = 'localtax1';
3736  $fieldlocaltax2 = 'localtax2';
3737  $fieldttc = 'total_ttc';
3738  // Specific code for backward compatibility with old field names
3739  if ($this->element == 'facture' || $this->element == 'facturerec') {
3740  $fieldtva = 'total_tva';
3741  }
3742  if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier' || $this->element == 'invoice_supplier_rec') {
3743  $fieldtva = 'total_tva';
3744  }
3745  if ($this->element == 'propal') {
3746  $fieldtva = 'total_tva';
3747  }
3748  if ($this->element == 'expensereport') {
3749  $fieldtva = 'total_tva';
3750  }
3751  if ($this->element == 'supplier_proposal') {
3752  $fieldtva = 'total_tva';
3753  }
3754  if ($this->element == 'commande') {
3755  $fieldtva = 'total_tva';
3756  }
3757  if ($this->element == 'order_supplier') {
3758  $fieldtva = 'total_tva';
3759  }
3760 
3761  if (empty($nodatabaseupdate)) {
3762  $sql = "UPDATE ".$this->db->prefix().$this->table_element.' SET';
3763  $sql .= " ".$fieldht." = ".((float) price2num($this->total_ht, 'MT', 1)).",";
3764  $sql .= " ".$fieldtva." = ".((float) price2num($this->total_tva, 'MT', 1)).",";
3765  $sql .= " ".$fieldlocaltax1." = ".((float) price2num($this->total_localtax1, 'MT', 1)).",";
3766  $sql .= " ".$fieldlocaltax2." = ".((float) price2num($this->total_localtax2, 'MT', 1)).",";
3767  $sql .= " ".$fieldttc." = ".((float) price2num($this->total_ttc, 'MT', 1));
3768  $sql .= ", multicurrency_total_ht = ".((float) price2num($this->multicurrency_total_ht, 'MT', 1));
3769  $sql .= ", multicurrency_total_tva = ".((float) price2num($this->multicurrency_total_tva, 'MT', 1));
3770  $sql .= ", multicurrency_total_ttc = ".((float) price2num($this->multicurrency_total_ttc, 'MT', 1));
3771  $sql .= " WHERE rowid = ".((int) $this->id);
3772 
3773  dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3774  $resql = $this->db->query($sql);
3775 
3776  if (!$resql) {
3777  $error++;
3778  $this->error = $this->db->lasterror();
3779  $this->errors[] = $this->db->lasterror();
3780  }
3781  }
3782 
3783  if (!$error) {
3784  return 1;
3785  } else {
3786  return -1;
3787  }
3788  } else {
3789  dol_print_error($this->db, 'Bad request in update_price');
3790  return -1;
3791  }
3792  }
3793 
3794  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3805  public function add_object_linked($origin = null, $origin_id = null, $f_user = null, $notrigger = 0)
3806  {
3807  // phpcs:enable
3808  global $user, $hookmanager, $action;
3809  $origin = (!empty($origin) ? $origin : $this->origin);
3810  $origin_id = (!empty($origin_id) ? $origin_id : $this->origin_id);
3811  $f_user = isset($f_user) ? $f_user : $user;
3812 
3813  // Special case
3814  if ($origin == 'order') {
3815  $origin = 'commande';
3816  }
3817  if ($origin == 'invoice') {
3818  $origin = 'facture';
3819  }
3820  if ($origin == 'invoice_template') {
3821  $origin = 'facturerec';
3822  }
3823  if ($origin == 'supplierorder') {
3824  $origin = 'order_supplier';
3825  }
3826 
3827  // 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.
3828  // 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.
3829  $coremodule = array('knowledgemanagement', 'partnership', 'workstation', 'ticket', 'recruitment', 'eventorganization', 'asset');
3830  // Add module part to target type if object has $module property and isn't in core modules.
3831  $targettype = ((!empty($this->module) && ! in_array($this->module, $coremodule)) ? $this->module.'_' : '').$this->element;
3832 
3833  $parameters = array('targettype'=>$targettype);
3834  // Hook for explicitly set the targettype if it must be differtent than $this->element
3835  $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3836  if ($reshook > 0) {
3837  if (!empty($hookmanager->resArray['targettype'])) $targettype = $hookmanager->resArray['targettype'];
3838  }
3839 
3840  $this->db->begin();
3841  $error = 0;
3842 
3843  $sql = "INSERT INTO " . $this->db->prefix() . "element_element (";
3844  $sql .= "fk_source";
3845  $sql .= ", sourcetype";
3846  $sql .= ", fk_target";
3847  $sql .= ", targettype";
3848  $sql .= ") VALUES (";
3849  $sql .= ((int) $origin_id);
3850  $sql .= ", '" . $this->db->escape($origin) . "'";
3851  $sql .= ", " . ((int) $this->id);
3852  $sql .= ", '" . $this->db->escape($targettype) . "'";
3853  $sql .= ")";
3854 
3855  dol_syslog(get_class($this) . "::add_object_linked", LOG_DEBUG);
3856  if ($this->db->query($sql)) {
3857  if (!$notrigger) {
3858  // Call trigger
3859  $this->context['link_origin'] = $origin;
3860  $this->context['link_origin_id'] = $origin_id;
3861  $result = $this->call_trigger('OBJECT_LINK_INSERT', $f_user);
3862  if ($result < 0) {
3863  $error++;
3864  }
3865  // End call triggers
3866  }
3867  } else {
3868  $this->error = $this->db->lasterror();
3869  $error++;
3870  }
3871 
3872  if (!$error) {
3873  $this->db->commit();
3874  return 1;
3875  } else {
3876  $this->db->rollback();
3877  return 0;
3878  }
3879  }
3880 
3903  public function fetchObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1)
3904  {
3905  global $conf, $hookmanager, $action;
3906 
3907  // Important for pdf generation time reduction
3908  // This boolean is true if $this->linkedObjects has already been loaded with all objects linked without filter
3909  if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
3910  return 1;
3911  }
3912 
3913  $this->linkedObjectsIds = array();
3914  $this->linkedObjects = array();
3915 
3916  $justsource = false;
3917  $justtarget = false;
3918  $withtargettype = false;
3919  $withsourcetype = false;
3920 
3921  $parameters = array('sourcetype'=>$sourcetype, 'sourceid'=>$sourceid, 'targettype'=>$targettype, 'targetid'=>$targetid);
3922  // Hook for explicitly set the targettype if it must be differtent than $this->element
3923  $reshook = $hookmanager->executeHooks('setLinkedObjectSourceTargetType', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3924  if ($reshook > 0) {
3925  if (!empty($hookmanager->resArray['sourcetype'])) $sourcetype = $hookmanager->resArray['sourcetype'];
3926  if (!empty($hookmanager->resArray['sourceid'])) $sourceid = $hookmanager->resArray['sourceid'];
3927  if (!empty($hookmanager->resArray['targettype'])) $targettype = $hookmanager->resArray['targettype'];
3928  if (!empty($hookmanager->resArray['targetid'])) $targetid = $hookmanager->resArray['targetid'];
3929  }
3930 
3931  if (!empty($sourceid) && !empty($sourcetype) && empty($targetid)) {
3932  $justsource = true; // the source (id and type) is a search criteria
3933  if (!empty($targettype)) {
3934  $withtargettype = true;
3935  }
3936  }
3937  if (!empty($targetid) && !empty($targettype) && empty($sourceid)) {
3938  $justtarget = true; // the target (id and type) is a search criteria
3939  if (!empty($sourcetype)) {
3940  $withsourcetype = true;
3941  }
3942  }
3943 
3944  $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
3945  $targetid = (!empty($targetid) ? $targetid : $this->id);
3946  $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
3947  $targettype = (!empty($targettype) ? $targettype : $this->element);
3948 
3949  /*if (empty($sourceid) && empty($targetid))
3950  {
3951  dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
3952  return -1;
3953  }*/
3954 
3955  // Links between objects are stored in table element_element
3956  $sql = "SELECT rowid, fk_source, sourcetype, fk_target, targettype";
3957  $sql .= " FROM ".$this->db->prefix()."element_element";
3958  $sql .= " WHERE ";
3959  if ($justsource || $justtarget) {
3960  if ($justsource) {
3961  $sql .= "fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."'";
3962  if ($withtargettype) {
3963  $sql .= " AND targettype = '".$this->db->escape($targettype)."'";
3964  }
3965  } elseif ($justtarget) {
3966  $sql .= "fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."'";
3967  if ($withsourcetype) {
3968  $sql .= " AND sourcetype = '".$this->db->escape($sourcetype)."'";
3969  }
3970  }
3971  } else {
3972  $sql .= "(fk_source = ".((int) $sourceid)." AND sourcetype = '".$this->db->escape($sourcetype)."')";
3973  $sql .= " ".$clause." (fk_target = ".((int) $targetid)." AND targettype = '".$this->db->escape($targettype)."')";
3974  if ($loadalsoobjects && $this->id > 0 && $sourceid == $this->id && $sourcetype == $this->element && $targetid == $this->id && $targettype == $this->element && $clause == 'OR') {
3975  $this->linkedObjectsFullLoaded[$this->id] = true;
3976  }
3977  }
3978  $sql .= " ORDER BY ".$orderby;
3979 
3980  dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
3981  $resql = $this->db->query($sql);
3982  if ($resql) {
3983  $num = $this->db->num_rows($resql);
3984  $i = 0;
3985  while ($i < $num) {
3986  $obj = $this->db->fetch_object($resql);
3987  if ($justsource || $justtarget) {
3988  if ($justsource) {
3989  $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
3990  } elseif ($justtarget) {
3991  $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
3992  }
3993  } else {
3994  if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype) {
3995  $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
3996  }
3997  if ($obj->fk_target == $targetid && $obj->targettype == $targettype) {
3998  $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
3999  }
4000  }
4001  $i++;
4002  }
4003 
4004  if (!empty($this->linkedObjectsIds)) {
4005  $tmparray = $this->linkedObjectsIds;
4006  foreach ($tmparray as $objecttype => $objectids) { // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
4007  // Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
4008  $module = $element = $subelement = $objecttype;
4009  $regs = array();
4010  if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
4011  && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs)) {
4012  $module = $element = $regs[1];
4013  $subelement = $regs[2];
4014  }
4015 
4016  $classpath = $element.'/class';
4017  // To work with non standard classpath or module name
4018  if ($objecttype == 'facture') {
4019  $classpath = 'compta/facture/class';
4020  } elseif ($objecttype == 'facturerec') {
4021  $classpath = 'compta/facture/class';
4022  $module = 'facture';
4023  } elseif ($objecttype == 'propal') {
4024  $classpath = 'comm/propal/class';
4025  } elseif ($objecttype == 'supplier_proposal') {
4026  $classpath = 'supplier_proposal/class';
4027  } elseif ($objecttype == 'shipping') {
4028  $classpath = 'expedition/class';
4029  $subelement = 'expedition';
4030  $module = 'expedition_bon';
4031  } elseif ($objecttype == 'delivery') {
4032  $classpath = 'delivery/class';
4033  $subelement = 'delivery';
4034  $module = 'delivery_note';
4035  } elseif ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier') {
4036  $classpath = 'fourn/class';
4037  $module = 'fournisseur';
4038  } elseif ($objecttype == 'fichinter') {
4039  $classpath = 'fichinter/class';
4040  $subelement = 'fichinter';
4041  $module = 'ficheinter';
4042  } elseif ($objecttype == 'subscription') {
4043  $classpath = 'adherents/class';
4044  $module = 'adherent';
4045  } elseif ($objecttype == 'contact') {
4046  $module = 'societe';
4047  }
4048  // Set classfile
4049  $classfile = strtolower($subelement);
4050  $classname = ucfirst($subelement);
4051 
4052  if ($objecttype == 'order') {
4053  $classfile = 'commande';
4054  $classname = 'Commande';
4055  } elseif ($objecttype == 'invoice_supplier') {
4056  $classfile = 'fournisseur.facture';
4057  $classname = 'FactureFournisseur';
4058  } elseif ($objecttype == 'order_supplier') {
4059  $classfile = 'fournisseur.commande';
4060  $classname = 'CommandeFournisseur';
4061  } elseif ($objecttype == 'supplier_proposal') {
4062  $classfile = 'supplier_proposal';
4063  $classname = 'SupplierProposal';
4064  } elseif ($objecttype == 'facturerec') {
4065  $classfile = 'facture-rec';
4066  $classname = 'FactureRec';
4067  } elseif ($objecttype == 'subscription') {
4068  $classfile = 'subscription';
4069  $classname = 'Subscription';
4070  } elseif ($objecttype == 'project' || $objecttype == 'projet') {
4071  $classpath = 'projet/class';
4072  $classfile = 'project';
4073  $classname = 'Project';
4074  } elseif ($objecttype == 'conferenceorboothattendee') {
4075  $classpath = 'eventorganization/class';
4076  $classfile = 'conferenceorboothattendee';
4077  $classname = 'ConferenceOrBoothAttendee';
4078  $module = 'eventorganization';
4079  } elseif ($objecttype == 'conferenceorbooth') {
4080  $classpath = 'eventorganization/class';
4081  $classfile = 'conferenceorbooth';
4082  $classname = 'ConferenceOrBooth';
4083  $module = 'eventorganization';
4084  } elseif ($objecttype == 'mo') {
4085  $classpath = 'mrp/class';
4086  $classfile = 'mo';
4087  $classname = 'Mo';
4088  $module = 'mrp';
4089  }
4090 
4091  // Here $module, $classfile and $classname are set, we can use them.
4092  if ($conf->$module->enabled && (($element != $this->element) || $alsosametype)) {
4093  if ($loadalsoobjects && (is_numeric($loadalsoobjects) || ($loadalsoobjects === $objecttype))) {
4094  dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
4095  //print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
4096  if (class_exists($classname)) {
4097  foreach ($objectids as $i => $objectid) { // $i is rowid into llx_element_element
4098  $object = new $classname($this->db);
4099  $ret = $object->fetch($objectid);
4100  if ($ret >= 0) {
4101  $this->linkedObjects[$objecttype][$i] = $object;
4102  }
4103  }
4104  }
4105  }
4106  } else {
4107  unset($this->linkedObjectsIds[$objecttype]);
4108  }
4109  }
4110  }
4111  return 1;
4112  } else {
4113  dol_print_error($this->db);
4114  return -1;
4115  }
4116  }
4117 
4124  public function clearObjectLinkedCache()
4125  {
4126  if ($this->id > 0 && !empty($this->linkedObjectsFullLoaded[$this->id])) {
4127  unset($this->linkedObjectsFullLoaded[$this->id]);
4128  }
4129 
4130  return 1;
4131  }
4132 
4145  public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $f_user = null, $notrigger = 0)
4146  {
4147  global $user;
4148  $updatesource = false;
4149  $updatetarget = false;
4150  $f_user = isset($f_user) ? $f_user : $user;
4151 
4152  if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4153  $updatesource = true;
4154  } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4155  $updatetarget = true;
4156  }
4157 
4158  $this->db->begin();
4159  $error = 0;
4160 
4161  $sql = "UPDATE " . $this->db->prefix() . "element_element SET ";
4162  if ($updatesource) {
4163  $sql .= "fk_source = " . ((int) $sourceid);
4164  $sql .= ", sourcetype = '" . $this->db->escape($sourcetype) . "'";
4165  $sql .= " WHERE fk_target = " . ((int) $this->id);
4166  $sql .= " AND targettype = '" . $this->db->escape($this->element) . "'";
4167  } elseif ($updatetarget) {
4168  $sql .= "fk_target = " . ((int) $targetid);
4169  $sql .= ", targettype = '" . $this->db->escape($targettype) . "'";
4170  $sql .= " WHERE fk_source = " . ((int) $this->id);
4171  $sql .= " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4172  }
4173 
4174  dol_syslog(get_class($this) . "::updateObjectLinked", LOG_DEBUG);
4175  if ($this->db->query($sql)) {
4176  if (!$notrigger) {
4177  // Call trigger
4178  $this->context['link_source_id'] = $sourceid;
4179  $this->context['link_source_type'] = $sourcetype;
4180  $this->context['link_target_id'] = $targetid;
4181  $this->context['link_target_type'] = $targettype;
4182  $result = $this->call_trigger('OBJECT_LINK_MODIFY', $f_user);
4183  if ($result < 0) {
4184  $error++;
4185  }
4186  // End call triggers
4187  }
4188  } else {
4189  $this->error = $this->db->lasterror();
4190  $error++;
4191  }
4192 
4193  if (!$error) {
4194  $this->db->commit();
4195  return 1;
4196  } else {
4197  $this->db->rollback();
4198  return -1;
4199  }
4200  }
4201 
4215  public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = '', $f_user = null, $notrigger = 0)
4216  {
4217  global $user;
4218  $deletesource = false;
4219  $deletetarget = false;
4220  $f_user = isset($f_user) ? $f_user : $user;
4221 
4222  if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) {
4223  $deletesource = true;
4224  } elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) {
4225  $deletetarget = true;
4226  }
4227 
4228  $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
4229  $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
4230  $targetid = (!empty($targetid) ? $targetid : $this->id);
4231  $targettype = (!empty($targettype) ? $targettype : $this->element);
4232  $this->db->begin();
4233  $error = 0;
4234 
4235  if (!$notrigger) {
4236  // Call trigger
4237  $this->context['link_id'] = $rowid;
4238  $this->context['link_source_id'] = $sourceid;
4239  $this->context['link_source_type'] = $sourcetype;
4240  $this->context['link_target_id'] = $targetid;
4241  $this->context['link_target_type'] = $targettype;
4242  $result = $this->call_trigger('OBJECT_LINK_DELETE', $f_user);
4243  if ($result < 0) {
4244  $error++;
4245  }
4246  // End call triggers
4247  }
4248 
4249  if (!$error) {
4250  $sql = "DELETE FROM " . $this->db->prefix() . "element_element";
4251  $sql .= " WHERE";
4252  if ($rowid > 0) {
4253  $sql .= " rowid = " . ((int) $rowid);
4254  } else {
4255  if ($deletesource) {
4256  $sql .= " fk_source = " . ((int) $sourceid) . " AND sourcetype = '" . $this->db->escape($sourcetype) . "'";
4257  $sql .= " AND fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "'";
4258  } elseif ($deletetarget) {
4259  $sql .= " fk_target = " . ((int) $targetid) . " AND targettype = '" . $this->db->escape($targettype) . "'";
4260  $sql .= " AND fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "'";
4261  } else {
4262  $sql .= " (fk_source = " . ((int) $this->id) . " AND sourcetype = '" . $this->db->escape($this->element) . "')";
4263  $sql .= " OR";
4264  $sql .= " (fk_target = " . ((int) $this->id) . " AND targettype = '" . $this->db->escape($this->element) . "')";
4265  }
4266  }
4267 
4268  dol_syslog(get_class($this) . "::deleteObjectLinked", LOG_DEBUG);
4269  if (!$this->db->query($sql)) {
4270  $this->error = $this->db->lasterror();
4271  $this->errors[] = $this->error;
4272  $error++;
4273  }
4274  }
4275 
4276  if (!$error) {
4277  $this->db->commit();
4278  return 1;
4279  } else {
4280  $this->db->rollback();
4281  return 0;
4282  }
4283  }
4284 
4294  public static function getAllItemsLinkedByObjectID($fk_object_where, $field_select, $field_where, $table_element)
4295  {
4296  if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4297  return -1;
4298  }
4299 
4300  global $db;
4301 
4302  $sql = "SELECT ".$field_select." FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4303  $resql = $db->query($sql);
4304 
4305  $TRes = array();
4306  if (!empty($resql)) {
4307  while ($res = $db->fetch_object($resql)) {
4308  $TRes[] = $res->{$field_select};
4309  }
4310  }
4311 
4312  return $TRes;
4313  }
4314 
4323  public static function deleteAllItemsLinkedByObjectID($fk_object_where, $field_where, $table_element)
4324  {
4325  if (empty($fk_object_where) || empty($field_where) || empty($table_element)) {
4326  return -1;
4327  }
4328 
4329  global $db;
4330 
4331  $sql = "DELETE FROM ".$db->prefix().$table_element." WHERE ".$field_where." = ".((int) $fk_object_where);
4332  $resql = $db->query($sql);
4333 
4334  if (empty($resql)) {
4335  return 0;
4336  }
4337 
4338  return 1;
4339  }
4340 
4351  public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '', $fieldstatus = 'fk_statut')
4352  {
4353  global $user, $langs, $conf;
4354 
4355  $savElementId = $elementId; // To be used later to know if we were using the method using the id of this or not.
4356 
4357  $elementId = (!empty($elementId) ? $elementId : $this->id);
4358  $elementTable = (!empty($elementType) ? $elementType : $this->table_element);
4359 
4360  $this->db->begin();
4361 
4362  if ($elementTable == 'facture_rec') {
4363  $fieldstatus = "suspended";
4364  }
4365  if ($elementTable == 'mailing') {
4366  $fieldstatus = "statut";
4367  }
4368  if ($elementTable == 'cronjob') {
4369  $fieldstatus = "status";
4370  }
4371  if ($elementTable == 'user') {
4372  $fieldstatus = "statut";
4373  }
4374  if ($elementTable == 'expensereport') {
4375  $fieldstatus = "fk_statut";
4376  }
4377  if ($elementTable == 'commande_fournisseur_dispatch') {
4378  $fieldstatus = "status";
4379  }
4380  if (isset($this->fields) && is_array($this->fields) && array_key_exists('status', $this->fields)) {
4381  $fieldstatus = 'status';
4382  }
4383 
4384  $sql = "UPDATE ".$this->db->prefix().$elementTable;
4385  $sql .= " SET ".$fieldstatus." = ".((int) $status);
4386  // If status = 1 = validated, update also fk_user_valid
4387  // TODO Replace the test on $elementTable by doing a test on existence of the field in $this->fields
4388  if ($status == 1 && in_array($elementTable, array('expensereport', 'inventory'))) {
4389  $sql .= ", fk_user_valid = ".((int) $user->id);
4390  }
4391  if ($status == 1 && in_array($elementTable, array('expensereport'))) {
4392  $sql .= ", date_valid = '".$this->db->idate(dol_now())."'";
4393  }
4394  if ($status == 1 && in_array($elementTable, array('inventory'))) {
4395  $sql .= ", date_validation = '".$this->db->idate(dol_now())."'";
4396  }
4397  $sql .= " WHERE rowid = ".((int) $elementId);
4398  $sql .= " AND ".$fieldstatus." <> ".((int) $status); // We avoid update if status already correct
4399 
4400  dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
4401  $resql = $this->db->query($sql);
4402  if ($resql) {
4403  $error = 0;
4404 
4405  $nb_rows_affected = $this->db->affected_rows($resql); // should be 1 or 0 if status was already correct
4406 
4407  if ($nb_rows_affected > 0) {
4408  if (empty($trigkey)) {
4409  // Try to guess trigkey (for backward compatibility, now we should have trigkey defined into the call of setStatus)
4410  if ($this->element == 'supplier_proposal' && $status == 2) {
4411  $trigkey = 'SUPPLIER_PROPOSAL_SIGN'; // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
4412  }
4413  if ($this->element == 'supplier_proposal' && $status == 3) {
4414  $trigkey = 'SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
4415  }
4416  if ($this->element == 'supplier_proposal' && $status == 4) {
4417  $trigkey = 'SUPPLIER_PROPOSAL_CLOSE'; // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
4418  }
4419  if ($this->element == 'fichinter' && $status == 3) {
4420  $trigkey = 'FICHINTER_CLASSIFY_DONE';
4421  }
4422  if ($this->element == 'fichinter' && $status == 2) {
4423  $trigkey = 'FICHINTER_CLASSIFY_BILLED';
4424  }
4425  if ($this->element == 'fichinter' && $status == 1) {
4426  $trigkey = 'FICHINTER_CLASSIFY_UNBILLED';
4427  }
4428  }
4429 
4430  if ($trigkey) {
4431  // Call trigger
4432  $result = $this->call_trigger($trigkey, $user);
4433  if ($result < 0) {
4434  $error++;
4435  }
4436  // End call triggers
4437  }
4438  } else {
4439  // The status was probably already good. We do nothing more, no triggers.
4440  }
4441 
4442  if (!$error) {
4443  $this->db->commit();
4444 
4445  if (empty($savElementId)) {
4446  // If the element we update is $this (so $elementId was provided as null)
4447  if ($fieldstatus == 'tosell') {
4448  $this->status = $status;
4449  } elseif ($fieldstatus == 'tobuy') {
4450  $this->status_buy = $status;
4451  } else {
4452  $this->statut = $status;
4453  $this->status = $status;
4454  }
4455  }
4456 
4457  return 1;
4458  } else {
4459  $this->db->rollback();
4460  dol_syslog(get_class($this)."::setStatut ".$this->error, LOG_ERR);
4461  return -1;
4462  }
4463  } else {
4464  $this->error = $this->db->lasterror();
4465  $this->db->rollback();
4466  return -1;
4467  }
4468  }
4469 
4470 
4478  public function getCanvas($id = 0, $ref = '')
4479  {
4480  global $conf;
4481 
4482  if (empty($id) && empty($ref)) {
4483  return 0;
4484  }
4485  if (!empty($conf->global->MAIN_DISABLE_CANVAS)) {
4486  return 0; // To increase speed. Not enabled by default.
4487  }
4488 
4489  // Clean parameters
4490  $ref = trim($ref);
4491 
4492  $sql = "SELECT rowid, canvas";
4493  $sql .= " FROM ".$this->db->prefix().$this->table_element;
4494  $sql .= " WHERE entity IN (".getEntity($this->element).")";
4495  if (!empty($id)) {
4496  $sql .= " AND rowid = ".((int) $id);
4497  }
4498  if (!empty($ref)) {
4499  $sql .= " AND ref = '".$this->db->escape($ref)."'";
4500  }
4501 
4502  $resql = $this->db->query($sql);
4503  if ($resql) {
4504  $obj = $this->db->fetch_object($resql);
4505  if ($obj) {
4506  $this->canvas = $obj->canvas;
4507  return 1;
4508  } else {
4509  return 0;
4510  }
4511  } else {
4512  dol_print_error($this->db);
4513  return -1;
4514  }
4515  }
4516 
4517 
4524  public function getSpecialCode($lineid)
4525  {
4526  $sql = "SELECT special_code FROM ".$this->db->prefix().$this->table_element_line;
4527  $sql .= " WHERE rowid = ".((int) $lineid);
4528  $resql = $this->db->query($sql);
4529  if ($resql) {
4530  $row = $this->db->fetch_row($resql);
4531  return $row[0];
4532  }
4533  }
4534 
4543  public function isObjectUsed($id = 0, $entity = 0)
4544  {
4545  global $langs;
4546 
4547  if (empty($id)) {
4548  $id = $this->id;
4549  }
4550 
4551  // Check parameters
4552  if (!isset($this->childtables) || !is_array($this->childtables) || count($this->childtables) == 0) {
4553  dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
4554  return -1;
4555  }
4556 
4557  $arraytoscan = $this->childtables;
4558  // For backward compatibility, we check if array is old format array('table1', 'table2', ...)
4559  $tmparray = array_keys($this->childtables);
4560  if (is_numeric($tmparray[0])) {
4561  $arraytoscan = array_flip($this->childtables);
4562  }
4563 
4564  // Test if child exists
4565  $haschild = 0;
4566  foreach ($arraytoscan as $table => $element) {
4567  //print $id.'-'.$table.'-'.$elementname.'<br>';
4568  // Check if element can be deleted
4569  $sql = "SELECT COUNT(*) as nb";
4570  $sql.= " FROM ".$this->db->prefix().$table." as c";
4571  if (!empty($element['parent']) && !empty($element['parentkey'])) {
4572  $sql.= ", ".$this->db->prefix().$element['parent']." as p";
4573  }
4574  $sql.= " WHERE c.".$this->fk_element." = ".((int) $id);
4575  if (!empty($element['parent']) && !empty($element['parentkey'])) {
4576  $sql.= " AND c.".$element['parentkey']." = p.rowid";
4577  }
4578  if (!empty($entity)) {
4579  if (!empty($element['parent']) && !empty($element['parentkey'])) {
4580  $sql.= " AND p.entity = ".((int) $entity);
4581  } else {
4582  $sql.= " AND c.entity = ".((int) $entity);
4583  }
4584  }
4585  $resql = $this->db->query($sql);
4586  if ($resql) {
4587  $obj = $this->db->fetch_object($resql);
4588  if ($obj->nb > 0) {
4589  $langs->load("errors");
4590  //print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
4591  $haschild += $obj->nb;
4592  if (is_numeric($element)) { // very old usage array('table1', 'table2', ...)
4593  $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $table);
4594  } elseif (is_string($element)) { // old usage array('table1' => 'TranslateKey1', 'table2' => 'TranslateKey2', ...)
4595  $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element));
4596  } else { // new usage: $element['name']=Translation key
4597  $this->errors[] = $langs->transnoentitiesnoconv("ErrorRecordHasAtLeastOneChildOfType", method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref, $langs->transnoentitiesnoconv($element['name']));
4598  }
4599  break; // We found at least one, we stop here
4600  }
4601  } else {
4602  $this->errors[] = $this->db->lasterror();
4603  return -1;
4604  }
4605  }
4606  if ($haschild > 0) {
4607  $this->errors[] = "ErrorRecordHasChildren";
4608  return $haschild;
4609  } else {
4610  return 0;
4611  }
4612  }
4613 
4620  public function hasProductsOrServices($predefined = -1)
4621  {
4622  $nb = 0;
4623 
4624  foreach ($this->lines as $key => $val) {
4625  $qualified = 0;
4626  if ($predefined == -1) {
4627  $qualified = 1;
4628  }
4629  if ($predefined == 1 && $val->fk_product > 0) {
4630  $qualified = 1;
4631  }
4632  if ($predefined == 0 && $val->fk_product <= 0) {
4633  $qualified = 1;
4634  }
4635  if ($predefined == 2 && $val->fk_product > 0 && $val->product_type == 0) {
4636  $qualified = 1;
4637  }
4638  if ($predefined == 3 && $val->fk_product > 0 && $val->product_type == 1) {
4639  $qualified = 1;
4640  }
4641  if ($qualified) {
4642  $nb++;
4643  }
4644  }
4645  dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
4646  return $nb;
4647  }
4648 
4654  public function getTotalDiscount()
4655  {
4656  if (!empty($this->table_element_line) ) {
4657  $total_discount = 0.00;
4658 
4659  $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
4660  $sql .= " FROM ".$this->db->prefix().$this->table_element_line;
4661  $sql .= " WHERE ".$this->fk_element." = ".((int) $this->id);
4662 
4663  dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
4664  $resql = $this->db->query($sql);
4665  if ($resql) {
4666  $num = $this->db->num_rows($resql);
4667  $i = 0;
4668  while ($i < $num) {
4669  $obj = $this->db->fetch_object($resql);
4670 
4671  $pu_ht = $obj->pu_ht;
4672  $qty = $obj->qty;
4673  $total_ht = $obj->total_ht;
4674 
4675  $total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
4676  $total_discount += $total_discount_line;
4677 
4678  $i++;
4679  }
4680  }
4681 
4682  //print $total_discount; exit;
4683  return price2num($total_discount);
4684  }
4685 
4686  return null;
4687  }
4688 
4689 
4696  public function getTotalWeightVolume()
4697  {
4698  $totalWeight = 0;
4699  $totalVolume = 0;
4700  // defined for shipment only
4701  $totalOrdered = '';
4702  // defined for shipment only
4703  $totalToShip = '';
4704 
4705  foreach ($this->lines as $line) {
4706  if (isset($line->qty_asked)) {
4707  if (empty($totalOrdered)) {
4708  $totalOrdered = 0; // Avoid warning because $totalOrdered is ''
4709  }
4710  $totalOrdered += $line->qty_asked; // defined for shipment only
4711  }
4712  if (isset($line->qty_shipped)) {
4713  if (empty($totalToShip)) {
4714  $totalToShip = 0; // Avoid warning because $totalToShip is ''
4715  }
4716  $totalToShip += $line->qty_shipped; // defined for shipment only
4717  } elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty)) {
4718  if (empty($totalToShip)) {
4719  $totalToShip = 0;
4720  }
4721  $totalToShip += $line->qty; // defined for reception only
4722  }
4723 
4724  // Define qty, weight, volume, weight_units, volume_units
4725  if ($this->element == 'shipping') {
4726  // for shipments
4727  $qty = $line->qty_shipped ? $line->qty_shipped : 0;
4728  } else {
4729  $qty = $line->qty ? $line->qty : 0;
4730  }
4731 
4732  $weight = !empty($line->weight) ? $line->weight : 0;
4733  ($weight == 0 && !empty($line->product->weight)) ? $weight = $line->product->weight : 0;
4734  $volume = !empty($line->volume) ? $line->volume : 0;
4735  ($volume == 0 && !empty($line->product->volume)) ? $volume = $line->product->volume : 0;
4736 
4737  $weight_units = !empty($line->weight_units) ? $line->weight_units : 0;
4738  ($weight_units == 0 && !empty($line->product->weight_units)) ? $weight_units = $line->product->weight_units : 0;
4739  $volume_units = !empty($line->volume_units) ? $line->volume_units : 0;
4740  ($volume_units == 0 && !empty($line->product->volume_units)) ? $volume_units = $line->product->volume_units : 0;
4741 
4742  $weightUnit = 0;
4743  $volumeUnit = 0;
4744  if (!empty($weight_units)) {
4745  $weightUnit = $weight_units;
4746  }
4747  if (!empty($volume_units)) {
4748  $volumeUnit = $volume_units;
4749  }
4750 
4751  if (empty($totalWeight)) {
4752  $totalWeight = 0; // Avoid warning because $totalWeight is ''
4753  }
4754  if (empty($totalVolume)) {
4755  $totalVolume = 0; // Avoid warning because $totalVolume is ''
4756  }
4757 
4758  //var_dump($line->volume_units);
4759  if ($weight_units < 50) { // < 50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
4760  $trueWeightUnit = pow(10, $weightUnit);
4761  $totalWeight += $weight * $qty * $trueWeightUnit;
4762  } else {
4763  if ($weight_units == 99) {
4764  // conversion 1 Pound = 0.45359237 KG
4765  $trueWeightUnit = 0.45359237;
4766  $totalWeight += $weight * $qty * $trueWeightUnit;
4767  } elseif ($weight_units == 98) {
4768  // conversion 1 Ounce = 0.0283495 KG
4769  $trueWeightUnit = 0.0283495;
4770  $totalWeight += $weight * $qty * $trueWeightUnit;
4771  } else {
4772  $totalWeight += $weight * $qty; // This may be wrong if we mix different units
4773  }
4774  }
4775  if ($volume_units < 50) { // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
4776  //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
4777  $trueVolumeUnit = pow(10, $volumeUnit);
4778  //print $line->volume;
4779  $totalVolume += $volume * $qty * $trueVolumeUnit;
4780  } else {
4781  $totalVolume += $volume * $qty; // This may be wrong if we mix different units
4782  }
4783  }
4784 
4785  return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
4786  }
4787 
4788 
4794  public function setExtraParameters()
4795  {
4796  $this->db->begin();
4797 
4798  $extraparams = (!empty($this->extraparams) ? json_encode($this->extraparams) : null);
4799 
4800  $sql = "UPDATE ".$this->db->prefix().$this->table_element;
4801  $sql .= " SET extraparams = ".(!empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
4802  $sql .= " WHERE rowid = ".((int) $this->id);
4803 
4804  dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
4805  $resql = $this->db->query($sql);
4806  if (!$resql) {
4807  $this->error = $this->db->lasterror();
4808  $this->db->rollback();
4809  return -1;
4810  } else {
4811  $this->db->commit();
4812  return 1;
4813  }
4814  }
4815 
4816 
4817  // --------------------
4818  // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
4819  // --------------------
4820 
4821  /* This is to show add lines */
4822 
4832  public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl')
4833  {
4834  global $conf, $user, $langs, $object, $hookmanager, $extrafields;
4835  global $form;
4836 
4837  // Line extrafield
4838  if (!is_object($extrafields)) {
4839  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4840  $extrafields = new ExtraFields($this->db);
4841  }
4842  $extrafields->fetch_name_optionals_label($this->table_element_line);
4843 
4844  // Output template part (modules that overwrite templates must declare this into descriptor)
4845  // Use global variables + $dateSelector + $seller and $buyer
4846  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'.
4847  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4848  foreach ($dirtpls as $module => $reldir) {
4849  if (!empty($module)) {
4850  $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
4851  } else {
4852  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php';
4853  }
4854 
4855  if (empty($conf->file->strict_mode)) {
4856  $res = @include $tpl;
4857  } else {
4858  $res = include $tpl; // for debug
4859  }
4860  if ($res) {
4861  break;
4862  }
4863  }
4864  }
4865 
4866 
4867 
4868  /* This is to show array of line of details */
4869 
4870 
4885  public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl')
4886  {
4887  global $conf, $hookmanager, $langs, $user, $form, $extrafields, $object;
4888  // TODO We should not use global var for this
4889  global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
4890 
4891  // Define usemargins
4892  $usemargins = 0;
4893  if (!empty($conf->margin->enabled) && !empty($this->element) && in_array($this->element, array('facture', 'facturerec', 'propal', 'commande'))) {
4894  $usemargins = 1;
4895  }
4896 
4897  $num = count($this->lines);
4898 
4899  // Line extrafield
4900  if (!is_object($extrafields)) {
4901  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4902  $extrafields = new ExtraFields($this->db);
4903  }
4904  $extrafields->fetch_name_optionals_label($this->table_element_line);
4905 
4906  $parameters = array('num'=>$num, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$this->table_element_line);
4907  $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4908  if (empty($reshook)) {
4909  // Output template part (modules that overwrite templates must declare this into descriptor)
4910  // Use global variables + $dateSelector + $seller and $buyer
4911  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook.
4912  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4913  foreach ($dirtpls as $module => $reldir) {
4914  if (!empty($module)) {
4915  $tpl = dol_buildpath($reldir.'/objectline_title.tpl.php');
4916  } else {
4917  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php';
4918  }
4919  if (empty($conf->file->strict_mode)) {
4920  $res = @include $tpl;
4921  } else {
4922  $res = include $tpl; // for debug
4923  }
4924  if ($res) {
4925  break;
4926  }
4927  }
4928  }
4929 
4930  $i = 0;
4931 
4932  print "<!-- begin printObjectLines() --><tbody>\n";
4933  foreach ($this->lines as $line) {
4934  //Line extrafield
4935  $line->fetch_optionals();
4936 
4937  //if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
4938  if (is_object($hookmanager)) { // Old code is commented on preceding line.
4939  if (empty($line->fk_parent_line)) {
4940  $parameters = array('line'=>$line, 'num'=>$num, 'i'=>$i, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$line->table_element);
4941  $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4942  } else {
4943  $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);
4944  $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4945  }
4946  }
4947  if (empty($reshook)) {
4948  $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafields, $defaulttpldir);
4949  }
4950 
4951  $i++;
4952  }
4953  print "</tbody><!-- end printObjectLines() -->\n";
4954  }
4955 
4973  public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/core/tpl')
4974  {
4975  global $conf, $langs, $user, $object, $hookmanager;
4976  global $form;
4977  global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
4978 
4979  $object_rights = $this->getRights();
4980 
4981  $element = $this->element;
4982 
4983  $text = '';
4984  $description = '';
4985 
4986  // Line in view mode
4987  if ($action != 'editline' || $selected != $line->id) {
4988  // Product
4989  if ($line->fk_product > 0) {
4990  $product_static = new Product($this->db);
4991  $product_static->fetch($line->fk_product);
4992 
4993  $product_static->ref = $line->ref; //can change ref in hook
4994  $product_static->label = !empty($line->label) ? $line->label : ""; //can change label in hook
4995 
4996  $text = $product_static->getNomUrl(1);
4997 
4998  // Define output language and label
4999  if (!empty($conf->global->MAIN_MULTILANGS)) {
5000  if (property_exists($this, 'socid') && !is_object($this->thirdparty)) {
5001  dol_print_error('', 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
5002  return;
5003  }
5004 
5005  $prod = new Product($this->db);
5006  $prod->fetch($line->fk_product);
5007 
5008  $outputlangs = $langs;
5009  $newlang = '';
5010  if (empty($newlang) && GETPOST('lang_id', 'aZ09')) {
5011  $newlang = GETPOST('lang_id', 'aZ09');
5012  }
5013  if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang) && is_object($this->thirdparty)) {
5014  $newlang = $this->thirdparty->default_lang; // To use language of customer
5015  }
5016  if (!empty($newlang)) {
5017  $outputlangs = new Translate("", $conf);
5018  $outputlangs->setDefaultLang($newlang);
5019  }
5020 
5021  $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
5022  } else {
5023  $label = $line->product_label;
5024  }
5025 
5026  $text .= ' - '.(!empty($line->label) ? $line->label : $label);
5027  $description .= (!empty($conf->global->PRODUIT_DESC_IN_FORM) ? '' : (!empty($line->description) ? dol_htmlentitiesbr($line->description) : '')); // Description is what to show on popup. We shown nothing if already into desc.
5028  }
5029 
5030  $line->pu_ttc = price2num((!empty($line->subprice) ? $line->subprice : 0) * (1 + ((!empty($line->tva_tx) ? $line->tva_tx : 0) / 100)), 'MU');
5031 
5032  // Output template part (modules that overwrite templates must declare this into descriptor)
5033  // Use global variables + $dateSelector + $seller and $buyer
5034  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5035  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5036  foreach ($dirtpls as $module => $reldir) {
5037  if (!empty($module)) {
5038  $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
5039  } else {
5040  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php';
5041  }
5042 
5043  if (empty($conf->file->strict_mode)) {
5044  $res = @include $tpl;
5045  } else {
5046  $res = include $tpl; // for debug
5047  }
5048  if ($res) {
5049  break;
5050  }
5051  }
5052  }
5053 
5054  // Line in update mode
5055  if ($this->statut == 0 && $action == 'editline' && $selected == $line->id) {
5056  $label = (!empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
5057 
5058  $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU');
5059 
5060  // Output template part (modules that overwrite templates must declare this into descriptor)
5061  // Use global variables + $dateSelector + $seller and $buyer
5062  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
5063  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5064  foreach ($dirtpls as $module => $reldir) {
5065  if (!empty($module)) {
5066  $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
5067  } else {
5068  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php';
5069  }
5070 
5071  if (empty($conf->file->strict_mode)) {
5072  $res = @include $tpl;
5073  } else {
5074  $res = include $tpl; // for debug
5075  }
5076  if ($res) {
5077  break;
5078  }
5079  }
5080  }
5081  }
5082 
5083 
5084  /* This is to show array of line of details of source object */
5085 
5086 
5097  public function printOriginLinesList($restrictlist = '', $selectedLines = array())
5098  {
5099  global $langs, $hookmanager, $conf, $form, $action;
5100 
5101  print '<tr class="liste_titre">';
5102  print '<td class="linecolref">'.$langs->trans('Ref').'</td>';
5103  print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
5104  print '<td class="linecolvat right">'.$langs->trans('VATRate').'</td>';
5105  print '<td class="linecoluht right">'.$langs->trans('PriceUHT').'</td>';
5106  if (!empty($conf->multicurrency->enabled)) {
5107  print '<td class="linecoluht_currency right">'.$langs->trans('PriceUHTCurrency').'</td>';
5108  }
5109  print '<td class="linecolqty right">'.$langs->trans('Qty').'</td>';
5110  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
5111  print '<td class="linecoluseunit left">'.$langs->trans('Unit').'</td>';
5112  }
5113  print '<td class="linecoldiscount right">'.$langs->trans('ReductionShort').'</td>';
5114  print '<td class="linecolht right">'.$langs->trans('TotalHT').'</td>';
5115  print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
5116  print '</tr>';
5117  $i = 0;
5118 
5119  if (!empty($this->lines)) {
5120  foreach ($this->lines as $line) {
5121  $reshook = 0;
5122  //if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line))) {
5123  if (is_object($hookmanager)) { // Old code is commented on preceding line.
5124  $parameters = array('line'=>$line, 'i'=>$i, 'restrictlist'=>$restrictlist, 'selectedLines'=> $selectedLines);
5125  if (!empty($line->fk_parent_line)) { $parameters['fk_parent_line'] = $line->fk_parent_line; }
5126  $reshook = $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5127  }
5128  if (empty($reshook)) {
5129  $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines);
5130  }
5131 
5132  $i++;
5133  }
5134  }
5135  }
5136 
5150  public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
5151  {
5152  global $langs, $conf;
5153 
5154  //var_dump($line);
5155  if (!empty($line->date_start)) {
5156  $date_start = $line->date_start;
5157  } else {
5158  $date_start = $line->date_debut_prevue;
5159  if ($line->date_debut_reel) {
5160  $date_start = $line->date_debut_reel;
5161  }
5162  }
5163  if (!empty($line->date_end)) {
5164  $date_end = $line->date_end;
5165  } else {
5166  $date_end = $line->date_fin_prevue;
5167  if ($line->date_fin_reel) {
5168  $date_end = $line->date_fin_reel;
5169  }
5170  }
5171 
5172  $this->tpl['id'] = $line->id;
5173 
5174  $this->tpl['label'] = '';
5175  if (!empty($line->fk_parent_line)) {
5176  $this->tpl['label'] .= img_picto('', 'rightarrow');
5177  }
5178 
5179  if (($line->info_bits & 2) == 2) { // TODO Not sure this is used for source object
5180  $discount = new DiscountAbsolute($this->db);
5181  $discount->fk_soc = $this->socid;
5182  $this->tpl['label'] .= $discount->getNomUrl(0, 'discount');
5183  } elseif (!empty($line->fk_product)) {
5184  $productstatic = new Product($this->db);
5185  $productstatic->id = $line->fk_product;
5186  $productstatic->ref = $line->ref;
5187  $productstatic->type = $line->fk_product_type;
5188  if (empty($productstatic->ref)) {
5189  $line->fetch_product();
5190  $productstatic = $line->product;
5191  }
5192 
5193  $this->tpl['label'] .= $productstatic->getNomUrl(1);
5194  $this->tpl['label'] .= ' - '.(!empty($line->label) ? $line->label : $line->product_label);
5195  // Dates
5196  if ($line->product_type == 1 && ($date_start || $date_end)) {
5197  $this->tpl['label'] .= get_date_range($date_start, $date_end);
5198  }
5199  } else {
5200  $this->tpl['label'] .= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
5201  if (!empty($line->desc)) {
5202  $this->tpl['label'] .= $line->desc;
5203  } else {
5204  $this->tpl['label'] .= ($line->label ? '&nbsp;'.$line->label : '');
5205  }
5206 
5207  // Dates
5208  if ($line->product_type == 1 && ($date_start || $date_end)) {
5209  $this->tpl['label'] .= get_date_range($date_start, $date_end);
5210  }
5211  }
5212 
5213  if (!empty($line->desc)) {
5214  if ($line->desc == '(CREDIT_NOTE)') { // TODO Not sure this is used for source object
5215  $discount = new DiscountAbsolute($this->db);
5216  $discount->fetch($line->fk_remise_except);
5217  $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
5218  } elseif ($line->desc == '(DEPOSIT)') { // TODO Not sure this is used for source object
5219  $discount = new DiscountAbsolute($this->db);
5220  $discount->fetch($line->fk_remise_except);
5221  $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
5222  } elseif ($line->desc == '(EXCESS RECEIVED)') {
5223  $discount = new DiscountAbsolute($this->db);
5224  $discount->fetch($line->fk_remise_except);
5225  $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
5226  } elseif ($line->desc == '(EXCESS PAID)') {
5227  $discount = new DiscountAbsolute($this->db);
5228  $discount->fetch($line->fk_remise_except);
5229  $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
5230  } else {
5231  $this->tpl['description'] = dol_trunc($line->desc, 60);
5232  }
5233  } else {
5234  $this->tpl['description'] = '&nbsp;';
5235  }
5236 
5237  // VAT Rate
5238  $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
5239  $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
5240  if (!empty($line->vat_src_code) && !preg_match('/\(/', $this->tpl['vat_rate'])) {
5241  $this->tpl['vat_rate'] .= ' ('.$line->vat_src_code.')';
5242  }
5243 
5244  $this->tpl['price'] = price($line->subprice);
5245  $this->tpl['total_ht'] = price($line->total_ht);
5246  $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
5247  $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
5248  if (!empty($conf->global->PRODUCT_USE_UNITS)) {
5249  $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
5250  }
5251  $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
5252 
5253  // Is the line strike or not
5254  $this->tpl['strike'] = 0;
5255  if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) {
5256  $this->tpl['strike'] = 1;
5257  }
5258 
5259  // Output template part (modules that overwrite templates must declare this into descriptor)
5260  // Use global variables + $dateSelector + $seller and $buyer
5261  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
5262  foreach ($dirtpls as $module => $reldir) {
5263  if (!empty($module)) {
5264  $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
5265  } else {
5266  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php';
5267  }
5268 
5269  if (empty($conf->file->strict_mode)) {
5270  $res = @include $tpl;
5271  } else {
5272  $res = include $tpl; // for debug
5273  }
5274  if ($res) {
5275  break;
5276  }
5277  }
5278  }
5279 
5280 
5281  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5292  public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
5293  {
5294  // phpcs:enable
5295  $this->db->begin();
5296 
5297  $sql = "INSERT INTO ".$this->db->prefix()."element_resources (";
5298  $sql .= "resource_id";
5299  $sql .= ", resource_type";
5300  $sql .= ", element_id";
5301  $sql .= ", element_type";
5302  $sql .= ", busy";
5303  $sql .= ", mandatory";
5304  $sql .= ") VALUES (";
5305  $sql .= $resource_id;
5306  $sql .= ", '".$this->db->escape($resource_type)."'";
5307  $sql .= ", '".$this->db->escape($this->id)."'";
5308  $sql .= ", '".$this->db->escape($this->element)."'";
5309  $sql .= ", '".$this->db->escape($busy)."'";
5310  $sql .= ", '".$this->db->escape($mandatory)."'";
5311  $sql .= ")";
5312 
5313  dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
5314  if ($this->db->query($sql)) {
5315  $this->db->commit();
5316  return 1;
5317  } else {
5318  $this->error = $this->db->lasterror();
5319  $this->db->rollback();
5320  return 0;
5321  }
5322  }
5323 
5324  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5333  public function delete_resource($rowid, $element, $notrigger = 0)
5334  {
5335  // phpcs:enable
5336  global $user;
5337 
5338  $this->db->begin();
5339 
5340  $sql = "DELETE FROM ".$this->db->prefix()."element_resources";
5341  $sql .= " WHERE rowid = ".((int) $rowid);
5342 
5343  dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
5344 
5345  $resql = $this->db->query($sql);
5346  if (!$resql) {
5347  $this->error = $this->db->lasterror();
5348  $this->db->rollback();
5349  return -1;
5350  } else {
5351  if (!$notrigger) {
5352  $result = $this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
5353  if ($result < 0) {
5354  $this->db->rollback();
5355  return -1;
5356  }
5357  }
5358  $this->db->commit();
5359  return 1;
5360  }
5361  }
5362 
5363 
5369  public function __clone()
5370  {
5371  // Force a copy of this->lines, otherwise it will point to same object.
5372  if (isset($this->lines) && is_array($this->lines)) {
5373  $nboflines = count($this->lines);
5374  for ($i = 0; $i < $nboflines; $i++) {
5375  $this->lines[$i] = clone $this->lines[$i];
5376  }
5377  }
5378  }
5379 
5393  protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
5394  {
5395  global $conf, $langs, $user, $hookmanager, $action;
5396 
5397  $srctemplatepath = '';
5398 
5399  $parameters = array('modelspath'=>$modelspath, 'modele'=>$modele, 'outputlangs'=>$outputlangs, 'hidedetails'=>$hidedetails, 'hidedesc'=>$hidedesc, 'hideref'=>$hideref, 'moreparams'=>$moreparams);
5400  $reshook = $hookmanager->executeHooks('commonGenerateDocument', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
5401 
5402  if (empty($reshook)) {
5403  dol_syslog("commonGenerateDocument modele=".$modele." outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
5404 
5405  if (empty($modele)) {
5406  $this->error = 'BadValueForParameterModele';
5407  return -1;
5408  }
5409 
5410  // Increase limit for PDF build
5411  $err = error_reporting();
5412  error_reporting(0);
5413  @set_time_limit(120);
5414  error_reporting($err);
5415 
5416  // If selected model is a filename template (then $modele="modelname" or "modelname:filename")
5417  $tmp = explode(':', $modele, 2);
5418  if (!empty($tmp[1])) {
5419  $modele = $tmp[0];
5420  $srctemplatepath = $tmp[1];
5421  }
5422 
5423  // Search template files
5424  $file = '';
5425  $classname = '';
5426  $filefound = '';
5427  $dirmodels = array('/');
5428  if (is_array($conf->modules_parts['models'])) {
5429  $dirmodels = array_merge($dirmodels, $conf->modules_parts['models']);
5430  }
5431  foreach ($dirmodels as $reldir) {
5432  foreach (array('doc', 'pdf') as $prefix) {
5433  if (in_array(get_class($this), array('Adherent'))) {
5434  // Member module use prefix_modele.class.php
5435  $file = $prefix."_".$modele.".class.php";
5436  } else {
5437  // Other module use prefix_modele.modules.php
5438  $file = $prefix."_".$modele.".modules.php";
5439  }
5440 
5441  // On verifie l'emplacement du modele
5442  $file = dol_buildpath($reldir.$modelspath.$file, 0);
5443  if (file_exists($file)) {
5444  $filefound = $file;
5445  $classname = $prefix.'_'.$modele;
5446  break;
5447  }
5448  }
5449  if ($filefound) {
5450  break;
5451  }
5452  }
5453 
5454  // If generator was found
5455  if ($filefound) {
5456  global $db; // Required to solve a conception default making an include of code using $db instead of $this->db just after.
5457 
5458  require_once $file;
5459 
5460  $obj = new $classname($this->db);
5461 
5462  // If generator is ODT, we must have srctemplatepath defined, if not we set it.
5463  if ($obj->type == 'odt' && empty($srctemplatepath)) {
5464  $varfortemplatedir = $obj->scandir;
5465  if ($varfortemplatedir && !empty($conf->global->$varfortemplatedir)) {
5466  $dirtoscan = $conf->global->$varfortemplatedir;
5467 
5468  $listoffiles = array();
5469 
5470  // Now we add first model found in directories scanned
5471  $listofdir = explode(',', $dirtoscan);
5472  foreach ($listofdir as $key => $tmpdir) {
5473  $tmpdir = trim($tmpdir);
5474  $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
5475  if (!$tmpdir) {
5476  unset($listofdir[$key]);
5477  continue;
5478  }
5479  if (is_dir($tmpdir)) {
5480  $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
5481  if (count($tmpfiles)) {
5482  $listoffiles = array_merge($listoffiles, $tmpfiles);
5483  }
5484  }
5485  }
5486 
5487  if (count($listoffiles)) {
5488  foreach ($listoffiles as $record) {
5489  $srctemplatepath = $record['fullname'];
5490  break;
5491  }
5492  }
5493  }
5494 
5495  if (empty($srctemplatepath)) {
5496  $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
5497  return -1;
5498  }
5499  }
5500 
5501  if ($obj->type == 'odt' && !empty($srctemplatepath)) {
5502  if (!dol_is_file($srctemplatepath)) {
5503  dol_syslog("Failed to locate template file ".$srctemplatepath, LOG_WARNING);
5504  $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
5505  return -1;
5506  }
5507  }
5508 
5509  // We save charset_output to restore it because write_file can change it if needed for
5510  // output format that does not support UTF8.
5511  $sav_charset_output = empty($outputlangs->charset_output) ? '' : $outputlangs->charset_output;
5512 
5513  if (in_array(get_class($this), array('Adherent'))) {
5514  $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, 'tmp_cards', $moreparams);
5515  } else {
5516  $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
5517  }
5518  // After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
5519 
5520  if ($resultwritefile > 0) {
5521  $outputlangs->charset_output = $sav_charset_output;
5522 
5523  // We delete old preview
5524  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
5525  dol_delete_preview($this);
5526 
5527  // Index file in database
5528  if (!empty($obj->result['fullpath'])) {
5529  $destfull = $obj->result['fullpath'];
5530 
5531  // Update the last_main_doc field into main object (if document generator has property ->update_main_doc_field set)
5532  $update_main_doc_field = 0;
5533  if (!empty($obj->update_main_doc_field)) {
5534  $update_main_doc_field = 1;
5535  }
5536 
5537  $this->indexFile($destfull, $update_main_doc_field);
5538  } else {
5539  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);
5540  }
5541 
5542  // Success in building document. We build meta file.
5543  dol_meta_create($this);
5544 
5545  return 1;
5546  } else {
5547  $outputlangs->charset_output = $sav_charset_output;
5548  $this->error = $obj->error;
5549  $this->errors = $obj->errors;
5550  dol_syslog("Error generating document for ".__CLASS__.". Error: ".$obj->error, LOG_ERR);
5551  return -1;
5552  }
5553  } else {
5554  if (!$filefound) {
5555  $this->error = $langs->trans("Error").' Failed to load doc generator with modelpaths='.$modelspath.' - modele='.$modele;
5556  $this->errors[] = $this->error;
5557  dol_syslog($this->error, LOG_ERR);
5558  } else {
5559  $this->error = $langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists", $filefound);
5560  $this->errors[] = $this->error;
5561  dol_syslog($this->error, LOG_ERR);
5562  }
5563  return -1;
5564  }
5565  } else {
5566  return $reshook;
5567  }
5568  }
5569 
5579  public function indexFile($destfull, $update_main_doc_field)
5580  {
5581  global $conf, $user;
5582 
5583  $upload_dir = dirname($destfull);
5584  $destfile = basename($destfull);
5585  $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
5586 
5587  if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) { // If not a tmp dir
5588  $filename = basename($destfile);
5589  $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
5590  $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
5591 
5592  include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
5593  $ecmfile = new EcmFiles($this->db);
5594  $result = $ecmfile->fetch(0, '', ($rel_dir ? $rel_dir.'/' : '').$filename);
5595 
5596  // Set the public "share" key
5597  $setsharekey = false;
5598  if ($this->element == 'propal' || $this->element == 'proposal') {
5599  if (!isset($conf->global->PROPOSAL_ALLOW_ONLINESIGN) || !empty($conf->global->PROPOSAL_ALLOW_ONLINESIGN)) {
5600  $setsharekey = true; // feature to make online signature is not set or set to on (default)
5601  }
5602  if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) {
5603  $setsharekey = true;
5604  }
5605  }
5606  if ($this->element == 'commande' && !empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD)) {
5607  $setsharekey = true;
5608  }
5609  if ($this->element == 'facture' && !empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD)) {
5610  $setsharekey = true;
5611  }
5612  if ($this->element == 'bank_account' && !empty($conf->global->BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD)) {
5613  $setsharekey = true;
5614  }
5615  if ($this->element == 'contrat' && !empty($conf->global->CONTRACT_ALLOW_EXTERNAL_DOWNLOAD)) {
5616  $setsharekey = true;
5617  }
5618  if ($this->element == 'supplier_proposal' && !empty($conf->global->SUPPLIER_PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) {
5619  $setsharekey = true;
5620  }
5621 
5622  if ($setsharekey) {
5623  if (empty($ecmfile->share)) { // Because object not found or share not set yet
5624  require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
5625  $ecmfile->share = getRandomPassword(true);
5626  }
5627  }
5628 
5629  if ($result > 0) {
5630  $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5631  $ecmfile->fullpath_orig = '';
5632  $ecmfile->gen_or_uploaded = 'generated';
5633  $ecmfile->description = ''; // indexed content
5634  $ecmfile->keywords = ''; // keyword content
5635  $result = $ecmfile->update($user);
5636  if ($result < 0) {
5637  setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5638  return -1;
5639  }
5640  } else {
5641  $ecmfile->entity = $conf->entity;
5642  $ecmfile->filepath = $rel_dir;
5643  $ecmfile->filename = $filename;
5644  $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
5645  $ecmfile->fullpath_orig = '';
5646  $ecmfile->gen_or_uploaded = 'generated';
5647  $ecmfile->description = ''; // indexed content
5648  $ecmfile->keywords = ''; // keyword content
5649  $ecmfile->src_object_type = $this->table_element; // $this->table_name is 'myobject' or 'mymodule_myobject'.
5650  $ecmfile->src_object_id = $this->id;
5651 
5652  $result = $ecmfile->create($user);
5653  if ($result < 0) {
5654  setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
5655  return -1;
5656  }
5657  }
5658 
5659  /*$this->result['fullname']=$destfull;
5660  $this->result['filepath']=$ecmfile->filepath;
5661  $this->result['filename']=$ecmfile->filename;*/
5662  //var_dump($obj->update_main_doc_field);exit;
5663 
5664  if ($update_main_doc_field && !empty($this->table_element)) {
5665  $sql = "UPDATE ".$this->db->prefix().$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath."/".$ecmfile->filename)."'";
5666  $sql .= " WHERE rowid = ".((int) $this->id);
5667 
5668  $resql = $this->db->query($sql);
5669  if (!$resql) {
5670  dol_print_error($this->db);
5671  return -1;
5672  } else {
5673  $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
5674  }
5675  }
5676  }
5677 
5678  return 1;
5679  }
5680 
5688  public function addThumbs($file)
5689  {
5690  require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
5691 
5692  $tmparraysize = getDefaultImageSizes();
5693  $maxwidthsmall = $tmparraysize['maxwidthsmall'];
5694  $maxheightsmall = $tmparraysize['maxheightsmall'];
5695  $maxwidthmini = $tmparraysize['maxwidthmini'];
5696  $maxheightmini = $tmparraysize['maxheightmini'];
5697  //$quality = $tmparraysize['quality'];
5698  $quality = 50; // For thumbs, we force quality to 50
5699 
5700  $file_osencoded = dol_osencode($file);
5701  if (file_exists($file_osencoded)) {
5702  // Create small thumbs for company (Ratio is near 16/9)
5703  // Used on logon for example
5704  vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
5705 
5706  // Create mini thumbs for company (Ratio is near 16/9)
5707  // Used on menu or for setup page for example
5708  vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
5709  }
5710  }
5711 
5712 
5713  /* Functions common to commonobject and commonobjectline */
5714 
5715  /* For default values */
5716 
5729  public function getDefaultCreateValueFor($fieldname, $alternatevalue = null)
5730  {
5731  global $conf, $_POST;
5732 
5733  // If param here has been posted, we use this value first.
5734  if (GETPOSTISSET($fieldname)) {
5735  return GETPOST($fieldname, 'alphanohtml', 3);
5736  }
5737 
5738  if (isset($alternatevalue)) {
5739  return $alternatevalue;
5740  }
5741 
5742  $newelement = $this->element;
5743  if ($newelement == 'facture') {
5744  $newelement = 'invoice';
5745  }
5746  if ($newelement == 'commande') {
5747  $newelement = 'order';
5748  }
5749  if (empty($newelement)) {
5750  dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
5751  return '';
5752  }
5753 
5754  $keyforfieldname = strtoupper($newelement.'_DEFAULT_'.$fieldname);
5755  //var_dump($keyforfieldname);
5756  if (isset($conf->global->$keyforfieldname)) {
5757  return $conf->global->$keyforfieldname;
5758  }
5759 
5760  // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
5761  }
5762 
5763 
5764  /* For triggers */
5765 
5766 
5767  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5778  public function call_trigger($triggerName, $user)
5779  {
5780  // phpcs:enable
5781  global $langs, $conf;
5782  if (!empty(self::TRIGGER_PREFIX) && strpos($triggerName, self::TRIGGER_PREFIX . '_') !== 0) {
5783  dol_print_error('', 'The trigger "' . $triggerName . '" does not start with "' . self::TRIGGER_PREFIX . '_" as required.');
5784  exit;
5785  }
5786  if (!is_object($langs)) { // If lang was not defined, we set it. It is required by run_triggers.
5787  include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
5788  $langs = new Translate('', $conf);
5789  }
5790 
5791  include_once DOL_DOCUMENT_ROOT.'/core/class/interfaces.class.php';
5792  $interface = new Interfaces($this->db);
5793  $result = $interface->run_triggers($triggerName, $this, $user, $langs, $conf);
5794 
5795  if ($result < 0) {
5796  if (!empty($this->errors)) {
5797  $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.
5798  } else {
5799  $this->errors = $interface->errors;
5800  }
5801  }
5802  return $result;
5803  }
5804 
5805 
5806  /* Functions for data in other language */
5807 
5808 
5816  public function fetchValuesForExtraLanguages()
5817  {
5818  // To avoid SQL errors. Probably not the better solution though
5819  if (!$this->element) {
5820  return 0;
5821  }
5822  if (!($this->id > 0)) {
5823  return 0;
5824  }
5825  if (is_array($this->array_languages)) {
5826  return 1;
5827  }
5828 
5829  $this->array_languages = array();
5830 
5831  $element = $this->element;
5832  if ($element == 'categorie') {
5833  $element = 'categories'; // For compatibility
5834  }
5835 
5836  // Request to get translation values for object
5837  $sql = "SELECT rowid, property, lang , value";
5838  $sql .= " FROM ".$this->db->prefix()."object_lang";
5839  $sql .= " WHERE type_object = '".$this->db->escape($element)."'";
5840  $sql .= " AND fk_object = ".((int) $this->id);
5841 
5842  //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
5843  $resql = $this->db->query($sql);
5844  if ($resql) {
5845  $numrows = $this->db->num_rows($resql);
5846  if ($numrows) {
5847  $i = 0;
5848  while ($i < $numrows) {
5849  $obj = $this->db->fetch_object($resql);
5850  $key = $obj->property;
5851  $value = $obj->value;
5852  $codelang = $obj->lang;
5853  $type = $this->fields[$key]['type'];
5854 
5855  // we can add this attribute to object
5856  if (preg_match('/date/', $type)) {
5857  $this->array_languages[$key][$codelang] = $this->db->jdate($value);
5858  } else {
5859  $this->array_languages[$key][$codelang] = $value;
5860  }
5861 
5862  $i++;
5863  }
5864  }
5865 
5866  $this->db->free($resql);
5867 
5868  if ($numrows) {
5869  return $numrows;
5870  } else {
5871  return 0;
5872  }
5873  } else {
5874  dol_print_error($this->db);
5875  return -1;
5876  }
5877  }
5878 
5885  public function setValuesForExtraLanguages($onlykey = '')
5886  {
5887  global $_POST, $langs;
5888 
5889  // Get extra fields
5890  foreach ($_POST as $postfieldkey => $postfieldvalue) {
5891  $tmparray = explode('-', $postfieldkey);
5892  if ($tmparray[0] != 'field') {
5893  continue;
5894  }
5895 
5896  $element = $tmparray[1];
5897  $key = $tmparray[2];
5898  $codelang = $tmparray[3];
5899  //var_dump("postfieldkey=".$postfieldkey." element=".$element." key=".$key." codelang=".$codelang);
5900 
5901  if (!empty($onlykey) && $key != $onlykey) {
5902  continue;
5903  }
5904  if ($element != $this->element) {
5905  continue;
5906  }
5907 
5908  $key_type = $this->fields[$key]['type'];
5909 
5910  $enabled = 1;
5911  if (isset($this->fields[$key]['enabled'])) {
5912  $enabled = dol_eval($this->fields[$key]['enabled'], 1, 1, '1');
5913  }
5914  /*$perms = 1;
5915  if (isset($this->fields[$key]['perms']))
5916  {
5917  $perms = dol_eval($this->fields[$key]['perms'], 1, 1, '1');
5918  }*/
5919  if (empty($enabled)) {
5920  continue;
5921  }
5922  //if (empty($perms)) continue;
5923 
5924  if (in_array($key_type, array('date'))) {
5925  // Clean parameters
5926  // TODO GMT date in memory must be GMT so we should add gm=true in parameters
5927  $value_key = dol_mktime(0, 0, 0, GETPOST($postfieldkey."month", 'int'), GETPOST($postfieldkey."day", 'int'), GETPOST($postfieldkey."year", 'int'));
5928  } elseif (in_array($key_type, array('datetime'))) {
5929  // Clean parameters
5930  // TODO GMT date in memory must be GMT so we should add gm=true in parameters
5931  $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'));
5932  } elseif (in_array($key_type, array('checkbox', 'chkbxlst'))) {
5933  $value_arr = GETPOST($postfieldkey, 'array'); // check if an array
5934  if (!empty($value_arr)) {
5935  $value_key = implode(',', $value_arr);
5936  } else {
5937  $value_key = '';
5938  }
5939  } elseif (in_array($key_type, array('price', 'double'))) {
5940  $value_arr = GETPOST($postfieldkey, 'alpha');
5941  $value_key = price2num($value_arr);
5942  } else {
5943  $value_key = GETPOST($postfieldkey);
5944  if (in_array($key_type, array('link')) && $value_key == '-1') {
5945  $value_key = '';
5946  }
5947  }
5948 
5949  $this->array_languages[$key][$codelang] = $value_key;
5950 
5951  /*if ($nofillrequired) {
5952  $langs->load('errors');
5953  setEventMessages($langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required), null, 'errors');
5954  return -1;
5955  }*/
5956  }
5957 
5958  return 1;
5959  }
5960 
5961 
5962  /* Functions for extrafields */
5963 
5970  public function fetchNoCompute($id)
5971  {
5972  global $conf;
5973 
5974  $savDisableCompute = $conf->disable_compute;
5975  $conf->disable_compute = 1;
5976 
5977  $ret = $this->fetch($id);
5978 
5979  $conf->disable_compute = $savDisableCompute;
5980 
5981  return $ret;
5982  }
5983 
5984  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5994  public function fetch_optionals($rowid = null, $optionsArray = null)
5995  {
5996  // phpcs:enable
5997  global $conf, $extrafields;
5998 
5999  if (empty($rowid)) {
6000  $rowid = $this->id;
6001  }
6002  if (empty($rowid) && isset($this->rowid)) {
6003  $rowid = $this->rowid; // deprecated
6004  }
6005 
6006  // To avoid SQL errors. Probably not the better solution though
6007  if (!$this->table_element) {
6008  return 0;
6009  }
6010 
6011  $this->array_options = array();
6012 
6013  if (!is_array($optionsArray)) {
6014  // If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
6015  if (!isset($extrafields) || !is_object($extrafields)) {
6016  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6017  $extrafields = new ExtraFields($this->db);
6018  }
6019 
6020  // Load array of extrafields for elementype = $this->table_element
6021  if (empty($extrafields->attributes[$this->table_element]['loaded'])) {
6022  $extrafields->fetch_name_optionals_label($this->table_element);
6023  }
6024  $optionsArray = (!empty($extrafields->attributes[$this->table_element]['label']) ? $extrafields->attributes[$this->table_element]['label'] : null);
6025  } else {
6026  global $extrafields;
6027  dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
6028  }
6029 
6030  $table_element = $this->table_element;
6031  if ($table_element == 'categorie') {
6032  $table_element = 'categories'; // For compatibility
6033  }
6034 
6035  // Request to get complementary values
6036  if (is_array($optionsArray) && count($optionsArray) > 0) {
6037  $sql = "SELECT rowid";
6038  foreach ($optionsArray as $name => $label) {
6039  if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate') {
6040  $sql .= ", ".$name;
6041  }
6042  }
6043  $sql .= " FROM ".$this->db->prefix().$table_element."_extrafields";
6044  $sql .= " WHERE fk_object = ".((int) $rowid);
6045 
6046  //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
6047  $resql = $this->db->query($sql);
6048  if ($resql) {
6049  $numrows = $this->db->num_rows($resql);
6050  if ($numrows) {
6051  $tab = $this->db->fetch_array($resql);
6052 
6053  foreach ($tab as $key => $value) {
6054  // 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)
6055  if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && !is_int($key)) {
6056  // we can add this attribute to object
6057  if (!empty($extrafields->attributes[$this->table_element]) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date', 'datetime'))) {
6058  //var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
6059  $this->array_options["options_".$key] = $this->db->jdate($value);
6060  } else {
6061  $this->array_options["options_".$key] = $value;
6062  }
6063 
6064  //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
6065  }
6066  }
6067  }
6068 
6069  // If field is a computed field, value must become result of compute (regardless of whether a row exists
6070  // in the element's extrafields table)
6071  foreach ($extrafields->attributes[$this->table_element]['label'] as $key => $val) {
6072  if (!empty($extrafields->attributes[$this->table_element]) && !empty($extrafields->attributes[$this->table_element]['computed'][$key])) {
6073  //var_dump($conf->disable_compute);
6074  if (empty($conf->disable_compute)) {
6075  global $objectoffield; // We set a global variable to $objectoffield so
6076  $objectoffield = $this; // we can use it inside computed formula
6077  $this->array_options["options_".$key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0, '');
6078  }
6079  }
6080  }
6081 
6082  $this->db->free($resql);
6083 
6084  if ($numrows) {
6085  return $numrows;
6086  } else {
6087  return 0;
6088  }
6089  } else {
6090  $this->errors[]=$this->db->lasterror;
6091  return -1;
6092  }
6093  }
6094  return 0;
6095  }
6096 
6103  public function deleteExtraFields()
6104  {
6105  global $conf;
6106 
6107  if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) {
6108  return 0;
6109  }
6110 
6111  $this->db->begin();
6112 
6113  $table_element = $this->table_element;
6114  if ($table_element == 'categorie') {
6115  $table_element = 'categories'; // For compatibility
6116  }
6117 
6118  dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
6119 
6120  $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6121 
6122  $resql = $this->db->query($sql_del);
6123  if (!$resql) {
6124  $this->error = $this->db->lasterror();
6125  $this->db->rollback();
6126  return -1;
6127  } else {
6128  $this->db->commit();
6129  return 1;
6130  }
6131  }
6132 
6143  public function insertExtraFields($trigger = '', $userused = null)
6144  {
6145  global $conf, $langs, $user;
6146 
6147  if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) {
6148  return 0;
6149  }
6150 
6151  if (empty($userused)) {
6152  $userused = $user;
6153  }
6154 
6155  $error = 0;
6156 
6157  if (!empty($this->array_options)) {
6158  // Check parameters
6159  $langs->load('admin');
6160  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6161  $extrafields = new ExtraFields($this->db);
6162  $target_extrafields = $extrafields->fetch_name_optionals_label($this->table_element);
6163 
6164  // Eliminate copied source object extra fields that do not exist in target object
6165  $new_array_options = array();
6166  foreach ($this->array_options as $key => $value) {
6167  if (in_array(substr($key, 8), array_keys($target_extrafields))) { // We remove the 'options_' from $key for test
6168  $new_array_options[$key] = $value;
6169  } elseif (in_array($key, array_keys($target_extrafields))) { // We test on $key that does not contains the 'options_' prefix
6170  $new_array_options['options_'.$key] = $value;
6171  }
6172  }
6173 
6174  foreach ($new_array_options as $key => $value) {
6175  $attributeKey = substr($key, 8); // Remove 'options_' prefix
6176  $attributeType = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
6177  $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$attributeKey];
6178  $attributeParam = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
6179  $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
6180  $attributeUnique = $extrafields->attributes[$this->table_element]['unique'][$attributeKey];
6181  $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$attributeKey];
6182 
6183  // If we clone, we have to clean unique extrafields to prevent duplicates.
6184  // This behaviour can be prevented by external code by changing $this->context['createfromclone'] value in createFrom hook
6185  if (! empty($this->context['createfromclone']) && $this->context['createfromclone'] == 'createfromclone' && ! empty($attributeUnique)) {
6186  $new_array_options[$key] = null;
6187  }
6188 
6189  // Similar code than into insertExtraFields
6190  if ($attributeRequired) {
6191  $mandatorypb = false;
6192  if ($attributeType == 'link' && $this->array_options[$key] == '-1') {
6193  $mandatorypb = true;
6194  }
6195  if ($this->array_options[$key] === '') {
6196  $mandatorypb = true;
6197  }
6198  if ($attributeType == 'sellist' && $this->array_options[$key] == '0') {
6199  $mandatorypb = true;
6200  }
6201  if ($mandatorypb) {
6202  $langs->load("errors");
6203  dol_syslog("Mandatory field '".$key."' is empty during create and set to required into definition of extrafields");
6204  $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6205  return -1;
6206  }
6207  }
6208 
6209  //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6210  //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6211 
6212  if (!empty($attrfieldcomputed)) {
6213  if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS)) {
6214  $value = dol_eval($attrfieldcomputed, 1, 0, '');
6215  dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
6216  $new_array_options[$key] = $value;
6217  } else {
6218  $new_array_options[$key] = null;
6219  }
6220  }
6221 
6222  switch ($attributeType) {
6223  case 'int':
6224  if (!is_numeric($value) && $value != '') {
6225  $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6226  return -1;
6227  } elseif ($value == '') {
6228  $new_array_options[$key] = null;
6229  }
6230  break;
6231  case 'price':
6232  case 'double':
6233  $value = price2num($value);
6234  if (!is_numeric($value) && $value != '') {
6235  dol_syslog($langs->trans("ExtraFieldHasWrongValue")." for ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6236  $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6237  return -1;
6238  } elseif ($value == '') {
6239  $value = null;
6240  }
6241  //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6242  $new_array_options[$key] = $value;
6243  break;
6244  /*case 'select': // Not required, we chosed value='0' for undefined values
6245  if ($value=='-1')
6246  {
6247  $this->array_options[$key] = null;
6248  }
6249  break;*/
6250  case 'password':
6251  $algo = '';
6252  if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options'])) {
6253  // If there is an encryption choice, we use it to crypt data before insert
6254  $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
6255  $algo = reset($tmparrays);
6256  if ($algo != '') {
6257  //global $action; // $action may be 'create', 'update', 'update_extras'...
6258  //var_dump($action);
6259  //var_dump($this->oldcopy);exit;
6260  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
6261  //var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]);
6262  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.
6263  $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6264  } else {
6265  // var_dump($algo);
6266  $newvalue = dol_hash($this->array_options[$key], $algo);
6267  $new_array_options[$key] = $newvalue;
6268  }
6269  } else {
6270  $new_array_options[$key] = $this->array_options[$key]; // Value is kept
6271  }
6272  }
6273  } else // Common usage
6274  {
6275  $new_array_options[$key] = $this->array_options[$key];
6276  }
6277  break;
6278  case 'date':
6279  case 'datetime':
6280  // If data is a string instead of a timestamp, we convert it
6281  if (!is_int($this->array_options[$key])) {
6282  $this->array_options[$key] = strtotime($this->array_options[$key]);
6283  }
6284  $new_array_options[$key] = $this->db->idate($this->array_options[$key]);
6285  break;
6286  case 'link':
6287  $param_list = array_keys($attributeParam['options']);
6288  // 0 : ObjectName
6289  // 1 : classPath
6290  $InfoFieldList = explode(":", $param_list[0]);
6291  dol_include_once($InfoFieldList[1]);
6292  if ($InfoFieldList[0] && class_exists($InfoFieldList[0])) {
6293  if ($value == '-1') { // -1 is key for no defined in combo list of objects
6294  $new_array_options[$key] = '';
6295  } elseif ($value) {
6296  $object = new $InfoFieldList[0]($this->db);
6297  if (is_numeric($value)) {
6298  $res = $object->fetch($value); // Common case
6299  } else {
6300  $res = $object->fetch('', $value); // For compatibility
6301  }
6302 
6303  if ($res > 0) {
6304  $new_array_options[$key] = $object->id;
6305  } else {
6306  $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
6307  return -1;
6308  }
6309  }
6310  } else {
6311  dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6312  }
6313  break;
6314  }
6315  }
6316 
6317  $this->db->begin();
6318 
6319  $table_element = $this->table_element;
6320  if ($table_element == 'categorie') {
6321  $table_element = 'categories'; // For compatibility
6322  }
6323 
6324  dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG);
6325 
6326  $sql_del = "DELETE FROM ".$this->db->prefix().$table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6327  $this->db->query($sql_del);
6328 
6329  $sql = "INSERT INTO ".$this->db->prefix().$table_element."_extrafields (fk_object";
6330  foreach ($new_array_options as $key => $value) {
6331  $attributeKey = substr($key, 8); // Remove 'options_' prefix
6332  // Add field of attribut
6333  if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator
6334  $sql .= ",".$attributeKey;
6335  }
6336  }
6337  // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6338  if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6339  foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6340  if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6341  $sql .= ",".$tmpkey;
6342  }
6343  }
6344  }
6345  $sql .= ") VALUES (".$this->id;
6346 
6347  foreach ($new_array_options as $key => $value) {
6348  $attributeKey = substr($key, 8); // Remove 'options_' prefix
6349  // Add field of attribute
6350  if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator)
6351  if ($new_array_options[$key] != '' || $new_array_options[$key] == '0') {
6352  $sql .= ",'".$this->db->escape($new_array_options[$key])."'";
6353  } else {
6354  $sql .= ",null";
6355  }
6356  }
6357  }
6358  // We must insert a default value for fields for other entities that are mandatory to avoid not null error
6359  if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
6360  foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
6361  if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
6362  if (in_array($tmpval, array('int', 'double', 'price'))) {
6363  $sql .= ", 0";
6364  } else {
6365  $sql .= ", ''";
6366  }
6367  }
6368  }
6369  }
6370 
6371  $sql .= ")";
6372 
6373  $resql = $this->db->query($sql);
6374  if (!$resql) {
6375  $this->error = $this->db->lasterror();
6376  $error++;
6377  }
6378 
6379  if (!$error && $trigger) {
6380  // Call trigger
6381  $this->context = array('extrafieldaddupdate'=>1);
6382  $result = $this->call_trigger($trigger, $userused);
6383  if ($result < 0) {
6384  $error++;
6385  }
6386  // End call trigger
6387  }
6388 
6389  if ($error) {
6390  $this->db->rollback();
6391  return -1;
6392  } else {
6393  $this->db->commit();
6394  return 1;
6395  }
6396  } else {
6397  return 0;
6398  }
6399  }
6400 
6411  public function insertExtraLanguages($trigger = '', $userused = null)
6412  {
6413  global $conf, $langs, $user;
6414 
6415  if (empty($userused)) {
6416  $userused = $user;
6417  }
6418 
6419  $error = 0;
6420 
6421  if (!empty($conf->global->MAIN_EXTRALANGUAGES_DISABLED)) {
6422  return 0; // For avoid conflicts if trigger used
6423  }
6424 
6425  if (is_array($this->array_languages)) {
6426  $new_array_languages = $this->array_languages;
6427 
6428  foreach ($new_array_languages as $key => $value) {
6429  $attributeKey = $key;
6430  $attributeType = $this->fields[$attributeKey]['type'];
6431  $attributeLabel = $this->fields[$attributeKey]['label'];
6432 
6433  //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6434  //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6435 
6436  switch ($attributeType) {
6437  case 'int':
6438  if (!is_numeric($value) && $value != '') {
6439  $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6440  return -1;
6441  } elseif ($value == '') {
6442  $new_array_languages[$key] = null;
6443  }
6444  break;
6445  case 'double':
6446  $value = price2num($value);
6447  if (!is_numeric($value) && $value != '') {
6448  dol_syslog($langs->trans("ExtraLanguageHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6449  $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
6450  return -1;
6451  } elseif ($value == '') {
6452  $new_array_languages[$key] = null;
6453  }
6454  //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6455  $new_array_languages[$key] = $value;
6456  break;
6457  /*case 'select': // Not required, we chosed value='0' for undefined values
6458  if ($value=='-1')
6459  {
6460  $this->array_options[$key] = null;
6461  }
6462  break;*/
6463  }
6464  }
6465 
6466  $this->db->begin();
6467 
6468  $table_element = $this->table_element;
6469  if ($table_element == 'categorie') {
6470  $table_element = 'categories'; // For compatibility
6471  }
6472 
6473  dol_syslog(get_class($this)."::insertExtraLanguages delete then insert", LOG_DEBUG);
6474 
6475  foreach ($new_array_languages as $key => $langcodearray) { // $key = 'name', 'town', ...
6476  foreach ($langcodearray as $langcode => $value) {
6477  $sql_del = "DELETE FROM ".$this->db->prefix()."object_lang";
6478  $sql_del .= " WHERE fk_object = ".((int) $this->id)." AND property = '".$this->db->escape($key)."' AND type_object = '".$this->db->escape($table_element)."'";
6479  $sql_del .= " AND lang = '".$this->db->escape($langcode)."'";
6480  $this->db->query($sql_del);
6481 
6482  if ($value !== '') {
6483  $sql = "INSERT INTO ".$this->db->prefix()."object_lang (fk_object, property, type_object, lang, value";
6484  $sql .= ") VALUES (".$this->id.", '".$this->db->escape($key)."', '".$this->db->escape($table_element)."', '".$this->db->escape($langcode)."', '".$this->db->escape($value)."'";
6485  $sql .= ")";
6486 
6487  $resql = $this->db->query($sql);
6488  if (!$resql) {
6489  $this->error = $this->db->lasterror();
6490  $error++;
6491  break;
6492  }
6493  }
6494  }
6495  }
6496 
6497  if (!$error && $trigger) {
6498  // Call trigger
6499  $this->context = array('extralanguagesaddupdate'=>1);
6500  $result = $this->call_trigger($trigger, $userused);
6501  if ($result < 0) {
6502  $error++;
6503  }
6504  // End call trigger
6505  }
6506 
6507  if ($error) {
6508  $this->db->rollback();
6509  return -1;
6510  } else {
6511  $this->db->commit();
6512  return 1;
6513  }
6514  } else {
6515  return 0;
6516  }
6517  }
6518 
6529  public function updateExtraField($key, $trigger = null, $userused = null)
6530  {
6531  global $conf, $langs, $user;
6532 
6533  if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) {
6534  return 0;
6535  }
6536 
6537  if (empty($userused)) {
6538  $userused = $user;
6539  }
6540 
6541  $error = 0;
6542 
6543  if (!empty($this->array_options) && isset($this->array_options["options_".$key])) {
6544  // Check parameters
6545  $langs->load('admin');
6546  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
6547  $extrafields = new ExtraFields($this->db);
6548  $extrafields->fetch_name_optionals_label($this->table_element);
6549 
6550  $value = $this->array_options["options_".$key];
6551 
6552  $attributeType = $extrafields->attributes[$this->table_element]['type'][$key];
6553  $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key];
6554  $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key];
6555  $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
6556  $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$key];
6557 
6558  // Similar code than into insertExtraFields
6559  if ($attributeRequired) {
6560  $mandatorypb = false;
6561  if ($attributeType == 'link' && $this->array_options["options_".$key] == '-1') {
6562  $mandatorypb = true;
6563  }
6564  if ($this->array_options["options_".$key] === '') {
6565  $mandatorypb = true;
6566  }
6567  if ($mandatorypb) {
6568  $langs->load("errors");
6569  dol_syslog("Mandatory field 'options_".$key."' is empty during update and set to required into definition of extrafields");
6570  $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
6571  return -1;
6572  }
6573  }
6574 
6575  //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
6576  //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
6577 
6578  if (!empty($attrfieldcomputed)) {
6579  if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS)) {
6580  $value = dol_eval($attrfieldcomputed, 1, 0, '');
6581  dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
6582  $this->array_options["options_".$key] = $value;
6583  } else {
6584  $this->array_options["options_".$key] = null;
6585  }
6586  }
6587 
6588  switch ($attributeType) {
6589  case 'int':
6590  if (!is_numeric($value) && $value != '') {
6591  $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6592  return -1;
6593  } elseif ($value === '') {
6594  $this->array_options["options_".$key] = null;
6595  }
6596  break;
6597  case 'double':
6598  $value = price2num($value);
6599  if (!is_numeric($value) && $value != '') {
6600  dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
6601  $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
6602  return -1;
6603  } elseif ($value === '') {
6604  $value = null;
6605  }
6606  //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
6607  $this->array_options["options_".$key] = $value;
6608  break;
6609  /*case 'select': // Not required, we chosed value='0' for undefined values
6610  if ($value=='-1')
6611  {
6612  $this->array_options[$key] = null;
6613  }
6614  break;*/
6615  case 'price':
6616  $this->array_options["options_".$key] = price2num($this->array_options["options_".$key]);
6617  break;
6618  case 'date':
6619  case 'datetime':
6620  if (empty($this->array_options["options_".$key])) {
6621  $this->array_options["options_".$key] = null;
6622  } else {
6623  $this->array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key]);
6624  }
6625  break;
6626  case 'boolean':
6627  if (empty($this->array_options["options_".$key])) {
6628  $this->array_options["options_".$key] = null;
6629  }
6630  break;
6631  /*
6632  case 'link':
6633  $param_list = array_keys($attributeParam['options']);
6634  // 0 : ObjectName
6635  // 1 : classPath
6636  $InfoFieldList = explode(":", $param_list[0]);
6637  dol_include_once($InfoFieldList[1]);
6638  if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
6639  {
6640  if ($value == '-1') // -1 is key for no defined in combo list of objects
6641  {
6642  $new_array_options[$key] = '';
6643  } elseif ($value) {
6644  $object = new $InfoFieldList[0]($this->db);
6645  if (is_numeric($value)) $res = $object->fetch($value); // Common case
6646  else $res = $object->fetch('', $value); // For compatibility
6647 
6648  if ($res > 0) $new_array_options[$key] = $object->id;
6649  else {
6650  $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
6651  $this->db->rollback();
6652  return -1;
6653  }
6654  }
6655  } else {
6656  dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6657  }
6658  break;
6659  */
6660  }
6661 
6662  $this->db->begin();
6663 
6664  $linealreadyfound = 0;
6665 
6666  // 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)
6667  $sql = "SELECT COUNT(rowid) as nb FROM ".$this->db->prefix().$this->table_element."_extrafields WHERE fk_object = ".((int) $this->id);
6668  $resql = $this->db->query($sql);
6669  if ($resql) {
6670  $tmpobj = $this->db->fetch_object($resql);
6671  if ($tmpobj) {
6672  $linealreadyfound = $tmpobj->nb;
6673  }
6674  }
6675 
6676  if ($linealreadyfound) {
6677  if ($this->array_options["options_".$key] === null) {
6678  $sql = "UPDATE ".$this->db->prefix().$this->table_element."_extrafields SET ".$key." = null";
6679  } else {
6680  $sql = "UPDATE ".$this->db->prefix().$this->table_element."_extrafields SET ".$key." = '".$this->db->escape($this->array_options["options_".$key])."'";
6681  }
6682  $sql .= " WHERE fk_object = ".((int) $this->id);
6683  } else {
6684  $result = $this->insertExtraFields('', $user);
6685  if ($result < 0) {
6686  $error++;
6687  }
6688  }
6689 
6690  $resql = $this->db->query($sql);
6691  if (!$resql) {
6692  $error++;
6693  $this->error = $this->db->lasterror();
6694  }
6695  if (!$error && $trigger) {
6696  // Call trigger
6697  $this->context = array('extrafieldupdate'=>1);
6698  $result = $this->call_trigger($trigger, $userused);
6699  if ($result < 0) {
6700  $error++;
6701  }
6702  // End call trigger
6703  }
6704 
6705  if ($error) {
6706  dol_syslog(__METHOD__.$this->error, LOG_ERR);
6707  $this->db->rollback();
6708  return -1;
6709  } else {
6710  $this->db->commit();
6711  return 1;
6712  }
6713  } else {
6714  return 0;
6715  }
6716  }
6717 
6728  public function updateExtraLanguages($key, $trigger = null, $userused = null)
6729  {
6730  global $conf, $langs, $user;
6731 
6732  if (empty($userused)) {
6733  $userused = $user;
6734  }
6735 
6736  $error = 0;
6737 
6738  if (!empty($conf->global->MAIN_EXTRALANGUAGES_DISABLED)) {
6739  return 0; // For avoid conflicts if trigger used
6740  }
6741 
6742  return 0;
6743  }
6744 
6745 
6760  public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0, $nonewbutton = 0)
6761  {
6762  global $conf, $langs, $form;
6763 
6764  if (!is_object($form)) {
6765  require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
6766  $form = new Form($this->db);
6767  }
6768 
6769  if (!empty($this->fields)) {
6770  $val = $this->fields[$key];
6771  }
6772 
6773  // Validation tests and output
6774  $fieldValidationErrorMsg = '';
6775  $validationClass = '';
6776  $fieldValidationErrorMsg = $this->getFieldError($key);
6777  if (!empty($fieldValidationErrorMsg)) {
6778  $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
6779  } else {
6780  $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
6781  }
6782 
6783  $out = '';
6784  $type = '';
6785  $isDependList = 0;
6786  $param = array();
6787  $param['options'] = array();
6788  $reg = array();
6789  $size = !empty($this->fields[$key]['size']) ? $this->fields[$key]['size'] : 0;
6790  // Because we work on extrafields
6791  if (preg_match('/^(integer|link):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6792  $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
6793  $type = 'link';
6794  } elseif (preg_match('/^(integer|link):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6795  $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
6796  $type = 'link';
6797  } elseif (preg_match('/^(integer|link):(.*):(.*)/i', $val['type'], $reg)) {
6798  $param['options'] = array($reg[2].':'.$reg[3] => 'N');
6799  $type = 'link';
6800  } elseif (preg_match('/^(sellist):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6801  $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
6802  $type = 'sellist';
6803  } elseif (preg_match('/^(sellist):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6804  $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
6805  $type = 'sellist';
6806  } elseif (preg_match('/^(sellist):(.*):(.*)/i', $val['type'], $reg)) {
6807  $param['options'] = array($reg[2].':'.$reg[3] => 'N');
6808  $type = 'sellist';
6809  } elseif (preg_match('/varchar\((\d+)\)/', $val['type'], $reg)) {
6810  $param['options'] = array();
6811  $type = 'varchar';
6812  $size = $reg[1];
6813  } elseif (preg_match('/varchar/', $val['type'])) {
6814  $param['options'] = array();
6815  $type = 'varchar';
6816  } else {
6817  $param['options'] = array();
6818  $type = $this->fields[$key]['type'];
6819  }
6820 
6821  // Special case that force options and type ($type can be integer, varchar, ...)
6822  if (!empty($this->fields[$key]['arrayofkeyval']) && is_array($this->fields[$key]['arrayofkeyval'])) {
6823  $param['options'] = $this->fields[$key]['arrayofkeyval'];
6824  $type = 'select';
6825  }
6826 
6827  $label = $this->fields[$key]['label'];
6828  //$elementtype=$this->fields[$key]['elementtype']; // Seems not used
6829  $default = (!empty($this->fields[$key]['default']) ? $this->fields[$key]['default'] : '');
6830  $computed = (!empty($this->fields[$key]['computed']) ? $this->fields[$key]['computed'] : '');
6831  $unique = (!empty($this->fields[$key]['unique']) ? $this->fields[$key]['unique'] : 0);
6832  $required = (!empty($this->fields[$key]['required']) ? $this->fields[$key]['required'] : 0);
6833  $autofocusoncreate = (!empty($this->fields[$key]['autofocusoncreate']) ? $this->fields[$key]['autofocusoncreate'] : 0);
6834 
6835  $langfile = (!empty($this->fields[$key]['langfile']) ? $this->fields[$key]['langfile'] : '');
6836  $list = (!empty($this->fields[$key]['list']) ? $this->fields[$key]['list'] : 0);
6837  $hidden = (in_array(abs($this->fields[$key]['visible']), array(0, 2)) ? 1 : 0);
6838 
6839  $objectid = $this->id;
6840 
6841  if ($computed) {
6842  if (!preg_match('/^search_/', $keyprefix)) {
6843  return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
6844  } else {
6845  return '';
6846  }
6847  }
6848 
6849  // Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine
6850  if (empty($morecss) && !empty($val['css'])) {
6851  $morecss = $val['css'];
6852  } elseif (empty($morecss)) {
6853  if ($type == 'date') {
6854  $morecss = 'minwidth100imp';
6855  } elseif ($type == 'datetime' || $type == 'link') { // link means an foreign key to another primary id
6856  $morecss = 'minwidth200imp';
6857  } elseif (in_array($type, array('int', 'integer', 'price')) || preg_match('/^double(\([0-9],[0-9]\)){0,1}/', $type)) {
6858  $morecss = 'maxwidth75';
6859  } elseif ($type == 'url') {
6860  $morecss = 'minwidth400';
6861  } elseif ($type == 'boolean') {
6862  $morecss = '';
6863  } else {
6864  if (round($size) < 12) {
6865  $morecss = 'minwidth100';
6866  } elseif (round($size) <= 48) {
6867  $morecss = 'minwidth200';
6868  } else {
6869  $morecss = 'minwidth400';
6870  }
6871  }
6872  }
6873 
6874  // Add validation state class
6875  if (!empty($validationClass)) {
6876  $morecss.= $validationClass;
6877  }
6878 
6879  if (in_array($type, array('date'))) {
6880  $tmp = explode(',', $size);
6881  $newsize = $tmp[0];
6882  $showtime = 0;
6883 
6884  // Do not show current date when field not required (see selectDate() method)
6885  if (!$required && $value == '') {
6886  $value = '-1';
6887  }
6888 
6889  // TODO Must also support $moreparam
6890  $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
6891  } elseif (in_array($type, array('datetime'))) {
6892  $tmp = explode(',', $size);
6893  $newsize = $tmp[0];
6894  $showtime = 1;
6895 
6896  // Do not show current date when field not required (see selectDate() method)
6897  if (!$required && $value == '') $value = '-1';
6898 
6899  // TODO Must also support $moreparam
6900  $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
6901  } elseif (in_array($type, array('duration'))) {
6902  $out = $form->select_duration($keyprefix.$key.$keysuffix, $value, 0, 'text', 0, 1);
6903  } elseif (in_array($type, array('int', 'integer'))) {
6904  $tmp = explode(',', $size);
6905  $newsize = $tmp[0];
6906  $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' : '').'>';
6907  } elseif (in_array($type, array('real'))) {
6908  $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
6909  } elseif (preg_match('/varchar/', $type)) {
6910  $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' : '').'>';
6911  } elseif (in_array($type, array('email', 'mail', 'phone', 'url'))) {
6912  $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
6913  } elseif (preg_match('/^text/', $type)) {
6914  if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
6915  require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
6916  $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
6917  $out = $doleditor->Create(1);
6918  } else {
6919  $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
6920  }
6921  } elseif (preg_match('/^html/', $type)) {
6922  if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
6923  require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
6924  $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, !empty($conf->fckeditor->enabled) && $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_5, '90%');
6925  $out = $doleditor->Create(1, '', true, '', '', $moreparam, $morecss);
6926  } else {
6927  $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
6928  }
6929  } elseif ($type == 'boolean') {
6930  $checked = '';
6931  if (!empty($value)) {
6932  $checked = ' checked value="1" ';
6933  } else {
6934  $checked = ' value="1" ';
6935  }
6936  $out = '<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
6937  } elseif ($type == 'price') {
6938  if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
6939  $value = price($value);
6940  }
6941  $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '.$langs->getCurrencySymbol($conf->currency);
6942  } elseif (preg_match('/^double(\([0-9],[0-9]\)){0,1}/', $type)) {
6943  if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
6944  $value = price($value);
6945  }
6946  $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
6947  } elseif ($type == 'select') {
6948  $out = '';
6949  if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_EXTRAFIELDS_DISABLE_SELECT2)) {
6950  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
6951  $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
6952  }
6953 
6954  $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
6955  if ((!isset($this->fields[$key]['default'])) || ($this->fields[$key]['notnull'] != 1)) {
6956  $out .= '<option value="0">&nbsp;</option>';
6957  }
6958  foreach ($param['options'] as $keyb => $valb) {
6959  if ((string) $keyb == '') {
6960  continue;
6961  }
6962  if (strpos($valb, "|") !== false) {
6963  list($valb, $parent) = explode('|', $valb);
6964  }
6965  $out .= '<option value="'.$keyb.'"';
6966  $out .= (((string) $value == (string) $keyb) ? ' selected' : '');
6967  $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
6968  $out .= '>'.$valb.'</option>';
6969  }
6970  $out .= '</select>';
6971  } elseif ($type == 'sellist') {
6972  $out = '';
6973  if (!empty($conf->use_javascript_ajax) && empty($conf->global->MAIN_EXTRAFIELDS_DISABLE_SELECT2)) {
6974  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
6975  $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
6976  }
6977 
6978  $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
6979  if (is_array($param['options'])) {
6980  $param_list = array_keys($param['options']);
6981  $InfoFieldList = explode(":", $param_list[0]);
6982  $parentName = '';
6983  $parentField = '';
6984  // 0 : tableName
6985  // 1 : label field name
6986  // 2 : key fields name (if differ of rowid)
6987  // 3 : key field parent (for dependent lists)
6988  // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
6989  $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
6990 
6991  if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
6992  if (strpos($InfoFieldList[4], 'extra.') !== false) {
6993  $keyList = 'main.'.$InfoFieldList[2].' as rowid';
6994  } else {
6995  $keyList = $InfoFieldList[2].' as rowid';
6996  }
6997  }
6998  if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
6999  list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7000  $keyList .= ', '.$parentField;
7001  }
7002 
7003  $fields_label = explode('|', $InfoFieldList[1]);
7004  if (is_array($fields_label)) {
7005  $keyList .= ', ';
7006  $keyList .= implode(', ', $fields_label);
7007  }
7008 
7009  $sqlwhere = '';
7010  $sql = "SELECT ".$keyList;
7011  $sql .= " FROM ".$this->db->prefix().$InfoFieldList[0];
7012  if (!empty($InfoFieldList[4])) {
7013  // can use SELECT request
7014  if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7015  $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7016  }
7017 
7018  // current object id can be use into filter
7019  if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7020  $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
7021  } else {
7022  $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7023  }
7024 
7025  //We have to join on extrafield table
7026  if (strpos($InfoFieldList[4], 'extra') !== false) {
7027  $sql .= " as main, ".$this->db->prefix().$InfoFieldList[0]."_extrafields as extra";
7028  $sqlwhere .= " WHERE extra.fk_object=main.".$InfoFieldList[2]." AND ".$InfoFieldList[4];
7029  } else {
7030  $sqlwhere .= " WHERE ".$InfoFieldList[4];
7031  }
7032  } else {
7033  $sqlwhere .= ' WHERE 1=1';
7034  }
7035  // Some tables may have field, some other not. For the moment we disable it.
7036  if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7037  $sqlwhere .= " AND entity = ".((int) $conf->entity);
7038  }
7039  $sql .= $sqlwhere;
7040  //print $sql;
7041 
7042  $sql .= ' ORDER BY '.implode(', ', $fields_label);
7043 
7044  dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
7045  $resql = $this->db->query($sql);
7046  if ($resql) {
7047  $out .= '<option value="0">&nbsp;</option>';
7048  $num = $this->db->num_rows($resql);
7049  $i = 0;
7050  while ($i < $num) {
7051  $labeltoshow = '';
7052  $obj = $this->db->fetch_object($resql);
7053 
7054  // Several field into label (eq table:code|libelle:rowid)
7055  $notrans = false;
7056  $fields_label = explode('|', $InfoFieldList[1]);
7057  if (count($fields_label) > 1) {
7058  $notrans = true;
7059  foreach ($fields_label as $field_toshow) {
7060  $labeltoshow .= $obj->$field_toshow.' ';
7061  }
7062  } else {
7063  $labeltoshow = $obj->{$InfoFieldList[1]};
7064  }
7065  $labeltoshow = dol_trunc($labeltoshow, 45);
7066 
7067  if ($value == $obj->rowid) {
7068  foreach ($fields_label as $field_toshow) {
7069  $translabel = $langs->trans($obj->$field_toshow);
7070  if ($translabel != $obj->$field_toshow) {
7071  $labeltoshow = dol_trunc($translabel).' ';
7072  } else {
7073  $labeltoshow = dol_trunc($obj->$field_toshow).' ';
7074  }
7075  }
7076  $out .= '<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
7077  } else {
7078  if (!$notrans) {
7079  $translabel = $langs->trans($obj->{$InfoFieldList[1]});
7080  if ($translabel != $obj->{$InfoFieldList[1]}) {
7081  $labeltoshow = dol_trunc($translabel, 18);
7082  } else {
7083  $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]});
7084  }
7085  }
7086  if (empty($labeltoshow)) {
7087  $labeltoshow = '(not defined)';
7088  }
7089  if ($value == $obj->rowid) {
7090  $out .= '<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
7091  }
7092 
7093  if (!empty($InfoFieldList[3]) && $parentField) {
7094  $parent = $parentName.':'.$obj->{$parentField};
7095  $isDependList = 1;
7096  }
7097 
7098  $out .= '<option value="'.$obj->rowid.'"';
7099  $out .= ($value == $obj->rowid ? ' selected' : '');
7100  $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
7101  $out .= '>'.$labeltoshow.'</option>';
7102  }
7103 
7104  $i++;
7105  }
7106  $this->db->free($resql);
7107  } else {
7108  print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
7109  }
7110  }
7111  $out .= '</select>';
7112  } elseif ($type == 'checkbox') {
7113  $value_arr = explode(',', $value);
7114  $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ?null:$param['options']), $value_arr, '', 0, $morecss, 0, '100%');
7115  } elseif ($type == 'radio') {
7116  $out = '';
7117  foreach ($param['options'] as $keyopt => $valopt) {
7118  $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
7119  $out .= ' value="'.$keyopt.'"';
7120  $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
7121  $out .= ($value == $keyopt ? 'checked' : '');
7122  $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$valopt.'</label><br>';
7123  }
7124  } elseif ($type == 'chkbxlst') {
7125  if (is_array($value)) {
7126  $value_arr = $value;
7127  } else {
7128  $value_arr = explode(',', $value);
7129  }
7130 
7131  if (is_array($param['options'])) {
7132  $param_list = array_keys($param['options']);
7133  $InfoFieldList = explode(":", $param_list[0]);
7134  $parentName = '';
7135  $parentField = '';
7136  // 0 : tableName
7137  // 1 : label field name
7138  // 2 : key fields name (if differ of rowid)
7139  // 3 : key field parent (for dependent lists)
7140  // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
7141  $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
7142 
7143  if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
7144  list ($parentName, $parentField) = explode('|', $InfoFieldList[3]);
7145  $keyList .= ', '.$parentField;
7146  }
7147  if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
7148  if (strpos($InfoFieldList[4], 'extra.') !== false) {
7149  $keyList = 'main.'.$InfoFieldList[2].' as rowid';
7150  } else {
7151  $keyList = $InfoFieldList[2].' as rowid';
7152  }
7153  }
7154 
7155  $fields_label = explode('|', $InfoFieldList[1]);
7156  if (is_array($fields_label)) {
7157  $keyList .= ', ';
7158  $keyList .= implode(', ', $fields_label);
7159  }
7160 
7161  $sqlwhere = '';
7162  $sql = "SELECT ".$keyList;
7163  $sql .= ' FROM '.$this->db->prefix().$InfoFieldList[0];
7164  if (!empty($InfoFieldList[4])) {
7165  // can use SELECT request
7166  if (strpos($InfoFieldList[4], '$SEL$') !== false) {
7167  $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
7168  }
7169 
7170  // current object id can be use into filter
7171  if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
7172  $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
7173  } else {
7174  $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
7175  }
7176 
7177  // We have to join on extrafield table
7178  if (strpos($InfoFieldList[4], 'extra') !== false) {
7179  $sql .= ' as main, '.$this->db->prefix().$InfoFieldList[0].'_extrafields as extra';
7180  $sqlwhere .= " WHERE extra.fk_object=main.".$InfoFieldList[2]." AND ".$InfoFieldList[4];
7181  } else {
7182  $sqlwhere .= " WHERE ".$InfoFieldList[4];
7183  }
7184  } else {
7185  $sqlwhere .= ' WHERE 1=1';
7186  }
7187  // Some tables may have field, some other not. For the moment we disable it.
7188  if (in_array($InfoFieldList[0], array('tablewithentity'))) {
7189  $sqlwhere .= " AND entity = ".((int) $conf->entity);
7190  }
7191  // $sql.=preg_replace('/^ AND /','',$sqlwhere);
7192  // print $sql;
7193 
7194  $sql .= $sqlwhere;
7195  dol_syslog(get_class($this).'::showInputField type=chkbxlst', LOG_DEBUG);
7196  $resql = $this->db->query($sql);
7197  if ($resql) {
7198  $num = $this->db->num_rows($resql);
7199  $i = 0;
7200 
7201  $data = array();
7202 
7203  while ($i < $num) {
7204  $labeltoshow = '';
7205  $obj = $this->db->fetch_object($resql);
7206 
7207  $notrans = false;
7208  // Several field into label (eq table:code|libelle:rowid)
7209  $fields_label = explode('|', $InfoFieldList[1]);
7210  if (count($fields_label) > 1) {
7211  $notrans = true;
7212  foreach ($fields_label as $field_toshow) {
7213  $labeltoshow .= $obj->$field_toshow.' ';
7214  }
7215  } else {
7216  $labeltoshow = $obj->{$InfoFieldList[1]};
7217  }
7218  $labeltoshow = dol_trunc($labeltoshow, 45);
7219 
7220  if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
7221  foreach ($fields_label as $field_toshow) {
7222  $translabel = $langs->trans($obj->$field_toshow);
7223  if ($translabel != $obj->$field_toshow) {
7224  $labeltoshow = dol_trunc($translabel, 18).' ';
7225  } else {
7226  $labeltoshow = dol_trunc($obj->$field_toshow, 18).' ';
7227  }
7228  }
7229 
7230  $data[$obj->rowid] = $labeltoshow;
7231  } else {
7232  if (