dolibarr  13.0.2
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 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-2020 Frédéric France <frederic.france@netlogic.fr>
16  * Copyright (C) 2018 Josep Lluís Amador <joseplluis@lliuretic.cat>
17  *
18  * This program is free software; you can redistribute it and/or modify
19  * it under the terms of the GNU General Public License as published by
20  * the Free Software Foundation; either version 3 of the License, or
21  * (at your option) any later version.
22  *
23  * This program is distributed in the hope that it will be useful,
24  * but WITHOUT ANY WARRANTY; without even the implied warranty of
25  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26  * GNU General Public License for more details.
27  *
28  * You should have received a copy of the GNU General Public License
29  * along with this program. If not, see <https://www.gnu.org/licenses/>.
30  */
31 
42 abstract class CommonObject
43 {
47  public $db;
48 
52  public $id;
53 
57  public $entity;
58 
63  public $error;
64 
68  public $errorhidden;
69 
73  public $errors = array();
74 
78  public $element;
79 
83  public $table_element;
84 
88  public $table_element_line = '';
89 
93  public $import_key;
94 
98  public $array_options = array();
99 
103  public $array_languages = null; // Value is array() when load already tried
104 
108  public $linkedObjectsIds;
109 
113  public $linkedObjects;
114 
118  public $oldcopy;
119 
123  protected $table_ref_field = '';
124 
125 
126 
127  // Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them.
128 
132  public $context = array();
133 
137  public $canvas;
138 
143  public $project;
144 
149  public $fk_project;
150 
155  public $projet;
156 
161  public $contact;
162 
167  public $contact_id;
168 
173  public $thirdparty;
174 
179  public $user;
180 
185  public $origin;
186 
191  public $origin_id;
192 
196  public $ref;
197 
201  public $ref_ext;
202 
206  public $ref_previous;
207 
211  public $ref_next;
212 
216  public $newref;
217 
222  public $statut;
223 
228  public $status;
229 
234  public $country;
235 
240  public $country_id;
241 
246  public $country_code;
247 
252  public $state;
253 
258  public $state_id;
259 
264  public $state_code;
265 
270  public $region_id;
271 
276  public $region_code;
277 
282  public $region;
283 
288  public $barcode_type;
289 
294  public $barcode_type_code;
295 
300  public $barcode_type_label;
301 
306  public $barcode_type_coder;
307 
312  public $mode_reglement_id;
313 
318  public $cond_reglement_id;
319 
323  public $demand_reason_id;
324 
329  public $transport_mode_id;
330 
336  public $cond_reglement;
337 
343  public $fk_delivery_address;
344 
349  public $shipping_method_id;
350 
355  public $model_pdf;
356 
361  public $last_main_doc;
362 
368  public $fk_bank;
369 
374  public $fk_account;
375 
379  public $openid;
380 
385  public $note_public;
386 
391  public $note_private;
392 
397  public $note;
398 
403  public $total_ht;
404 
409  public $total_tva;
410 
415  public $total_localtax1;
416 
421  public $total_localtax2;
422 
427  public $total_ttc;
428 
432  public $lines;
433 
438  public $comments = array();
439 
443  public $name;
444 
448  public $lastname;
449 
453  public $firstname;
454 
458  public $civility_id;
459 
460  // Dates
464  public $date_creation;
465 
469  public $date_validation; // Date validation
470 
474  public $date_modification; // Date last change (tms field)
475 
476  public $next_prev_filter;
477 
481  public $specimen = 0;
482 
486  public $sendtoid;
487 
491  public $alreadypaid;
492 
496  protected $childtables = array();
497 
503  protected $childtablesoncascade = array();
504 
505 
506  // No constructor as it is an abstract class
517  public static function isExistingObject($element, $id, $ref = '', $ref_ext = '')
518  {
519  global $db, $conf;
520 
521  $sql = "SELECT rowid, ref, ref_ext";
522  $sql .= " FROM ".MAIN_DB_PREFIX.$element;
523  $sql .= " WHERE entity IN (".getEntity($element).")";
524 
525  if ($id > 0) $sql .= " AND rowid = ".$db->escape($id);
526  elseif ($ref) $sql .= " AND ref = '".$db->escape($ref)."'";
527  elseif ($ref_ext) $sql .= " AND ref_ext = '".$db->escape($ref_ext)."'";
528  else {
529  $error = 'ErrorWrongParameters';
530  dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
531  return -1;
532  }
533  if ($ref || $ref_ext) $sql .= " AND entity = ".$conf->entity;
534 
535  dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
536  $resql = $db->query($sql);
537  if ($resql)
538  {
539  $num = $db->num_rows($resql);
540  if ($num > 0) return 1;
541  else return 0;
542  }
543  return -1;
544  }
545 
551  public function errorsToString()
552  {
553  return $this->error.(is_array($this->errors) ? (($this->error != '' ? ', ' : '').join(', ', $this->errors)) : '');
554  }
555 
556 
563  public function getFormatedCustomerRef($objref)
564  {
565  global $hookmanager;
566 
567  $parameters = array('objref'=>$objref);
568  $action = '';
569  $reshook = $hookmanager->executeHooks('getFormatedCustomerRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
570  if ($reshook > 0)
571  {
572  return $hookmanager->resArray['objref'];
573  }
574  return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
575  }
576 
583  public function getFormatedSupplierRef($objref)
584  {
585  global $hookmanager;
586 
587  $parameters = array('objref'=>$objref);
588  $action = '';
589  $reshook = $hookmanager->executeHooks('getFormatedSupplierRef', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
590  if ($reshook > 0)
591  {
592  return $hookmanager->resArray['objref'];
593  }
594  return $objref.(isset($hookmanager->resArray['objref']) ? $hookmanager->resArray['objref'] : '');
595  }
596 
606  public function getFullName($langs, $option = 0, $nameorder = -1, $maxlen = 0)
607  {
608  //print "lastname=".$this->lastname." name=".$this->name." nom=".$this->nom."<br>\n";
609  $lastname = $this->lastname;
610  $firstname = $this->firstname;
611  if (empty($lastname)) $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 : '')))));
612 
613  $ret = '';
614  if ($option && $this->civility_code)
615  {
616  if ($langs->transnoentitiesnoconv("Civility".$this->civility_code) != "Civility".$this->civility_code) $ret .= $langs->transnoentitiesnoconv("Civility".$this->civility_code).' ';
617  else $ret .= $this->civility_code.' ';
618  }
619 
620  $ret .= dolGetFirstLastname($firstname, $lastname, $nameorder);
621 
622  return dol_trunc($ret, $maxlen);
623  }
624 
630  public function setUpperOrLowerCase()
631  {
632  global $conf;
633  if (!empty($conf->global->MAIN_FIRST_TO_UPPER)) {
634  $this->lastname = dol_ucwords(dol_strtolower($this->lastname));
635  $this->firstname = dol_ucwords(dol_strtolower($this->firstname));
636  $this->name = dol_ucwords(dol_strtolower($this->name));
637  }
638  if (!empty($conf->global->MAIN_ALL_TO_UPPER)) {
639  $this->lastname = dol_strtoupper($this->lastname);
640  $this->name = dol_strtoupper($this->name);
641  }
642  if (!empty($conf->global->MAIN_ALL_TOWN_TO_UPPER)) {
643  $this->town = dol_strtoupper($this->town);
644  }
645  }
646 
653  public function getKanbanView($option = '')
654  {
655  $return = '<div class="box-flex-item">';
656  $return .= '<div class="info-box info-box-sm">';
657  $return .= '<span class="info-box-icon bg-infobox-action">';
658  $return .= '<i class="fa fa-dol-action"></i>'; // Can be image
659  $return .= '</span>';
660  $return .= '<div class="info-box-content">';
661  $return .= '<span class="info-box-title">'.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).'</span>';
662  $return .= '</div>';
663  $return .= '</div>';
664  $return .= '</div>';
665 
666  return $return;
667  }
668 
678  public function getFullAddress($withcountry = 0, $sep = "\n", $withregion = 0, $extralangcode = '')
679  {
680  if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country)))
681  {
682  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
683  $tmparray = getCountry($this->country_id, 'all');
684  $this->country_code = $tmparray['code'];
685  $this->country = $tmparray['label'];
686  }
687 
688  if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_code)))
689  {
690  require_once DOL_DOCUMENT_ROOT.'/core/lib/company.lib.php';
691  $tmparray = getState($this->state_id, 'all', 0, 1);
692  $this->state_code = $tmparray['code'];
693  $this->state = $tmparray['label'];
694  $this->region_code = $tmparray['region_code'];
695  $this->region = $tmparray['region'];
696  }
697 
698  return dol_format_address($this, $withcountry, $sep, '', 0, $extralangcode);
699  }
700 
701 
709  public function getBannerAddress($htmlkey, $object)
710  {
711  global $conf, $langs, $form, $extralanguages;
712 
713  $countriesusingstate = array('AU', 'US', 'IN', 'GB', 'ES', 'UK', 'TR'); // See also option MAIN_FORCE_STATE_INTO_ADDRESS
714 
715  $contactid = 0;
716  $thirdpartyid = 0;
717  $elementforaltlanguage = $this->element;
718  if ($this->element == 'societe') {
719  $thirdpartyid = $this->id;
720  }
721  if ($this->element == 'contact') {
722  $contactid = $this->id;
723  $thirdpartyid = $object->fk_soc;
724  }
725  if ($this->element == 'user') {
726  $contactid = $this->contact_id;
727  $thirdpartyid = $object->fk_soc;
728  }
729 
730  $out = '';
731 
732  $outdone = 0;
733  $coords = $this->getFullAddress(1, ', ', $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT);
734  if ($coords)
735  {
736  if (!empty($conf->use_javascript_ajax))
737  {
738  // Add picto with tooltip on map
739  $namecoords = '';
740  if ($this->element == 'contact' && !empty($conf->global->MAIN_SHOW_COMPANY_NAME_IN_BANNER_ADDRESS))
741  {
742  $namecoords .= $object->name.'<br>';
743  }
744  $namecoords .= $this->getFullName($langs, 1).'<br>'.$coords;
745  // hideonsmatphone because copyToClipboard call jquery dialog that does not work with jmobile
746  $out .= '<a href="#" class="hideonsmartphone" onclick="return copyToClipboard(\''.dol_escape_js($namecoords).'\',\''.dol_escape_js($langs->trans("HelpCopyToClipboard")).'\');">';
747  $out .= img_picto($langs->trans("Address"), 'map-marker-alt');
748  $out .= '</a> ';
749  }
750  $out .= dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', '); $outdone++;
751  $outdone++;
752 
753  // List of extra languages
754  $arrayoflangcode = array();
755  if (!empty($conf->global->PDF_USE_ALSO_LANGUAGE_CODE)) $arrayoflangcode[] = $conf->global->PDF_USE_ALSO_LANGUAGE_CODE;
756 
757  if (is_array($arrayoflangcode) && count($arrayoflangcode)) {
758  if (!is_object($extralanguages)) {
759  include_once DOL_DOCUMENT_ROOT.'/core/class/extralanguages.class.php';
760  $extralanguages = new ExtraLanguages($this->db);
761  }
762  $extralanguages->fetch_name_extralanguages($elementforaltlanguage);
763 
764  if (!empty($extralanguages->attributes[$elementforaltlanguage]['address']) || !empty($extralanguages->attributes[$elementforaltlanguage]['town']))
765  {
766  $out .= "<!-- alternatelanguage for '".$elementforaltlanguage."' set to fields '".join(',', $extralanguages->attributes[$elementforaltlanguage])."' -->\n";
767  $this->fetchValuesForExtraLanguages();
768  if (!is_object($form)) $form = new Form($this->db);
769  $htmltext = '';
770  // If there is extra languages
771  foreach ($arrayoflangcode as $extralangcode) {
772  $s = picto_from_langcode($extralangcode, 'class="pictoforlang paddingright"');
773  $coords = $this->getFullAddress(1, ', ', $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT, $extralangcode);
774  $htmltext .= $s.dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', ');
775  }
776  $out .= $form->textwithpicto('', $htmltext, -1, 'language', 'opacitymedium paddingleft');
777  }
778  }
779  }
780 
781  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
782  && empty($conf->global->SOCIETE_DISABLE_STATE) && $this->state)
783  {
784  if (!empty($conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT) && $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT == 1 && $this->region) {
785  $out .= ($outdone ? ' - ' : '').$this->region.' - '.$this->state;
786  } else {
787  $out .= ($outdone ? ' - ' : '').$this->state;
788  }
789  $outdone++;
790  }
791 
792  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)) $out .= ($outdone ? '<br>' : '');
793  if (!empty($this->phone) && empty($this->phone_pro)) { // For objects that store pro phone into ->phone
794  $out .= dol_print_phone($this->phone, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro"));
795  $outdone++;
796  }
797  if (!empty($this->phone_pro)) {
798  $out .= dol_print_phone($this->phone_pro, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro"));
799  $outdone++;
800  }
801  if (!empty($this->phone_mobile)) {
802  $out .= dol_print_phone($this->phone_mobile, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'mobile', $langs->trans("PhoneMobile"));
803  $outdone++;
804  }
805  if (!empty($this->phone_perso)) {
806  $out .= dol_print_phone($this->phone_perso, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePerso"));
807  $outdone++;
808  }
809  if (!empty($this->office_phone)) {
810  $out .= dol_print_phone($this->office_phone, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'phone', $langs->trans("PhonePro"));
811  $outdone++;
812  }
813  if (!empty($this->user_mobile)) {
814  $out .= dol_print_phone($this->user_mobile, $this->country_code, $contactid, $thirdpartyid, 'AC_TEL', '&nbsp;', 'mobile', $langs->trans("PhoneMobile"));
815  $outdone++;
816  }
817  if (!empty($this->fax)) {
818  $out .= dol_print_phone($this->fax, $this->country_code, $contactid, $thirdpartyid, 'AC_FAX', '&nbsp;', 'fax', $langs->trans("Fax"));
819  $outdone++;
820  }
821  if (!empty($this->office_fax)) {
822  $out .= dol_print_phone($this->office_fax, $this->country_code, $contactid, $thirdpartyid, 'AC_FAX', '&nbsp;', 'fax', $langs->trans("Fax"));
823  $outdone++;
824  }
825 
826  if ($out) $out .= '<div style="clear: both;"></div>';
827  $outdone = 0;
828  if (!empty($this->email)) {
829  $out .= dol_print_email($this->email, $this->id, $object->id, 'AC_EMAIL', 0, 0, 1);
830  $outdone++;
831  }
832  if (!empty($this->url)) {
833  //$out.=dol_print_url($this->url,'_goout',0,1);//steve changed to blank
834  $out .= dol_print_url($this->url, '_blank', 0, 1);
835  $outdone++;
836  }
837 
838  if (!empty($conf->socialnetworks->enabled)) {
839  $outsocialnetwork = '';
840 
841  if (is_array($this->socialnetworks) && count($this->socialnetworks) > 0) {
842  $socialnetworksdict = getArrayOfSocialNetworks();
843  foreach ($this->socialnetworks as $key => $value) {
844  if ($value) {
845  $outsocialnetwork .= dol_print_socialnetworks($value, $this->id, $object->id, $key, $socialnetworksdict);
846  }
847  $outdone++;
848  }
849  } else { // Old code to remove
850  if ($this->skype) $outsocialnetwork .= dol_print_socialnetworks($this->skype, $this->id, $object->id, 'skype');
851  $outdone++;
852  if ($this->jabberid) $outsocialnetwork .= dol_print_socialnetworks($this->jabberid, $this->id, $object->id, 'jabber');
853  $outdone++;
854  if ($this->twitter) $outsocialnetwork .= dol_print_socialnetworks($this->twitter, $this->id, $object->id, 'twitter');
855  $outdone++;
856  if ($this->facebook) $outsocialnetwork .= dol_print_socialnetworks($this->facebook, $this->id, $object->id, 'facebook');
857  $outdone++;
858  if ($this->linkedin) $outsocialnetwork .= dol_print_socialnetworks($this->linkedin, $this->id, $object->id, 'linkedin');
859  $outdone++;
860  }
861 
862  if ($outsocialnetwork) {
863  $out .= '<div style="clear: both;">'.$outsocialnetwork.'</div>';
864  }
865  }
866 
867  if ($out) return '<!-- BEGIN part to show address block -->'."\n".$out.'<!-- END Part to show address block -->'."\n";
868  else return '';
869  }
870 
879  public function getLastMainDocLink($modulepart, $initsharekey = 0, $relativelink = 0)
880  {
881  global $user, $dolibarr_main_url_root;
882 
883  if (empty($this->last_main_doc))
884  {
885  return ''; // No way to known which document name to use
886  }
887 
888  include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
889  $ecmfile = new EcmFiles($this->db);
890  $result = $ecmfile->fetch(0, '', $this->last_main_doc);
891  if ($result < 0)
892  {
893  $this->error = $ecmfile->error;
894  $this->errors = $ecmfile->errors;
895  return -1;
896  }
897 
898  if (empty($ecmfile->id))
899  {
900  // Add entry into index
901  if ($initsharekey)
902  {
903  require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
904  // TODO We can't, we dont' have full path of file, only last_main_doc adn ->element, so we must rebuild full path first
905  /*
906  $ecmfile->filepath = $rel_dir;
907  $ecmfile->filename = $filename;
908  $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
909  $ecmfile->fullpath_orig = '';
910  $ecmfile->gen_or_uploaded = 'generated';
911  $ecmfile->description = ''; // indexed content
912  $ecmfile->keyword = ''; // keyword content
913  $ecmfile->share = getRandomPassword(true);
914  $result = $ecmfile->create($user);
915  if ($result < 0)
916  {
917  $this->error = $ecmfile->error;
918  $this->errors = $ecmfile->errors;
919  }
920  */
921  } else return '';
922  } elseif (empty($ecmfile->share))
923  {
924  // Add entry into index
925  if ($initsharekey)
926  {
927  require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
928  $ecmfile->share = getRandomPassword(true);
929  $ecmfile->update($user);
930  } else return '';
931  }
932  // Define $urlwithroot
933  $urlwithouturlroot = preg_replace('/'.preg_quote(DOL_URL_ROOT, '/').'$/i', '', trim($dolibarr_main_url_root));
934  // This is to use external domain name found into config file
935  //if (DOL_URL_ROOT && ! preg_match('/\/$/', $urlwithouturlroot) && ! preg_match('/^\//', DOL_URL_ROOT)) $urlwithroot=$urlwithouturlroot.'/'.DOL_URL_ROOT;
936  //else
937  $urlwithroot = $urlwithouturlroot.DOL_URL_ROOT;
938  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
939 
940  $forcedownload = 0;
941 
942  $paramlink = '';
943  //if (! empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart; // For sharing with hash (so public files), modulepart is not required.
944  //if (! empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; // For sharing with hash (so public files), entity is not required.
945  //$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath); // No need of name of file for public link, we will use the hash
946  if (!empty($ecmfile->share)) $paramlink .= ($paramlink ? '&' : '').'hashp='.$ecmfile->share; // Hash for public share
947  if ($forcedownload) $paramlink .= ($paramlink ? '&' : '').'attachment=1';
948 
949  if ($relativelink)
950  {
951  $linktoreturn = 'document.php'.($paramlink ? '?'.$paramlink : '');
952  } else {
953  $linktoreturn = $urlwithroot.'/document.php'.($paramlink ? '?'.$paramlink : '');
954  }
955 
956  // Here $ecmfile->share is defined
957  return $linktoreturn;
958  }
959 
960 
961  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
971  public function add_contact($fk_socpeople, $type_contact, $source = 'external', $notrigger = 0)
972  {
973  // phpcs:enable
974  global $user, $langs;
975 
976 
977  dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
978 
979  // Check parameters
980  if ($fk_socpeople <= 0)
981  {
982  $langs->load("errors");
983  $this->error = $langs->trans("ErrorWrongValueForParameterX", "1");
984  dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
985  return -1;
986  }
987  if (!$type_contact)
988  {
989  $langs->load("errors");
990  $this->error = $langs->trans("ErrorWrongValueForParameterX", "2");
991  dol_syslog(get_class($this)."::add_contact ".$this->error, LOG_ERR);
992  return -2;
993  }
994 
995  $id_type_contact = 0;
996  if (is_numeric($type_contact))
997  {
998  $id_type_contact = $type_contact;
999  } else {
1000  // We look for id type_contact
1001  $sql = "SELECT tc.rowid";
1002  $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
1003  $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1004  $sql .= " AND tc.source='".$this->db->escape($source)."'";
1005  $sql .= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
1006  //print $sql;
1007  $resql = $this->db->query($sql);
1008  if ($resql)
1009  {
1010  $obj = $this->db->fetch_object($resql);
1011  if ($obj) $id_type_contact = $obj->rowid;
1012  }
1013  }
1014 
1015  if ($id_type_contact == 0)
1016  {
1017  $this->error = 'CODE_NOT_VALID_FOR_THIS_ELEMENT';
1018  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");
1019  return -3;
1020  }
1021 
1022  $datecreate = dol_now();
1023 
1024  // Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
1025  $TListeContacts = $this->liste_contact(-1, $source);
1026  $already_added = false;
1027  if (is_array($TListeContacts) && !empty($TListeContacts)) {
1028  foreach ($TListeContacts as $array_contact) {
1029  if ($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
1030  $already_added = true;
1031  break;
1032  }
1033  }
1034  }
1035 
1036  if (!$already_added) {
1037  $this->db->begin();
1038 
1039  // Insert into database
1040  $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_contact";
1041  $sql .= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
1042  $sql .= " VALUES (".$this->id.", ".$fk_socpeople." , ";
1043  $sql .= "'".$this->db->idate($datecreate)."'";
1044  $sql .= ", 4, ".$id_type_contact;
1045  $sql .= ")";
1046 
1047  $resql = $this->db->query($sql);
1048  if ($resql)
1049  {
1050  if (!$notrigger)
1051  {
1052  $result = $this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
1053  if ($result < 0)
1054  {
1055  $this->db->rollback();
1056  return -1;
1057  }
1058  }
1059 
1060  $this->db->commit();
1061  return 1;
1062  } else {
1063  if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
1064  {
1065  $this->error = $this->db->errno();
1066  $this->db->rollback();
1067  return -2;
1068  } else {
1069  $this->error = $this->db->error();
1070  $this->db->rollback();
1071  return -1;
1072  }
1073  }
1074  } else return 0;
1075  }
1076 
1077  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1085  public function copy_linked_contact($objFrom, $source = 'internal')
1086  {
1087  // phpcs:enable
1088  $contacts = $objFrom->liste_contact(-1, $source);
1089  foreach ($contacts as $contact)
1090  {
1091  if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0)
1092  {
1093  $this->error = $this->db->lasterror();
1094  return -1;
1095  }
1096  }
1097  return 1;
1098  }
1099 
1100  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1110  public function update_contact($rowid, $statut, $type_contact_id = 0, $fk_socpeople = 0)
1111  {
1112  // phpcs:enable
1113  // Insert into database
1114  $sql = "UPDATE ".MAIN_DB_PREFIX."element_contact set";
1115  $sql .= " statut = ".$statut;
1116  if ($type_contact_id) $sql .= ", fk_c_type_contact = ".((int) $type_contact_id);
1117  if ($fk_socpeople) $sql .= ", fk_socpeople = ".((int) $fk_socpeople);
1118  $sql .= " where rowid = ".$rowid;
1119  $resql = $this->db->query($sql);
1120  if ($resql)
1121  {
1122  return 0;
1123  } else {
1124  $this->error = $this->db->lasterror();
1125  return -1;
1126  }
1127  }
1128 
1129  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1137  public function delete_contact($rowid, $notrigger = 0)
1138  {
1139  // phpcs:enable
1140  global $user;
1141 
1142 
1143  $this->db->begin();
1144 
1145  $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
1146  $sql .= " WHERE rowid =".$rowid;
1147 
1148  dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
1149  if ($this->db->query($sql))
1150  {
1151  if (!$notrigger)
1152  {
1153  $result = $this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
1154  if ($result < 0) { $this->db->rollback(); return -1; }
1155  }
1156 
1157  $this->db->commit();
1158  return 1;
1159  } else {
1160  $this->error = $this->db->lasterror();
1161  $this->db->rollback();
1162  return -1;
1163  }
1164  }
1165 
1166  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1174  public function delete_linked_contact($source = '', $code = '')
1175  {
1176  // phpcs:enable
1177  $temp = array();
1178  $typeContact = $this->liste_type_contact($source, '', 0, 0, $code);
1179 
1180  foreach ($typeContact as $key => $value)
1181  {
1182  array_push($temp, $key);
1183  }
1184  $listId = implode(",", $temp);
1185 
1186  $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
1187  $sql .= " WHERE element_id = ".$this->id;
1188  if ($listId)
1189  $sql .= " AND fk_c_type_contact IN (".$listId.")";
1190 
1191  dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
1192  if ($this->db->query($sql))
1193  {
1194  return 1;
1195  } else {
1196  $this->error = $this->db->lasterror();
1197  return -1;
1198  }
1199  }
1200 
1201  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1211  public function liste_contact($status = -1, $source = 'external', $list = 0, $code = '')
1212  {
1213  // phpcs:enable
1214  global $langs;
1215 
1216  $tab = array();
1217 
1218  $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
1219  if ($source == 'internal') $sql .= ", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
1220  if ($source == 'external' || $source == 'thirdparty') $sql .= ", t.fk_soc as socid, t.statut as statuscontact";
1221  $sql .= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
1222  $sql .= ", tc.source, tc.element, tc.code, tc.libelle";
1223  $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
1224  $sql .= ", ".MAIN_DB_PREFIX."element_contact ec";
1225  if ($source == 'internal') $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
1226  if ($source == 'external' || $source == 'thirdparty') $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
1227  $sql .= " WHERE ec.element_id =".$this->id;
1228  $sql .= " AND ec.fk_c_type_contact=tc.rowid";
1229  $sql .= " AND tc.element='".$this->db->escape($this->element)."'";
1230  if ($code) $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1231  if ($source == 'internal') $sql .= " AND tc.source = 'internal'";
1232  if ($source == 'external' || $source == 'thirdparty') $sql .= " AND tc.source = 'external'";
1233  $sql .= " AND tc.active=1";
1234  if ($status >= 0) $sql .= " AND ec.statut = ".$status;
1235  $sql .= " ORDER BY t.lastname ASC";
1236 
1237  dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
1238  $resql = $this->db->query($sql);
1239  if ($resql)
1240  {
1241  $num = $this->db->num_rows($resql);
1242  $i = 0;
1243  while ($i < $num)
1244  {
1245  $obj = $this->db->fetch_object($resql);
1246 
1247  if (!$list)
1248  {
1249  $transkey = "TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
1250  $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1251  $tab[$i] = array('source'=>$obj->source, 'socid'=>$obj->socid, 'id'=>$obj->id,
1252  'nom'=>$obj->lastname, // For backward compatibility
1253  'civility'=>$obj->civility, 'lastname'=>$obj->lastname, 'firstname'=>$obj->firstname, 'email'=>$obj->email, 'login'=>$obj->login, 'photo'=>$obj->photo, 'statuscontact'=>$obj->statuscontact,
1254  'rowid'=>$obj->rowid, 'code'=>$obj->code, 'libelle'=>$libelle_type, 'status'=>$obj->statuslink, 'fk_c_type_contact'=>$obj->fk_c_type_contact);
1255  } else {
1256  $tab[$i] = $obj->id;
1257  }
1258 
1259  $i++;
1260  }
1261 
1262  return $tab;
1263  } else {
1264  $this->error = $this->db->lasterror();
1265  dol_print_error($this->db);
1266  return -1;
1267  }
1268  }
1269 
1270 
1277  public function swapContactStatus($rowid)
1278  {
1279  $sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1280  $sql .= " tc.code, tc.libelle";
1281  $sql .= " FROM (".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as tc)";
1282  $sql .= " WHERE ec.rowid =".$rowid;
1283  $sql .= " AND ec.fk_c_type_contact=tc.rowid";
1284  $sql .= " AND tc.element = '".$this->db->escape($this->element)."'";
1285 
1286  dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1287  $resql = $this->db->query($sql);
1288  if ($resql)
1289  {
1290  $obj = $this->db->fetch_object($resql);
1291  $newstatut = ($obj->statut == 4) ? 5 : 4;
1292  $result = $this->update_contact($rowid, $newstatut);
1293  $this->db->free($resql);
1294  return $result;
1295  } else {
1296  $this->error = $this->db->error();
1297  dol_print_error($this->db);
1298  return -1;
1299  }
1300  }
1301 
1302  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1313  public function liste_type_contact($source = 'internal', $order = 'position', $option = 0, $activeonly = 0, $code = '')
1314  {
1315  // phpcs:enable
1316  global $langs;
1317 
1318  if (empty($order)) $order = 'position';
1319  if ($order == 'position') $order .= ',code';
1320 
1321  $tab = array();
1322  $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position";
1323  $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
1324  $sql .= " WHERE tc.element='".$this->db->escape($this->element)."'";
1325  if ($activeonly == 1) $sql .= " AND tc.active=1"; // only the active types
1326  if (!empty($source) && $source != 'all') $sql .= " AND tc.source='".$this->db->escape($source)."'";
1327  if (!empty($code)) $sql .= " AND tc.code='".$this->db->escape($code)."'";
1328  $sql .= $this->db->order($order, 'ASC');
1329 
1330  //print "sql=".$sql;
1331  $resql = $this->db->query($sql);
1332  if ($resql)
1333  {
1334  $num = $this->db->num_rows($resql);
1335  $i = 0;
1336  while ($i < $num)
1337  {
1338  $obj = $this->db->fetch_object($resql);
1339 
1340  $transkey = "TypeContact_".$this->element."_".$source."_".$obj->code;
1341  $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1342  if (empty($option)) $tab[$obj->rowid] = $libelle_type;
1343  else $tab[$obj->code] = $libelle_type;
1344  $i++;
1345  }
1346  return $tab;
1347  } else {
1348  $this->error = $this->db->lasterror();
1349  //dol_print_error($this->db);
1350  return null;
1351  }
1352  }
1353 
1354  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1366  public function listeTypeContacts($source = 'internal', $option = 0, $activeonly = 0, $code = '', $element = '', $excludeelement = '')
1367  {
1368  // phpcs:enable
1369  global $langs, $conf;
1370 
1371  $langs->loadLangs(array('bills', 'contracts', 'interventions', 'orders', 'projects', 'propal', 'ticket', 'agenda'));
1372 
1373  $tab = array();
1374 
1375  $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position, tc.element";
1376  $sql .= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
1377 
1378  $sqlWhere = array();
1379  if (!empty($element)) {
1380  $sqlWhere[] = " tc.element='".$this->db->escape($element)."'";
1381  }
1382  if (!empty($excludeelement)) {
1383  $sqlWhere[] = " tc.element <> '".$this->db->escape($excludeelement)."'";
1384  }
1385 
1386  if ($activeonly == 1)
1387  $sqlWhere[] = " tc.active=1"; // only the active types
1388 
1389  if (!empty($source) && $source != 'all')
1390  $sqlWhere[] = " tc.source='".$this->db->escape($source)."'";
1391 
1392  if (!empty($code))
1393  $sqlWhere[] = " tc.code='".$this->db->escape($code)."'";
1394 
1395  if (count($sqlWhere) > 0) {
1396  $sql .= " WHERE ".implode(' AND ', $sqlWhere);
1397  }
1398 
1399  $sql .= $this->db->order('tc.element, tc.position', 'ASC');
1400 
1401  dol_syslog(__METHOD__, LOG_DEBUG);
1402  $resql = $this->db->query($sql);
1403  if ($resql) {
1404  $num = $this->db->num_rows($resql);
1405  if ($num > 0) {
1406  $langs->loadLangs(array("propal", "orders", "bills", "suppliers", "contracts", "supplier_proposal"));
1407 
1408  while ($obj = $this->db->fetch_object($resql)) {
1409  $modulename = $obj->element;
1410  if (strpos($obj->element, 'project') !== false) {
1411  $modulename = 'projet';
1412  } elseif ($obj->element == 'contrat') {
1413  $element = 'contract';
1414  } elseif ($obj->element == 'action') {
1415  $modulename = 'agenda';
1416  } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1417  $modulename = 'fournisseur';
1418  } elseif (strpos($obj->element, 'supplier') !== false && $obj->element != 'supplier_proposal') {
1419  $modulename = 'fournisseur';
1420  }
1421  if ($conf->{$modulename}->enabled) {
1422  $libelle_element = $langs->trans('ContactDefault_'.$obj->element);
1423  $tmpelement = $obj->element;
1424  $transkey = "TypeContact_".$tmpelement."_".$source."_".$obj->code;
1425  $libelle_type = ($langs->trans($transkey) != $transkey ? $langs->trans($transkey) : $obj->libelle);
1426  if (empty($option)) {
1427  $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1428  }
1429  else {
1430  $tab[$obj->rowid] = $libelle_element.' - '.$libelle_type;
1431  }
1432  }
1433  }
1434  }
1435  return $tab;
1436  } else {
1437  $this->error = $this->db->lasterror();
1438  return null;
1439  }
1440  }
1441 
1453  public function getIdContact($source, $code, $status = 0)
1454  {
1455  global $conf;
1456 
1457  $result = array();
1458  $i = 0;
1459  //cas particulier pour les expeditions
1460  if ($this->element == 'shipping' && $this->origin_id != 0) {
1461  $id = $this->origin_id;
1462  $element = 'commande';
1463  } elseif ($this->element == 'reception' && $this->origin_id != 0) {
1464  $id = $this->origin_id;
1465  $element = 'order_supplier';
1466  } else {
1467  $id = $this->id;
1468  $element = $this->element;
1469  }
1470 
1471  $sql = "SELECT ec.fk_socpeople";
1472  $sql .= " FROM ".MAIN_DB_PREFIX."element_contact as ec,";
1473  if ($source == 'internal') $sql .= " ".MAIN_DB_PREFIX."user as c,";
1474  if ($source == 'external') $sql .= " ".MAIN_DB_PREFIX."socpeople as c,";
1475  $sql .= " ".MAIN_DB_PREFIX."c_type_contact as tc";
1476  $sql .= " WHERE ec.element_id = ".$id;
1477  $sql .= " AND ec.fk_socpeople = c.rowid";
1478  if ($source == 'internal') $sql .= " AND c.entity IN (".getEntity('user').")";
1479  if ($source == 'external') $sql .= " AND c.entity IN (".getEntity('societe').")";
1480  $sql .= " AND ec.fk_c_type_contact = tc.rowid";
1481  $sql .= " AND tc.element = '".$this->db->escape($element)."'";
1482  $sql .= " AND tc.source = '".$this->db->escape($source)."'";
1483  if ($code) $sql .= " AND tc.code = '".$this->db->escape($code)."'";
1484  $sql .= " AND tc.active = 1";
1485  if ($status) $sql .= " AND ec.statut = ".$status;
1486 
1487  dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1488  $resql = $this->db->query($sql);
1489  if ($resql)
1490  {
1491  while ($obj = $this->db->fetch_object($resql))
1492  {
1493  $result[$i] = $obj->fk_socpeople;
1494  $i++;
1495  }
1496  } else {
1497  $this->error = $this->db->error();
1498  return null;
1499  }
1500 
1501  return $result;
1502  }
1503 
1504  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1511  public function fetch_contact($contactid = null)
1512  {
1513  // phpcs:enable
1514  if (empty($contactid)) $contactid = $this->contact_id;
1515 
1516  if (empty($contactid)) return 0;
1517 
1518  require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1519  $contact = new Contact($this->db);
1520  $result = $contact->fetch($contactid);
1521  $this->contact = $contact;
1522  return $result;
1523  }
1524 
1525  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1532  public function fetch_thirdparty($force_thirdparty_id = 0)
1533  {
1534  // phpcs:enable
1535  global $conf;
1536 
1537  if (empty($this->socid) && empty($this->fk_soc) && empty($this->fk_thirdparty) && empty($force_thirdparty_id))
1538  return 0;
1539 
1540  require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
1541 
1542  $idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : $this->fk_thirdparty);
1543  if ($force_thirdparty_id)
1544  $idtofetch = $force_thirdparty_id;
1545 
1546  if ($idtofetch) {
1547  $thirdparty = new Societe($this->db);
1548  $result = $thirdparty->fetch($idtofetch);
1549  $this->thirdparty = $thirdparty;
1550 
1551  // Use first price level if level not defined for third party
1552  if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
1553  $this->thirdparty->price_level = 1;
1554  }
1555 
1556  return $result;
1557  } else return -1;
1558  }
1559 
1560 
1568  public function fetchOneLike($ref)
1569  {
1570  if (!$this->table_ref_field) {
1571  return 0;
1572  }
1573 
1574  $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE '.$this->table_ref_field.' LIKE "'.$this->db->escape($ref).'" LIMIT 1';
1575 
1576  $query = $this->db->query($sql);
1577 
1578  if (!$this->db->num_rows($query)) {
1579  return 0;
1580  }
1581 
1582  $result = $this->db->fetch_object($query);
1583 
1584  return $this->fetch($result->rowid);
1585  }
1586 
1587  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1595  public function fetch_barcode()
1596  {
1597  // phpcs:enable
1598  global $conf;
1599 
1600  dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1601 
1602  $idtype = $this->barcode_type;
1603  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
1604  {
1605  if ($this->element == 'product') $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1606  elseif ($this->element == 'societe') $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1607  else dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1608  }
1609 
1610  if ($idtype > 0)
1611  {
1612  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
1613  {
1614  $sql = "SELECT rowid, code, libelle as label, coder";
1615  $sql .= " FROM ".MAIN_DB_PREFIX."c_barcode_type";
1616  $sql .= " WHERE rowid = ".$idtype;
1617  dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1618  $resql = $this->db->query($sql);
1619  if ($resql)
1620  {
1621  $obj = $this->db->fetch_object($resql);
1622  $this->barcode_type = $obj->rowid;
1623  $this->barcode_type_code = $obj->code;
1624  $this->barcode_type_label = $obj->label;
1625  $this->barcode_type_coder = $obj->coder;
1626  return 1;
1627  } else {
1628  dol_print_error($this->db);
1629  return -1;
1630  }
1631  }
1632  }
1633  return 0;
1634  }
1635 
1636  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1642  public function fetch_projet()
1643  {
1644  // phpcs:enable
1645  include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1646 
1647  if (empty($this->fk_project) && !empty($this->fk_projet)) $this->fk_project = $this->fk_projet; // For backward compatibility
1648  if (empty($this->fk_project)) return 0;
1649 
1650  $project = new Project($this->db);
1651  $result = $project->fetch($this->fk_project);
1652 
1653  $this->projet = $project; // deprecated
1654  $this->project = $project;
1655  return $result;
1656  }
1657 
1658  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1664  public function fetch_product()
1665  {
1666  // phpcs:enable
1667  include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1668 
1669  if (empty($this->fk_product)) return 0;
1670 
1671  $product = new Product($this->db);
1672  $result = $product->fetch($this->fk_product);
1673 
1674  $this->product = $product;
1675  return $result;
1676  }
1677 
1678  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1685  public function fetch_user($userid)
1686  {
1687  // phpcs:enable
1688  $user = new User($this->db);
1689  $result = $user->fetch($userid);
1690  $this->user = $user;
1691  return $result;
1692  }
1693 
1694  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1700  public function fetch_origin()
1701  {
1702  // phpcs:enable
1703  if ($this->origin == 'shipping') $this->origin = 'expedition';
1704  if ($this->origin == 'delivery') $this->origin = 'livraison';
1705  if ($this->origin == 'order_supplier') $this->origin = 'commandeFournisseur';
1706 
1707  $origin = $this->origin;
1708 
1709  $classname = ucfirst($origin);
1710  $this->$origin = new $classname($this->db);
1711  $this->$origin->fetch($this->origin_id);
1712  }
1713 
1723  public function fetchObjectFrom($table, $field, $key, $element = null)
1724  {
1725  global $conf;
1726 
1727  $result = false;
1728 
1729  $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$table;
1730  $sql .= " WHERE ".$field." = '".$key."'";
1731  if (!empty($element)) {
1732  $sql .= " AND entity IN (".getEntity($element).")";
1733  } else {
1734  $sql .= " AND entity = ".$conf->entity;
1735  }
1736 
1737  dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
1738  $resql = $this->db->query($sql);
1739  if ($resql)
1740  {
1741  $row = $this->db->fetch_row($resql);
1742  // Test for avoid error -1
1743  if ($row[0] > 0) {
1744  $result = $this->fetch($row[0]);
1745  }
1746  }
1747 
1748  return $result;
1749  }
1750 
1759  public function getValueFrom($table, $id, $field)
1760  {
1761  $result = false;
1762  if (!empty($id) && !empty($field) && !empty($table)) {
1763  $sql = "SELECT ".$field." FROM ".MAIN_DB_PREFIX.$table;
1764  $sql .= " WHERE rowid = ".$id;
1765 
1766  dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
1767  $resql = $this->db->query($sql);
1768  if ($resql)
1769  {
1770  $row = $this->db->fetch_row($resql);
1771  $result = $row[0];
1772  }
1773  }
1774  return $result;
1775  }
1776 
1793  public function setValueFrom($field, $value, $table = '', $id = null, $format = '', $id_field = '', $fuser = null, $trigkey = '', $fk_user_field = 'fk_user_modif')
1794  {
1795  global $user, $langs, $conf;
1796 
1797  if (empty($table)) $table = $this->table_element;
1798  if (empty($id)) $id = $this->id;
1799  if (empty($format)) $format = 'text';
1800  if (empty($id_field)) $id_field = 'rowid';
1801 
1802  $error = 0;
1803 
1804  $this->db->begin();
1805 
1806  // Special case
1807  if ($table == 'product' && $field == 'note_private') $field = 'note';
1808  if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) $fk_user_field = 'fk_user_mod';
1809 
1810  $sql = "UPDATE ".MAIN_DB_PREFIX.$table." SET ";
1811 
1812  if ($format == 'text') $sql .= $field." = '".$this->db->escape($value)."'";
1813  elseif ($format == 'int') $sql .= $field." = ".$this->db->escape($value);
1814  elseif ($format == 'date') $sql .= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
1815 
1816  if ($fk_user_field)
1817  {
1818  if (!empty($fuser) && is_object($fuser)) $sql .= ", ".$fk_user_field." = ".$fuser->id;
1819  elseif (empty($fuser) || $fuser != 'none') $sql .= ", ".$fk_user_field." = ".$user->id;
1820  }
1821 
1822  $sql .= " WHERE ".$id_field." = ".$id;
1823 
1824  dol_syslog(__METHOD__."", LOG_DEBUG);
1825  $resql = $this->db->query($sql);
1826  if ($resql)
1827  {
1828  if ($trigkey)
1829  {
1830  // call trigger with updated object values
1831  if (empty($this->fields) && method_exists($this, 'fetch'))
1832  {
1833  $result = $this->fetch($id);
1834  } else {
1835  $result = $this->fetchCommon($id);
1836  }
1837  if ($result >= 0) $result = $this->call_trigger($trigkey, (!empty($fuser) && is_object($fuser)) ? $fuser : $user); // This may set this->errors
1838  if ($result < 0) $error++;
1839  }
1840 
1841  if (!$error)
1842  {
1843  if (property_exists($this, $field)) $this->$field = $value;
1844  $this->db->commit();
1845  return 1;
1846  } else {
1847  $this->db->rollback();
1848  return -2;
1849  }
1850  } else {
1851  if ($this->db->lasterrno() == 'DB_ERROR_RECORD_ALREADY_EXISTS') {
1852  $this->error = 'DB_ERROR_RECORD_ALREADY_EXISTS';
1853  } else {
1854  $this->error = $this->db->lasterror();
1855  }
1856  $this->db->rollback();
1857  return -1;
1858  }
1859  }
1860 
1861  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1870  public function load_previous_next_ref($filter, $fieldid, $nodbprefix = 0)
1871  {
1872  // phpcs:enable
1873  global $conf, $user;
1874 
1875  if (!$this->table_element)
1876  {
1877  dol_print_error('', get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
1878  return -1;
1879  }
1880  if ($fieldid == 'none') return 1;
1881 
1882  // Security on socid
1883  $socid = 0;
1884  if ($user->socid > 0) $socid = $user->socid;
1885 
1886  // this->ismultientitymanaged contains
1887  // 0=No test on entity, 1=Test with field entity, 'field@table'=Test with link by field@table
1888  $aliastablesociete = 's';
1889  if ($this->element == 'societe') $aliastablesociete = 'te'; // te as table_element
1890 
1891  $sql = "SELECT MAX(te.".$fieldid.")";
1892  $sql .= " FROM ".(empty($nodbprefix) ?MAIN_DB_PREFIX:'').$this->table_element." as te";
1893  if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1894  $sql .= ",".MAIN_DB_PREFIX."usergroup_user as ug";
1895  }
1896  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
1897  $tmparray = explode('@', $this->ismultientitymanaged);
1898  $sql .= ", ".MAIN_DB_PREFIX.$tmparray[1]." as ".($tmparray[1] == 'societe' ? 's' : 'parenttable'); // If we need to link to this table to limit select to entity
1899  } elseif ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe as s"; // If we need to link to societe to limit select to socid
1900  elseif ($this->restrictiononfksoc == 2 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON te.fk_soc = s.rowid"; // If we need to link to societe to limit select to socid
1901  if ($this->restrictiononfksoc && !$user->rights->societe->client->voir && !$socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
1902  $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)
1903  if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql .= " AND sc.fk_user = ".$user->id;
1904  if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql .= " AND (sc.fk_user = ".$user->id.' OR te.fk_soc IS NULL)';
1905  if (!empty($filter))
1906  {
1907  if (!preg_match('/^\s*AND/i', $filter)) $sql .= " AND "; // For backward compatibility
1908  $sql .= $filter;
1909  }
1910  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
1911  $tmparray = explode('@', $this->ismultientitymanaged);
1912  $sql .= ' AND te.'.$tmparray[0].' = '.($tmparray[1] == 'societe' ? 's' : 'parenttable').'.rowid'; // If we need to link to this table to limit select to entity
1913  } elseif ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
1914  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
1915  if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1916  if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1917  $sql .= " AND te.entity IS NOT NULL"; // Show all users
1918  } else {
1919  $sql .= " AND ug.fk_user = te.rowid";
1920  $sql .= " AND ug.entity IN (".getEntity($this->element).")";
1921  }
1922  } else {
1923  $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
1924  }
1925  }
1926  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
1927  $tmparray = explode('@', $this->ismultientitymanaged);
1928  $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
1929  }
1930  if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql .= ' AND te.fk_soc = '.$socid;
1931  if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql .= ' AND (te.fk_soc = '.$socid.' OR te.fk_soc IS NULL)';
1932  if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql .= ' AND te.rowid = '.$socid;
1933  //print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
1934 
1935  $result = $this->db->query($sql);
1936  if (!$result)
1937  {
1938  $this->error = $this->db->lasterror();
1939  return -1;
1940  }
1941  $row = $this->db->fetch_row($result);
1942  $this->ref_previous = $row[0];
1943 
1944  $sql = "SELECT MIN(te.".$fieldid.")";
1945  $sql .= " FROM ".(empty($nodbprefix) ?MAIN_DB_PREFIX:'').$this->table_element." as te";
1946  if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1947  $sql .= ",".MAIN_DB_PREFIX."usergroup_user as ug";
1948  }
1949  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
1950  $tmparray = explode('@', $this->ismultientitymanaged);
1951  $sql .= ", ".MAIN_DB_PREFIX.$tmparray[1]." as ".($tmparray[1] == 'societe' ? 's' : 'parenttable'); // If we need to link to this table to limit select to entity
1952  } elseif ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql .= ", ".MAIN_DB_PREFIX."societe as s"; // If we need to link to societe to limit select to socid
1953  elseif ($this->restrictiononfksoc == 2 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON te.fk_soc = s.rowid"; // If we need to link to societe to limit select to socid
1954  if ($this->restrictiononfksoc && !$user->rights->societe->client->voir && !$socid) $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$aliastablesociete.".rowid = sc.fk_soc";
1955  $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)
1956  if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql .= " AND sc.fk_user = ".$user->id;
1957  if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql .= " AND (sc.fk_user = ".$user->id.' OR te.fk_soc IS NULL)';
1958  if (!empty($filter))
1959  {
1960  if (!preg_match('/^\s*AND/i', $filter)) $sql .= " AND "; // For backward compatibility
1961  $sql .= $filter;
1962  }
1963  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged)) {
1964  $tmparray = explode('@', $this->ismultientitymanaged);
1965  $sql .= ' AND te.'.$tmparray[0].' = '.($tmparray[1] == 'societe' ? 's' : 'parenttable').'.rowid'; // If we need to link to this table to limit select to entity
1966  } elseif ($this->restrictiononfksoc == 1 && $this->element != 'societe' && !$user->rights->societe->client->voir && !$socid) $sql .= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to socid
1967  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
1968  if ($this->element == 'user' && !empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1969  if (!empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1970  $sql .= " AND te.entity IS NOT NULL"; // Show all users
1971  } else {
1972  $sql .= " AND ug.fk_user = te.rowid";
1973  $sql .= " AND ug.entity IN (".getEntity($this->element).")";
1974  }
1975  } else {
1976  $sql .= ' AND te.entity IN ('.getEntity($this->element).')';
1977  }
1978  }
1979  if (isset($this->ismultientitymanaged) && !is_numeric($this->ismultientitymanaged) && $this->element != 'societe') {
1980  $tmparray = explode('@', $this->ismultientitymanaged);
1981  $sql .= ' AND parenttable.entity IN ('.getEntity($tmparray[1]).')';
1982  }
1983  if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql .= ' AND te.fk_soc = '.$socid;
1984  if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql .= ' AND (te.fk_soc = '.$socid.' OR te.fk_soc IS NULL)';
1985  if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql .= ' AND te.rowid = '.$socid;
1986  //print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
1987  // 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
1988 
1989  $result = $this->db->query($sql);
1990  if (!$result)
1991  {
1992  $this->error = $this->db->lasterror();
1993  return -2;
1994  }
1995  $row = $this->db->fetch_row($result);
1996  $this->ref_next = $row[0];
1997 
1998  return 1;
1999  }
2000 
2001 
2009  public function getListContactId($source = 'external')
2010  {
2011  $contactAlreadySelected = array();
2012  $tab = $this->liste_contact(-1, $source);
2013  $num = count($tab);
2014  $i = 0;
2015  while ($i < $num)
2016  {
2017  if ($source == 'thirdparty') $contactAlreadySelected[$i] = $tab[$i]['socid'];
2018  else $contactAlreadySelected[$i] = $tab[$i]['id'];
2019  $i++;
2020  }
2021  return $contactAlreadySelected;
2022  }
2023 
2024 
2031  public function setProject($projectid)
2032  {
2033  if (!$this->table_element)
2034  {
2035  dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined", LOG_ERR);
2036  return -1;
2037  }
2038 
2039  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2040  if (!empty($this->fields['fk_project'])) // Common case
2041  {
2042  if ($projectid) $sql .= ' SET fk_project = '.$projectid;
2043  else $sql .= ' SET fk_project = NULL';
2044  $sql .= ' WHERE rowid = '.$this->id;
2045  } elseif ($this->table_element == 'actioncomm') // Special case for actioncomm
2046  {
2047  if ($projectid) $sql .= ' SET fk_project = '.$projectid;
2048  else $sql .= ' SET fk_project = NULL';
2049  $sql .= ' WHERE id = '.$this->id;
2050  } else // Special case for old architecture objects
2051  {
2052  if ($projectid) $sql .= ' SET fk_projet = '.$projectid;
2053  else $sql .= ' SET fk_projet = NULL';
2054  $sql .= ' WHERE rowid = '.$this->id;
2055  }
2056 
2057  dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
2058  if ($this->db->query($sql))
2059  {
2060  $this->fk_project = $projectid;
2061  return 1;
2062  } else {
2063  dol_print_error($this->db);
2064  return -1;
2065  }
2066  }
2067 
2074  public function setPaymentMethods($id)
2075  {
2076  dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
2077  if ($this->statut >= 0 || $this->element == 'societe')
2078  {
2079  // TODO uniformize field name
2080  $fieldname = 'fk_mode_reglement';
2081  if ($this->element == 'societe') $fieldname = 'mode_reglement';
2082  if (get_class($this) == 'Fournisseur') $fieldname = 'mode_reglement_supplier';
2083 
2084  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2085  $sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
2086  $sql .= ' WHERE rowid='.$this->id;
2087 
2088  if ($this->db->query($sql))
2089  {
2090  $this->mode_reglement_id = $id;
2091  // for supplier
2092  if (get_class($this) == 'Fournisseur') $this->mode_reglement_supplier_id = $id;
2093  return 1;
2094  } else {
2095  dol_syslog(get_class($this).'::setPaymentMethods Error '.$sql.' - '.$this->db->error());
2096  $this->error = $this->db->error();
2097  return -1;
2098  }
2099  } else {
2100  dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
2101  $this->error = 'Status of the object is incompatible '.$this->statut;
2102  return -2;
2103  }
2104  }
2105 
2112  public function setMulticurrencyCode($code)
2113  {
2114  dol_syslog(get_class($this).'::setMulticurrencyCode('.$code.')');
2115  if ($this->statut >= 0 || $this->element == 'societe')
2116  {
2117  $fieldname = 'multicurrency_code';
2118 
2119  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2120  $sql .= ' SET '.$fieldname." = '".$this->db->escape($code)."'";
2121  $sql .= ' WHERE rowid='.$this->id;
2122 
2123  if ($this->db->query($sql))
2124  {
2125  $this->multicurrency_code = $code;
2126 
2127  list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
2128  if ($rate) $this->setMulticurrencyRate($rate, 2);
2129 
2130  return 1;
2131  } else {
2132  dol_syslog(get_class($this).'::setMulticurrencyCode Error '.$sql.' - '.$this->db->error());
2133  $this->error = $this->db->error();
2134  return -1;
2135  }
2136  } else {
2137  dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
2138  $this->error = 'Status of the object is incompatible '.$this->statut;
2139  return -2;
2140  }
2141  }
2142 
2150  public function setMulticurrencyRate($rate, $mode = 1)
2151  {
2152  dol_syslog(get_class($this).'::setMulticurrencyRate('.$rate.','.$mode.')');
2153  if ($this->statut >= 0 || $this->element == 'societe')
2154  {
2155  $fieldname = 'multicurrency_tx';
2156 
2157  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2158  $sql .= ' SET '.$fieldname.' = '.$rate;
2159  $sql .= ' WHERE rowid='.$this->id;
2160 
2161  if ($this->db->query($sql))
2162  {
2163  $this->multicurrency_tx = $rate;
2164 
2165  // Update line price
2166  if (!empty($this->lines))
2167  {
2168  foreach ($this->lines as &$line)
2169  {
2170  // Amounts in company currency will be recalculated
2171  if ($mode == 1) {
2172  $line->subprice = 0;
2173  }
2174 
2175  // Amounts in foreign currency will be recalculated
2176  if ($mode == 2) {
2177  $line->multicurrency_subprice = 0;
2178  }
2179 
2180  switch ($this->element) {
2181  case 'propal':
2182  $this->updateline(
2183  $line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
2184  ($line->description ? $line->description : $line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
2185  $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start,
2186  $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2187  );
2188  break;
2189  case 'commande':
2190  $this->updateline(
2191  $line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->qty, $line->remise_percent,
2192  $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->date_start, $line->date_end,
2193  $line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
2194  $line->special_code, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
2195  );
2196  break;
2197  case 'facture':
2198  $this->updateline(
2199  $line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->qty, $line->remise_percent,
2200  $line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits,
2201  $line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
2202  $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice
2203  );
2204  break;
2205  case 'supplier_proposal':
2206  $this->updateline(
2207  $line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
2208  ($line->description ? $line->description : $line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
2209  $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->array_options,
2210  $line->ref_fourn, $line->multicurrency_subprice
2211  );
2212  break;
2213  case 'order_supplier':
2214  $this->updateline(
2215  $line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->qty, $line->remise_percent,
2216  $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, false,
2217  $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice,
2218  $line->ref_supplier
2219  );
2220  break;
2221  case 'invoice_supplier':
2222  $this->updateline(
2223  $line->id, ($line->description ? $line->description : $line->desc), $line->subprice, $line->tva_tx, $line->localtax1_tx,
2224  $line->localtax2_tx, $line->qty, 0, 'HT', $line->info_bits, $line->product_type, $line->remise_percent, false,
2225  $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice,
2226  $line->ref_supplier
2227  );
2228  break;
2229  default:
2230  dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
2231  break;
2232  }
2233  }
2234  }
2235 
2236  return 1;
2237  } else {
2238  dol_syslog(get_class($this).'::setMulticurrencyRate Error '.$sql.' - '.$this->db->error());
2239  $this->error = $this->db->error();
2240  return -1;
2241  }
2242  } else {
2243  dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
2244  $this->error = 'Status of the object is incompatible '.$this->statut;
2245  return -2;
2246  }
2247  }
2248 
2255  public function setPaymentTerms($id)
2256  {
2257  dol_syslog(get_class($this).'::setPaymentTerms('.$id.')');
2258  if ($this->statut >= 0 || $this->element == 'societe')
2259  {
2260  // TODO uniformize field name
2261  $fieldname = 'fk_cond_reglement';
2262  if ($this->element == 'societe') $fieldname = 'cond_reglement';
2263  if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier';
2264 
2265  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2266  $sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
2267  $sql .= ' WHERE rowid='.$this->id;
2268 
2269  if ($this->db->query($sql))
2270  {
2271  $this->cond_reglement_id = $id;
2272  // for supplier
2273  if (get_class($this) == 'Fournisseur') $this->cond_reglement_supplier_id = $id;
2274  $this->cond_reglement = $id; // for compatibility
2275  return 1;
2276  } else {
2277  dol_syslog(get_class($this).'::setPaymentTerms Error '.$sql.' - '.$this->db->error());
2278  $this->error = $this->db->error();
2279  return -1;
2280  }
2281  } else {
2282  dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
2283  $this->error = 'Status of the object is incompatible '.$this->statut;
2284  return -2;
2285  }
2286  }
2287 
2294  public function setTransportMode($id)
2295  {
2296  dol_syslog(get_class($this).'::setTransportMode('.$id.')');
2297  if ($this->statut >= 0 || $this->element == 'societe')
2298  {
2299  $fieldname = 'fk_transport_mode';
2300  if ($this->element == 'societe') $fieldname = 'transport_mode';
2301  if (get_class($this) == 'Fournisseur') $fieldname = 'transport_mode_supplier';
2302 
2303  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2304  $sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
2305  $sql .= ' WHERE rowid='.$this->id;
2306 
2307  if ($this->db->query($sql))
2308  {
2309  $this->transport_mode_id = $id;
2310  // for supplier
2311  if (get_class($this) == 'Fournisseur') $this->transport_mode_supplier_id = $id;
2312  return 1;
2313  } else {
2314  dol_syslog(get_class($this).'::setTransportMode Error '.$sql.' - '.$this->db->error());
2315  $this->error = $this->db->error();
2316  return -1;
2317  }
2318  } else {
2319  dol_syslog(get_class($this).'::setTransportMode, status of the object is incompatible');
2320  $this->error = 'Status of the object is incompatible '.$this->statut;
2321  return -2;
2322  }
2323  }
2324 
2331  public function setRetainedWarrantyPaymentTerms($id)
2332  {
2333  dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms('.$id.')');
2334  if ($this->statut >= 0 || $this->element == 'societe')
2335  {
2336  $fieldname = 'retained_warranty_fk_cond_reglement';
2337 
2338  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2339  $sql .= ' SET '.$fieldname.' = '.$id;
2340  $sql .= ' WHERE rowid='.$this->id;
2341 
2342  if ($this->db->query($sql))
2343  {
2344  $this->retained_warranty_fk_cond_reglement = $id;
2345  return 1;
2346  } else {
2347  dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms Error '.$sql.' - '.$this->db->error());
2348  $this->error = $this->db->error();
2349  return -1;
2350  }
2351  } else {
2352  dol_syslog(get_class($this).'::setRetainedWarrantyPaymentTerms, status of the object is incompatible');
2353  $this->error = 'Status of the object is incompatible '.$this->statut;
2354  return -2;
2355  }
2356  }
2357 
2365  public function setDeliveryAddress($id)
2366  {
2367  $fieldname = 'fk_delivery_address';
2368  if ($this->element == 'delivery' || $this->element == 'shipping') $fieldname = 'fk_address';
2369 
2370  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ".$fieldname." = ".$id;
2371  $sql .= " WHERE rowid = ".$this->id." AND fk_statut = 0";
2372 
2373  if ($this->db->query($sql))
2374  {
2375  $this->fk_delivery_address = $id;
2376  return 1;
2377  } else {
2378  $this->error = $this->db->error();
2379  dol_syslog(get_class($this).'::setDeliveryAddress Error '.$sql.' - '.$this->error);
2380  return -1;
2381  }
2382  }
2383 
2384 
2394  public function setShippingMethod($shipping_method_id, $notrigger = false, $userused = null)
2395  {
2396  global $user;
2397 
2398  if (empty($userused)) $userused = $user;
2399 
2400  $error = 0;
2401 
2402  if (!$this->table_element) {
2403  dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined", LOG_ERR);
2404  return -1;
2405  }
2406 
2407  $this->db->begin();
2408 
2409  if ($shipping_method_id < 0) $shipping_method_id = 'NULL';
2410  dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
2411 
2412  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2413  $sql .= " SET fk_shipping_method = ".$shipping_method_id;
2414  $sql .= " WHERE rowid=".$this->id;
2415  $resql = $this->db->query($sql);
2416  if (!$resql) {
2417  dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
2418  $this->error = $this->db->lasterror();
2419  $error++;
2420  } else {
2421  if (!$notrigger)
2422  {
2423  // Call trigger
2424  $this->context = array('shippingmethodupdate'=>1);
2425  $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused);
2426  if ($result < 0) $error++;
2427  // End call trigger
2428  }
2429  }
2430  if ($error)
2431  {
2432  $this->db->rollback();
2433  return -1;
2434  } else {
2435  $this->shipping_method_id = ($shipping_method_id == 'NULL') ?null:$shipping_method_id;
2436  $this->db->commit();
2437  return 1;
2438  }
2439  }
2440 
2441 
2448  public function setWarehouse($warehouse_id)
2449  {
2450  if (!$this->table_element) {
2451  dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined", LOG_ERR);
2452  return -1;
2453  }
2454  if ($warehouse_id < 0) $warehouse_id = 'NULL';
2455  dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
2456 
2457  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2458  $sql .= " SET fk_warehouse = ".$warehouse_id;
2459  $sql .= " WHERE rowid=".$this->id;
2460 
2461  if ($this->db->query($sql)) {
2462  $this->warehouse_id = ($warehouse_id == 'NULL') ?null:$warehouse_id;
2463  return 1;
2464  } else {
2465  dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
2466  $this->error = $this->db->error();
2467  return 0;
2468  }
2469  }
2470 
2471 
2479  public function setDocModel($user, $modelpdf)
2480  {
2481  if (!$this->table_element)
2482  {
2483  dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined", LOG_ERR);
2484  return -1;
2485  }
2486 
2487  $newmodelpdf = dol_trunc($modelpdf, 255);
2488 
2489  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2490  $sql .= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
2491  $sql .= " WHERE rowid = ".$this->id;
2492 
2493  dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
2494  $resql = $this->db->query($sql);
2495  if ($resql)
2496  {
2497  $this->model_pdf = $modelpdf;
2498  $this->modelpdf = $modelpdf; // For bakward compatibility
2499  return 1;
2500  } else {
2501  dol_print_error($this->db);
2502  return 0;
2503  }
2504  }
2505 
2506 
2515  public function setBankAccount($fk_account, $notrigger = false, $userused = null)
2516  {
2517  global $user;
2518 
2519  if (empty($userused)) $userused = $user;
2520 
2521  $error = 0;
2522 
2523  if (!$this->table_element) {
2524  dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined", LOG_ERR);
2525  return -1;
2526  }
2527  $this->db->begin();
2528 
2529  if ($fk_account < 0) $fk_account = 'NULL';
2530  dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
2531 
2532  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2533  $sql .= " SET fk_account = ".$fk_account;
2534  $sql .= " WHERE rowid=".$this->id;
2535 
2536  $resql = $this->db->query($sql);
2537  if (!$resql)
2538  {
2539  dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
2540  $this->error = $this->db->lasterror();
2541  $error++;
2542  } else {
2543  if (!$notrigger)
2544  {
2545  // Call trigger
2546  $this->context = array('bankaccountupdate'=>1);
2547  $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $userused);
2548  if ($result < 0) $error++;
2549  // End call trigger
2550  }
2551  }
2552  if ($error)
2553  {
2554  $this->db->rollback();
2555  return -1;
2556  } else {
2557  $this->fk_account = ($fk_account == 'NULL') ?null:$fk_account;
2558  $this->db->commit();
2559  return 1;
2560  }
2561  }
2562 
2563 
2564  // TODO: Move line related operations to CommonObjectLine?
2565 
2566  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2576  public function line_order($renum = false, $rowidorder = 'ASC', $fk_parent_line = true)
2577  {
2578  // phpcs:enable
2579  if (!$this->table_element_line)
2580  {
2581  dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined", LOG_ERR);
2582  return -1;
2583  }
2584  if (!$this->fk_element)
2585  {
2586  dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined", LOG_ERR);
2587  return -1;
2588  }
2589 
2590  // Count number of lines to reorder (according to choice $renum)
2591  $nl = 0;
2592  $sql = 'SELECT count(rowid) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2593  $sql .= ' WHERE '.$this->fk_element.'='.$this->id;
2594  if (!$renum) $sql .= ' AND rang = 0';
2595  if ($renum) $sql .= ' AND rang <> 0';
2596 
2597  dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
2598  $resql = $this->db->query($sql);
2599  if ($resql)
2600  {
2601  $row = $this->db->fetch_row($resql);
2602  $nl = $row[0];
2603  } else dol_print_error($this->db);
2604  if ($nl > 0)
2605  {
2606  // The goal of this part is to reorder all lines, with all children lines sharing the same counter that parents.
2607  $rows = array();
2608 
2609  // We first search all lines that are parent lines (for multilevel details lines)
2610  $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2611  $sql .= ' WHERE '.$this->fk_element.' = '.$this->id;
2612  if ($fk_parent_line) $sql .= ' AND fk_parent_line IS NULL';
2613  $sql .= ' ORDER BY rang ASC, rowid '.$rowidorder;
2614 
2615  dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
2616  $resql = $this->db->query($sql);
2617  if ($resql)
2618  {
2619  $i = 0;
2620  $num = $this->db->num_rows($resql);
2621  while ($i < $num)
2622  {
2623  $row = $this->db->fetch_row($resql);
2624  $rows[] = $row[0]; // Add parent line into array rows
2625  $childrens = $this->getChildrenOfLine($row[0]);
2626  if (!empty($childrens))
2627  {
2628  foreach ($childrens as $child)
2629  {
2630  array_push($rows, $child);
2631  }
2632  }
2633  $i++;
2634  }
2635 
2636  // Now we set a new number for each lines (parent and children with children included into parent tree)
2637  if (!empty($rows))
2638  {
2639  foreach ($rows as $key => $row)
2640  {
2641  $this->updateRangOfLine($row, ($key + 1));
2642  }
2643  }
2644  } else {
2645  dol_print_error($this->db);
2646  }
2647  }
2648  return 1;
2649  }
2650 
2658  public function getChildrenOfLine($id, $includealltree = 0)
2659  {
2660  $rows = array();
2661 
2662  $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2663  $sql .= ' WHERE '.$this->fk_element.' = '.$this->id;
2664  $sql .= ' AND fk_parent_line = '.$id;
2665  $sql .= ' ORDER BY rang ASC';
2666 
2667  dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id."", LOG_DEBUG);
2668  $resql = $this->db->query($sql);
2669  if ($resql)
2670  {
2671  if ($this->db->num_rows($resql) > 0) {
2672  while ($row = $this->db->fetch_row($resql)) {
2673  $rows[] = $row[0];
2674  if (!empty($includealltree)) $rows = array_merge($rows, $this->getChildrenOfLine($row[0]), $includealltree);
2675  }
2676  }
2677  }
2678  return $rows;
2679  }
2680 
2681  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2689  public function line_up($rowid, $fk_parent_line = true)
2690  {
2691  // phpcs:enable
2692  $this->line_order(false, 'ASC', $fk_parent_line);
2693 
2694  // Get rang of line
2695  $rang = $this->getRangOfLine($rowid);
2696 
2697  // Update position of line
2698  $this->updateLineUp($rowid, $rang);
2699  }
2700 
2701  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2709  public function line_down($rowid, $fk_parent_line = true)
2710  {
2711  // phpcs:enable
2712  $this->line_order(false, 'ASC', $fk_parent_line);
2713 
2714  // Get rang of line
2715  $rang = $this->getRangOfLine($rowid);
2716 
2717  // Get max value for rang
2718  $max = $this->line_max();
2719 
2720  // Update position of line
2721  $this->updateLineDown($rowid, $rang, $max);
2722  }
2723 
2731  public function updateRangOfLine($rowid, $rang)
2732  {
2733  $fieldposition = 'rang'; // @todo Rename 'rang' into 'position'
2734  if (in_array($this->table_element_line, array('bom_bomline', 'ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2735 
2736  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2737  $sql .= ' WHERE rowid = '.$rowid;
2738 
2739  dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
2740  if (!$this->db->query($sql))
2741  {
2742  dol_print_error($this->db);
2743  }
2744  }
2745 
2746  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2753  public function line_ajaxorder($rows)
2754  {
2755  // phpcs:enable
2756  $num = count($rows);
2757  for ($i = 0; $i < $num; $i++)
2758  {
2759  $this->updateRangOfLine($rows[$i], ($i + 1));
2760  }
2761  }
2762 
2770  public function updateLineUp($rowid, $rang)
2771  {
2772  if ($rang > 1)
2773  {
2774  $fieldposition = 'rang';
2775  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2776 
2777  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2778  $sql .= ' WHERE '.$this->fk_element.' = '.$this->id;
2779  $sql .= ' AND rang = '.($rang - 1);
2780  if ($this->db->query($sql))
2781  {
2782  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang - 1);
2783  $sql .= ' WHERE rowid = '.$rowid;
2784  if (!$this->db->query($sql))
2785  {
2786  dol_print_error($this->db);
2787  }
2788  } else {
2789  dol_print_error($this->db);
2790  }
2791  }
2792  }
2793 
2802  public function updateLineDown($rowid, $rang, $max)
2803  {
2804  if ($rang < $max)
2805  {
2806  $fieldposition = 'rang';
2807  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2808 
2809  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2810  $sql .= ' WHERE '.$this->fk_element.' = '.$this->id;
2811  $sql .= ' AND rang = '.($rang + 1);
2812  if ($this->db->query($sql))
2813  {
2814  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang + 1);
2815  $sql .= ' WHERE rowid = '.$rowid;
2816  if (!$this->db->query($sql))
2817  {
2818  dol_print_error($this->db);
2819  }
2820  } else {
2821  dol_print_error($this->db);
2822  }
2823  }
2824  }
2825 
2832  public function getRangOfLine($rowid)
2833  {
2834  $sql = 'SELECT rang FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2835  $sql .= ' WHERE rowid ='.$rowid;
2836 
2837  dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
2838  $resql = $this->db->query($sql);
2839  if ($resql)
2840  {
2841  $row = $this->db->fetch_row($resql);
2842  return $row[0];
2843  }
2844  }
2845 
2852  public function getIdOfLine($rang)
2853  {
2854  $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2855  $sql .= ' WHERE '.$this->fk_element.' = '.$this->id;
2856  $sql .= ' AND rang = '.$rang;
2857  $resql = $this->db->query($sql);
2858  if ($resql)
2859  {
2860  $row = $this->db->fetch_row($resql);
2861  return $row[0];
2862  }
2863  }
2864 
2865  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2872  public function line_max($fk_parent_line = 0)
2873  {
2874  // phpcs:enable
2875  $positionfield = 'rang';
2876  if ($this->table_element == 'bom_bom') $positionfield = 'position';
2877 
2878  // Search the last rang with fk_parent_line
2879  if ($fk_parent_line)
2880  {
2881  $sql = 'SELECT max('.$positionfield.') FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2882  $sql .= ' WHERE '.$this->fk_element.' = '.$this->id;
2883  $sql .= ' AND fk_parent_line = '.$fk_parent_line;
2884 
2885  dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2886  $resql = $this->db->query($sql);
2887  if ($resql)
2888  {
2889  $row = $this->db->fetch_row($resql);
2890  if (!empty($row[0]))
2891  {
2892  return $row[0];
2893  } else {
2894  return $this->getRangOfLine($fk_parent_line);
2895  }
2896  }
2897  }
2898  // If not, search the last rang of element
2899  else {
2900  $sql = 'SELECT max('.$positionfield.') FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2901  $sql .= ' WHERE '.$this->fk_element.' = '.$this->id;
2902 
2903  dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2904  $resql = $this->db->query($sql);
2905  if ($resql)
2906  {
2907  $row = $this->db->fetch_row($resql);
2908  return $row[0];
2909  }
2910  }
2911  }
2912 
2913  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2920  public function update_ref_ext($ref_ext)
2921  {
2922  // phpcs:enable
2923  if (!$this->table_element)
2924  {
2925  dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
2926  return -1;
2927  }
2928 
2929  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2930  $sql .= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
2931  $sql .= " WHERE ".(isset($this->table_rowid) ? $this->table_rowid : 'rowid')." = ".$this->id;
2932 
2933  dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
2934  if ($this->db->query($sql))
2935  {
2936  $this->ref_ext = $ref_ext;
2937  return 1;
2938  } else {
2939  $this->error = $this->db->error();
2940  return -1;
2941  }
2942  }
2943 
2944  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2952  public function update_note($note, $suffix = '')
2953  {
2954  // phpcs:enable
2955  global $user;
2956 
2957  if (!$this->table_element)
2958  {
2959  $this->error = 'update_note was called on objet with property table_element not defined';
2960  dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
2961  return -1;
2962  }
2963  if (!in_array($suffix, array('', '_public', '_private')))
2964  {
2965  $this->error = 'update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
2966  dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
2967  return -2;
2968  }
2969 
2970  $newsuffix = $suffix;
2971 
2972  // Special cas
2973  if ($this->table_element == 'product' && $newsuffix == '_private') $newsuffix = '';
2974 
2975  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2976  $sql .= " SET note".$newsuffix." = ".(!empty($note) ? ("'".$this->db->escape($note)."'") : "NULL");
2977  $sql .= " ,".(in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment')) ? "fk_user_mod" : "fk_user_modif")." = ".$user->id;
2978  $sql .= " WHERE rowid =".$this->id;
2979 
2980  dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
2981  if ($this->db->query($sql))
2982  {
2983  if ($suffix == '_public') $this->note_public = $note;
2984  elseif ($suffix == '_private') $this->note_private = $note;
2985  else {
2986  $this->note = $note; // deprecated
2987  $this->note_private = $note;
2988  }
2989  return 1;
2990  } else {
2991  $this->error = $this->db->lasterror();
2992  return -1;
2993  }
2994  }
2995 
2996  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3005  public function update_note_public($note)
3006  {
3007  // phpcs:enable
3008  return $this->update_note($note, '_public');
3009  }
3010 
3011  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3022  public function update_price($exclspec = 0, $roundingadjust = 'none', $nodatabaseupdate = 0, $seller = null)
3023  {
3024  // phpcs:enable
3025  global $conf, $hookmanager, $action;
3026 
3027  $parameters = array('exclspec' => $exclspec, 'roundingadjust' => $roundingadjust, 'nodatabaseupdate' => $nodatabaseupdate, 'seller' => $seller);
3028  $reshook = $hookmanager->executeHooks('updateTotalPrice', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3029  if ($reshook > 0) {
3030  return 1; // replacement code
3031  } elseif ($reshook < 0) {
3032  return -1; // failure
3033  } // reshook = 0 => execute normal code
3034 
3035  // Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
3036  $MODULE = "";
3037  if ($this->element == 'propal')
3038  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
3039  elseif ($this->element == 'commande' || $this->element == 'order')
3040  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
3041  elseif ($this->element == 'facture' || $this->element == 'invoice')
3042  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
3043  elseif ($this->element == 'facture_fourn' || $this->element == 'supplier_invoice' || $this->element == 'invoice_supplier')
3044  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
3045  elseif ($this->element == 'order_supplier' || $this->element == 'supplier_order')
3046  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
3047  elseif ($this->element == 'supplier_proposal')
3048  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
3049 
3050  if (!empty($MODULE)) {
3051  if (!empty($conf->global->$MODULE)) {
3052  $modsactivated = explode(',', $conf->global->$MODULE);
3053  foreach ($modsactivated as $mod) {
3054  if ($conf->$mod->enabled)
3055  return 1; // update was disabled by specific setup
3056  }
3057  }
3058  }
3059 
3060  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
3061 
3062  if ($roundingadjust == '-1') $roundingadjust = 'auto'; // For backward compatibility
3063 
3064  $forcedroundingmode = $roundingadjust;
3065  if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) $forcedroundingmode = $conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND;
3066  elseif ($forcedroundingmode == 'auto') $forcedroundingmode = '0';
3067 
3068  $error = 0;
3069 
3070  $multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
3071 
3072  // Define constants to find lines to sum
3073  $fieldtva = 'total_tva';
3074  $fieldlocaltax1 = 'total_localtax1';
3075  $fieldlocaltax2 = 'total_localtax2';
3076  $fieldup = 'subprice';
3077  if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
3078  {
3079  $fieldtva = 'tva';
3080  $fieldup = 'pu_ht';
3081  }
3082  if ($this->element == 'expensereport')
3083  {
3084  $fieldup = 'value_unit';
3085  }
3086 
3087  $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,';
3088  $sql .= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
3089  if ($this->table_element_line == 'facturedet') $sql .= ', situation_percent';
3090  $sql .= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
3091  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line;
3092  $sql .= ' WHERE '.$this->fk_element.' = '.$this->id;
3093  if ($exclspec)
3094  {
3095  $product_field = 'product_type';
3096  if ($this->table_element_line == 'contratdet') $product_field = ''; // contratdet table has no product_type field
3097  if ($product_field) $sql .= ' AND '.$product_field.' <> 9';
3098  }
3099  $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
3100 
3101  dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3102  $resql = $this->db->query($sql);
3103  if ($resql)
3104  {
3105  $this->total_ht = 0;
3106  $this->total_tva = 0;
3107  $this->total_localtax1 = 0;
3108  $this->total_localtax2 = 0;
3109  $this->total_ttc = 0;
3110  $total_ht_by_vats = array();
3111  $total_tva_by_vats = array();
3112  $total_ttc_by_vats = array();
3113  $this->multicurrency_total_ht = 0;
3114  $this->multicurrency_total_tva = 0;
3115  $this->multicurrency_total_ttc = 0;
3116 
3117  $num = $this->db->num_rows($resql);
3118  $i = 0;
3119  while ($i < $num)
3120  {
3121  $obj = $this->db->fetch_object($resql);
3122 
3123  // Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
3124  $parameters = array('fk_element' => $obj->rowid);
3125  $reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3126 
3127  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'
3128  {
3129  // This part of code is to fix data. We should not call it too often.
3130  $localtax_array = array($obj->localtax1_type, $obj->localtax1_tx, $obj->localtax2_type, $obj->localtax2_tx);
3131  $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);
3132 
3133  $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.
3134  $diff_on_current_total = price2num($obj->total_ttc - $obj->total_ht - $obj->total_tva - $obj->total_localtax1 - $obj->total_localtax2, 'MT', 1);
3135  //var_dump($obj->total_ht.' '.$obj->total_tva.' '.$obj->total_localtax1.' '.$obj->total_localtax2.' =? '.$obj->total_ttc);
3136  //var_dump($diff_when_using_price_ht.' '.$diff_on_current_total);
3137 
3138  if ($diff_when_using_price_ht && $diff_on_current_total)
3139  {
3140  $sqlfix = "UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".$tmpcal[1].", total_ttc = ".$tmpcal[2]." WHERE rowid = ".$obj->rowid;
3141  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);
3142  $resqlfix = $this->db->query($sqlfix);
3143  if (!$resqlfix) dol_print_error($this->db, 'Failed to update line');
3144  $obj->total_tva = $tmpcal[1];
3145  $obj->total_ttc = $tmpcal[2];
3146  }
3147  }
3148 
3149  $this->total_ht += $obj->total_ht; // The field visible at end of line detail
3150  $this->total_tva += $obj->total_tva;
3151  $this->total_localtax1 += $obj->total_localtax1;
3152  $this->total_localtax2 += $obj->total_localtax2;
3153  $this->total_ttc += $obj->total_ttc;
3154  $this->multicurrency_total_ht += $obj->multicurrency_total_ht; // The field visible at end of line detail
3155  $this->multicurrency_total_tva += $obj->multicurrency_total_tva;
3156  $this->multicurrency_total_ttc += $obj->multicurrency_total_ttc;
3157 
3158  if (!isset($total_ht_by_vats[$obj->vatrate])) $total_ht_by_vats[$obj->vatrate] = 0;
3159  if (!isset($total_tva_by_vats[$obj->vatrate])) $total_tva_by_vats[$obj->vatrate] = 0;
3160  if (!isset($total_ttc_by_vats[$obj->vatrate])) $total_ttc_by_vats[$obj->vatrate] = 0;
3161  $total_ht_by_vats[$obj->vatrate] += $obj->total_ht;
3162  $total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
3163  $total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
3164 
3165  if ($forcedroundingmode == '1') // Check if we need adjustement onto line for vat. TODO This works on the company currency but not on multicurrency
3166  {
3167  $tmpvat = price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
3168  $diff = price2num($total_tva_by_vats[$obj->vatrate] - $tmpvat, 'MT', 1);
3169  //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";
3170  if ($diff)
3171  {
3172  if (abs($diff) > 0.1) {
3173  $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.';
3174  dol_syslog($errmsg, LOG_WARNING);
3175  dol_print_error('', $errmsg);
3176  exit;
3177  }
3178  $sqlfix = "UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".($obj->total_tva - $diff).", total_ttc = ".($obj->total_ttc - $diff)." WHERE rowid = ".$obj->rowid;
3179  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);
3180  $resqlfix = $this->db->query($sqlfix);
3181  if (!$resqlfix) dol_print_error($this->db, 'Failed to update line');
3182  $this->total_tva -= $diff;
3183  $this->total_ttc -= $diff;
3184  $total_tva_by_vats[$obj->vatrate] -= $diff;
3185  $total_ttc_by_vats[$obj->vatrate] -= $diff;
3186  }
3187  }
3188 
3189  $i++;
3190  }
3191 
3192  // Add revenue stamp to total
3193  $this->total_ttc += isset($this->revenuestamp) ? $this->revenuestamp : 0;
3194  $this->multicurrency_total_ttc += isset($this->revenuestamp) ? ($this->revenuestamp * $multicurrency_tx) : 0;
3195 
3196  // Situations totals
3197  if (!empty($this->situation_cycle_ref) && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits') && $this->type != $this::TYPE_CREDIT_NOTE)
3198  {
3199  $prev_sits = $this->get_prev_sits();
3200 
3201  foreach ($prev_sits as $sit) { // $sit is an object Facture loaded with a fetch.
3202  $this->total_ht -= $sit->total_ht;
3203  $this->total_tva -= $sit->total_tva;
3204  $this->total_localtax1 -= $sit->total_localtax1;
3205  $this->total_localtax2 -= $sit->total_localtax2;
3206  $this->total_ttc -= $sit->total_ttc;
3207  $this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
3208  $this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
3209  $this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
3210  }
3211  }
3212 
3213  $this->db->free($resql);
3214 
3215  // Now update global field total_ht, total_ttc and tva
3216  $fieldht = 'total_ht';
3217  $fieldtva = 'tva';
3218  $fieldlocaltax1 = 'localtax1';
3219  $fieldlocaltax2 = 'localtax2';
3220  $fieldttc = 'total_ttc';
3221  // Specific code for backward compatibility with old field names
3222  if ($this->element == 'facture' || $this->element == 'facturerec') $fieldht = 'total';
3223  if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') $fieldtva = 'total_tva';
3224  if ($this->element == 'propal') $fieldttc = 'total';
3225  if ($this->element == 'expensereport') $fieldtva = 'total_tva';
3226  if ($this->element == 'supplier_proposal') $fieldttc = 'total';
3227 
3228  if (empty($nodatabaseupdate))
3229  {
3230  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
3231  $sql .= " ".$fieldht."='".price2num($this->total_ht)."',";
3232  $sql .= " ".$fieldtva."='".price2num($this->total_tva)."',";
3233  $sql .= " ".$fieldlocaltax1."='".price2num($this->total_localtax1)."',";
3234  $sql .= " ".$fieldlocaltax2."='".price2num($this->total_localtax2)."',";
3235  $sql .= " ".$fieldttc."='".price2num($this->total_ttc)."'";
3236  $sql .= ", multicurrency_total_ht='".price2num($this->multicurrency_total_ht, 'MT', 1)."'";
3237  $sql .= ", multicurrency_total_tva='".price2num($this->multicurrency_total_tva, 'MT', 1)."'";
3238  $sql .= ", multicurrency_total_ttc='".price2num($this->multicurrency_total_ttc, 'MT', 1)."'";
3239  $sql .= ' WHERE rowid = '.$this->id;
3240 
3241 
3242  dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
3243  $resql = $this->db->query($sql);
3244  if (!$resql)
3245  {
3246  $error++;
3247  $this->error = $this->db->lasterror();
3248  $this->errors[] = $this->db->lasterror();
3249  }
3250  }
3251 
3252  if (!$error)
3253  {
3254  return 1;
3255  } else {
3256  return -1;
3257  }
3258  } else {
3259  dol_print_error($this->db, 'Bad request in update_price');
3260  return -1;
3261  }
3262  }
3263 
3264  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3273  public function add_object_linked($origin = null, $origin_id = null)
3274  {
3275  // phpcs:enable
3276  $origin = (!empty($origin) ? $origin : $this->origin);
3277  $origin_id = (!empty($origin_id) ? $origin_id : $this->origin_id);
3278 
3279  // Special case
3280  if ($origin == 'order') $origin = 'commande';
3281  if ($origin == 'invoice') $origin = 'facture';
3282  if ($origin == 'invoice_template') $origin = 'facturerec';
3283  if ($origin == 'supplierorder') $origin = 'order_supplier';
3284  $this->db->begin();
3285 
3286  $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element (";
3287  $sql .= "fk_source";
3288  $sql .= ", sourcetype";
3289  $sql .= ", fk_target";
3290  $sql .= ", targettype";
3291  $sql .= ") VALUES (";
3292  $sql .= $origin_id;
3293  $sql .= ", '".$this->db->escape($origin)."'";
3294  $sql .= ", ".$this->id;
3295  $sql .= ", '".$this->db->escape($this->element)."'";
3296  $sql .= ")";
3297 
3298  dol_syslog(get_class($this)."::add_object_linked", LOG_DEBUG);
3299  if ($this->db->query($sql))
3300  {
3301  $this->db->commit();
3302  return 1;
3303  } else {
3304  $this->error = $this->db->lasterror();
3305  $this->db->rollback();
3306  return 0;
3307  }
3308  }
3309 
3332  public function fetchObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $clause = 'OR', $alsosametype = 1, $orderby = 'sourcetype', $loadalsoobjects = 1)
3333  {
3334  global $conf;
3335 
3336  $this->linkedObjectsIds = array();
3337  $this->linkedObjects = array();
3338 
3339  $justsource = false;
3340  $justtarget = false;
3341  $withtargettype = false;
3342  $withsourcetype = false;
3343 
3344  if (!empty($sourceid) && !empty($sourcetype) && empty($targetid))
3345  {
3346  $justsource = true; // the source (id and type) is a search criteria
3347  if (!empty($targettype)) $withtargettype = true;
3348  }
3349  if (!empty($targetid) && !empty($targettype) && empty($sourceid))
3350  {
3351  $justtarget = true; // the target (id and type) is a search criteria
3352  if (!empty($sourcetype)) $withsourcetype = true;
3353  }
3354 
3355  $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
3356  $targetid = (!empty($targetid) ? $targetid : $this->id);
3357  $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
3358  $targettype = (!empty($targettype) ? $targettype : $this->element);
3359 
3360  /*if (empty($sourceid) && empty($targetid))
3361  {
3362  dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
3363  return -1;
3364  }*/
3365 
3366  // Links between objects are stored in table element_element
3367  $sql = 'SELECT rowid, fk_source, sourcetype, fk_target, targettype';
3368  $sql .= ' FROM '.MAIN_DB_PREFIX.'element_element';
3369  $sql .= " WHERE ";
3370  if ($justsource || $justtarget)
3371  {
3372  if ($justsource)
3373  {
3374  $sql .= "fk_source = ".$sourceid." AND sourcetype = '".$this->db->escape($sourcetype)."'";
3375  if ($withtargettype) $sql .= " AND targettype = '".$this->db->escape($targettype)."'";
3376  } elseif ($justtarget)
3377  {
3378  $sql .= "fk_target = ".$targetid." AND targettype = '".$this->db->escape($targettype)."'";
3379  if ($withsourcetype) $sql .= " AND sourcetype = '".$this->db->escape($sourcetype)."'";
3380  }
3381  } else {
3382  $sql .= "(fk_source = ".$sourceid." AND sourcetype = '".$this->db->escape($sourcetype)."')";
3383  $sql .= " ".$clause." (fk_target = ".$targetid." AND targettype = '".$this->db->escape($targettype)."')";
3384  }
3385  $sql .= ' ORDER BY '.$orderby;
3386 
3387  dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
3388  $resql = $this->db->query($sql);
3389  if ($resql)
3390  {
3391  $num = $this->db->num_rows($resql);
3392  $i = 0;
3393  while ($i < $num)
3394  {
3395  $obj = $this->db->fetch_object($resql);
3396  if ($justsource || $justtarget)
3397  {
3398  if ($justsource)
3399  {
3400  $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
3401  } elseif ($justtarget)
3402  {
3403  $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
3404  }
3405  } else {
3406  if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype)
3407  {
3408  $this->linkedObjectsIds[$obj->targettype][$obj->rowid] = $obj->fk_target;
3409  }
3410  if ($obj->fk_target == $targetid && $obj->targettype == $targettype)
3411  {
3412  $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid] = $obj->fk_source;
3413  }
3414  }
3415  $i++;
3416  }
3417 
3418  if (!empty($this->linkedObjectsIds))
3419  {
3420  $tmparray = $this->linkedObjectsIds;
3421  foreach ($tmparray as $objecttype => $objectids) // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
3422  {
3423  // Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
3424  $module = $element = $subelement = $objecttype;
3425  $regs = array();
3426  if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
3427  && preg_match('/^([^_]+)_([^_]+)/i', $objecttype, $regs))
3428  {
3429  $module = $element = $regs[1];
3430  $subelement = $regs[2];
3431  }
3432 
3433  $classpath = $element.'/class';
3434  // To work with non standard classpath or module name
3435  if ($objecttype == 'facture') {
3436  $classpath = 'compta/facture/class';
3437  } elseif ($objecttype == 'facturerec') {
3438  $classpath = 'compta/facture/class'; $module = 'facture';
3439  } elseif ($objecttype == 'propal') {
3440  $classpath = 'comm/propal/class';
3441  } elseif ($objecttype == 'supplier_proposal') {
3442  $classpath = 'supplier_proposal/class';
3443  } elseif ($objecttype == 'shipping') {
3444  $classpath = 'expedition/class'; $subelement = 'expedition'; $module = 'expedition_bon';
3445  } elseif ($objecttype == 'delivery') {
3446  $classpath = 'delivery/class'; $subelement = 'delivery'; $module = 'delivery_note';
3447  } elseif ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier') {
3448  $classpath = 'fourn/class'; $module = 'fournisseur';
3449  } elseif ($objecttype == 'fichinter') {
3450  $classpath = 'fichinter/class'; $subelement = 'fichinter'; $module = 'ficheinter';
3451  } elseif ($objecttype == 'subscription') {
3452  $classpath = 'adherents/class'; $module = 'adherent';
3453  } elseif ($objecttype == 'contact') {
3454  $module = 'societe';
3455  }
3456 
3457  // Set classfile
3458  $classfile = strtolower($subelement); $classname = ucfirst($subelement);
3459 
3460  if ($objecttype == 'order') {
3461  $classfile = 'commande'; $classname = 'Commande';
3462  } elseif ($objecttype == 'invoice_supplier') {
3463  $classfile = 'fournisseur.facture'; $classname = 'FactureFournisseur';
3464  } elseif ($objecttype == 'order_supplier') {
3465  $classfile = 'fournisseur.commande'; $classname = 'CommandeFournisseur';
3466  } elseif ($objecttype == 'supplier_proposal') {
3467  $classfile = 'supplier_proposal'; $classname = 'SupplierProposal';
3468  } elseif ($objecttype == 'facturerec') {
3469  $classfile = 'facture-rec'; $classname = 'FactureRec';
3470  } elseif ($objecttype == 'subscription') {
3471  $classfile = 'subscription'; $classname = 'Subscription';
3472  } elseif ($objecttype == 'project' || $objecttype == 'projet') {
3473  $classpath = 'projet/class'; $classfile = 'project'; $classname = 'Project';
3474  }
3475 
3476  // Here $module, $classfile and $classname are set
3477  if ($conf->$module->enabled && (($element != $this->element) || $alsosametype))
3478  {
3479  if ($loadalsoobjects)
3480  {
3481  dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
3482  //print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
3483  if (class_exists($classname))
3484  {
3485  foreach ($objectids as $i => $objectid) // $i is rowid into llx_element_element
3486  {
3487  $object = new $classname($this->db);
3488  $ret = $object->fetch($objectid);
3489  if ($ret >= 0)
3490  {
3491  $this->linkedObjects[$objecttype][$i] = $object;
3492  }
3493  }
3494  }
3495  }
3496  } else {
3497  unset($this->linkedObjectsIds[$objecttype]);
3498  }
3499  }
3500  }
3501  return 1;
3502  } else {
3503  dol_print_error($this->db);
3504  return -1;
3505  }
3506  }
3507 
3518  public function updateObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '')
3519  {
3520  $updatesource = false;
3521  $updatetarget = false;
3522 
3523  if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) $updatesource = true;
3524  elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) $updatetarget = true;
3525 
3526  $sql = "UPDATE ".MAIN_DB_PREFIX."element_element SET ";
3527  if ($updatesource)
3528  {
3529  $sql .= "fk_source = ".$sourceid;
3530  $sql .= ", sourcetype = '".$this->db->escape($sourcetype)."'";
3531  $sql .= " WHERE fk_target = ".$this->id;
3532  $sql .= " AND targettype = '".$this->db->escape($this->element)."'";
3533  } elseif ($updatetarget)
3534  {
3535  $sql .= "fk_target = ".$targetid;
3536  $sql .= ", targettype = '".$this->db->escape($targettype)."'";
3537  $sql .= " WHERE fk_source = ".$this->id;
3538  $sql .= " AND sourcetype = '".$this->db->escape($this->element)."'";
3539  }
3540 
3541  dol_syslog(get_class($this)."::updateObjectLinked", LOG_DEBUG);
3542  if ($this->db->query($sql))
3543  {
3544  return 1;
3545  } else {
3546  $this->error = $this->db->lasterror();
3547  return -1;
3548  }
3549  }
3550 
3562  public function deleteObjectLinked($sourceid = null, $sourcetype = '', $targetid = null, $targettype = '', $rowid = '')
3563  {
3564  $deletesource = false;
3565  $deletetarget = false;
3566 
3567  if (!empty($sourceid) && !empty($sourcetype) && empty($targetid) && empty($targettype)) $deletesource = true;
3568  elseif (empty($sourceid) && empty($sourcetype) && !empty($targetid) && !empty($targettype)) $deletetarget = true;
3569 
3570  $sourceid = (!empty($sourceid) ? $sourceid : $this->id);
3571  $sourcetype = (!empty($sourcetype) ? $sourcetype : $this->element);
3572  $targetid = (!empty($targetid) ? $targetid : $this->id);
3573  $targettype = (!empty($targettype) ? $targettype : $this->element);
3574 
3575  $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_element";
3576  $sql .= " WHERE";
3577  if ($rowid > 0)
3578  {
3579  $sql .= " rowid = ".$rowid;
3580  } else {
3581  if ($deletesource)
3582  {
3583  $sql .= " fk_source = ".$sourceid." AND sourcetype = '".$this->db->escape($sourcetype)."'";
3584  $sql .= " AND fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."'";
3585  } elseif ($deletetarget)
3586  {
3587  $sql .= " fk_target = ".$targetid." AND targettype = '".$this->db->escape($targettype)."'";
3588  $sql .= " AND fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."'";
3589  } else {
3590  $sql .= " (fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."')";
3591  $sql .= " OR";
3592  $sql .= " (fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."')";
3593  }
3594  }
3595 
3596  dol_syslog(get_class($this)."::deleteObjectLinked", LOG_DEBUG);
3597  if ($this->db->query($sql))
3598  {
3599  return 1;
3600  } else {
3601  $this->error = $this->db->lasterror();
3602  $this->errors[] = $this->error;
3603  return -1;
3604  }
3605  }
3606 
3616  public function setStatut($status, $elementId = null, $elementType = '', $trigkey = '')
3617  {
3618  global $user, $langs, $conf;
3619 
3620  $savElementId = $elementId; // To be used later to know if we were using the method using the id of this or not.
3621 
3622  $elementId = (!empty($elementId) ? $elementId : $this->id);
3623  $elementTable = (!empty($elementType) ? $elementType : $this->table_element);
3624 
3625  $this->db->begin();
3626 
3627  $fieldstatus = "fk_statut";
3628  if ($elementTable == 'facture_rec') $fieldstatus = "suspended";
3629  if ($elementTable == 'mailing') $fieldstatus = "statut";
3630  if ($elementTable == 'cronjob') $fieldstatus = "status";
3631  if ($elementTable == 'user') $fieldstatus = "statut";
3632  if ($elementTable == 'expensereport') $fieldstatus = "fk_statut";
3633  if ($elementTable == 'commande_fournisseur_dispatch') $fieldstatus = "status";
3634  if (is_array($this->fields) && array_key_exists('status', $this->fields)) $fieldstatus = 'status';
3635 
3636  $sql = "UPDATE ".MAIN_DB_PREFIX.$elementTable;
3637  $sql .= " SET ".$fieldstatus." = ".$status;
3638  // If status = 1 = validated, update also fk_user_valid
3639  if ($status == 1 && $elementTable == 'expensereport') $sql .= ", fk_user_valid = ".$user->id;
3640  $sql .= " WHERE rowid=".$elementId;
3641 
3642  dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
3643  if ($this->db->query($sql))
3644  {
3645  $error = 0;
3646 
3647  // Try autoset of trigkey
3648  if (empty($trigkey))
3649  {
3650  if ($this->element == 'supplier_proposal' && $status == 2) $trigkey = 'SUPPLIER_PROPOSAL_SIGN'; // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
3651  if ($this->element == 'supplier_proposal' && $status == 3) $trigkey = 'SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
3652  if ($this->element == 'supplier_proposal' && $status == 4) $trigkey = 'SUPPLIER_PROPOSAL_CLOSE'; // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
3653  if ($this->element == 'fichinter' && $status == 3) $trigkey = 'FICHINTER_CLASSIFY_DONE';
3654  if ($this->element == 'fichinter' && $status == 2) $trigkey = 'FICHINTER_CLASSIFY_BILLED';
3655  if ($this->element == 'fichinter' && $status == 1) $trigkey = 'FICHINTER_CLASSIFY_UNBILLED';
3656  }
3657 
3658  if ($trigkey)
3659  {
3660  // Call trigger
3661  $result = $this->call_trigger($trigkey, $user);
3662  if ($result < 0) $error++;
3663  // End call triggers
3664  }
3665 
3666  if (!$error)
3667  {
3668  $this->db->commit();
3669 
3670  if (empty($savElementId)) // If the element we update was $this (so $elementId is null)
3671  {
3672  $this->statut = $status;
3673  $this->status = $status;
3674  }
3675 
3676  return 1;
3677  } else {
3678  $this->db->rollback();
3679  dol_syslog(get_class($this)."::setStatut ".$this->error, LOG_ERR);
3680  return -1;
3681  }
3682  } else {
3683  $this->error = $this->db->lasterror();
3684  $this->db->rollback();
3685  return -1;
3686  }
3687  }
3688 
3689 
3697  public function getCanvas($id = 0, $ref = '')
3698  {
3699  global $conf;
3700 
3701  if (empty($id) && empty($ref)) return 0;
3702  if (!empty($conf->global->MAIN_DISABLE_CANVAS)) return 0; // To increase speed. Not enabled by default.
3703 
3704  // Clean parameters
3705  $ref = trim($ref);
3706 
3707  $sql = "SELECT rowid, canvas";
3708  $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element;
3709  $sql .= " WHERE entity IN (".getEntity($this->element).")";
3710  if (!empty($id)) $sql .= " AND rowid = ".$id;
3711  if (!empty($ref)) $sql .= " AND ref = '".$this->db->escape($ref)."'";
3712 
3713  $resql = $this->db->query($sql);
3714  if ($resql)
3715  {
3716  $obj = $this->db->fetch_object($resql);
3717  if ($obj)
3718  {
3719  $this->canvas = $obj->canvas;
3720  return 1;
3721  } else return 0;
3722  } else {
3723  dol_print_error($this->db);
3724  return -1;
3725  }
3726  }
3727 
3728 
3735  public function getSpecialCode($lineid)
3736  {
3737  $sql = 'SELECT special_code FROM '.MAIN_DB_PREFIX.$this->table_element_line;
3738  $sql .= ' WHERE rowid = '.$lineid;
3739  $resql = $this->db->query($sql);
3740  if ($resql)
3741  {
3742  $row = $this->db->fetch_row($resql);
3743  return $row[0];
3744  }
3745  }
3746 
3754  public function isObjectUsed($id = 0)
3755  {
3756  global $langs;
3757 
3758  if (empty($id)) $id = $this->id;
3759 
3760  // Check parameters
3761  if (!isset($this->childtables) || !is_array($this->childtables) || count($this->childtables) == 0)
3762  {
3763  dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
3764  return -1;
3765  }
3766 
3767  $arraytoscan = $this->childtables;
3768  // For backward compatibility, we check if array is old format array('table1', 'table2', ...)
3769  $tmparray = array_keys($this->childtables);
3770  if (is_numeric($tmparray[0]))
3771  {
3772  $arraytoscan = array_flip($this->childtables);
3773  }
3774 
3775  // Test if child exists
3776  $haschild = 0;
3777  foreach ($arraytoscan as $table => $elementname)
3778  {
3779  //print $id.'-'.$table.'-'.$elementname.'<br>';
3780  // Check if third party can be deleted
3781  $sql = "SELECT COUNT(*) as nb from ".MAIN_DB_PREFIX.$table;
3782  $sql .= " WHERE ".$this->fk_element." = ".$id;
3783  $resql = $this->db->query($sql);
3784  if ($resql)
3785  {
3786  $obj = $this->db->fetch_object($resql);
3787  if ($obj->nb > 0)
3788  {
3789  $langs->load("errors");
3790  //print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
3791  $haschild += $obj->nb;
3792  if (is_numeric($elementname)) // old usage
3793  {
3794  $this->errors[] = $langs->trans("ErrorRecordHasAtLeastOneChildOfType", $table);
3795  } else // new usage: $elementname=Translation key
3796  {
3797  $this->errors[] = $langs->trans("ErrorRecordHasAtLeastOneChildOfType", $langs->transnoentitiesnoconv($elementname));
3798  }
3799  break; // We found at least one, we stop here
3800  }
3801  } else {
3802  $this->errors[] = $this->db->lasterror();
3803  return -1;
3804  }
3805  }
3806  if ($haschild > 0)
3807  {
3808  $this->errors[] = "ErrorRecordHasChildren";
3809  return $haschild;
3810  } else return 0;
3811  }
3812 
3819  public function hasProductsOrServices($predefined = -1)
3820  {
3821  $nb = 0;
3822 
3823  foreach ($this->lines as $key => $val)
3824  {
3825  $qualified = 0;
3826  if ($predefined == -1) $qualified = 1;
3827  if ($predefined == 1 && $val->fk_product > 0) $qualified = 1;
3828  if ($predefined == 0 && $val->fk_product <= 0) $qualified = 1;
3829  if ($predefined == 2 && $val->fk_product > 0 && $val->product_type == 0) $qualified = 1;
3830  if ($predefined == 3 && $val->fk_product > 0 && $val->product_type == 1) $qualified = 1;
3831  if ($qualified) $nb++;
3832  }
3833  dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
3834  return $nb;
3835  }
3836 
3842  public function getTotalDiscount()
3843  {
3844  $total_discount = 0.00;
3845 
3846  $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
3847  $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element."det";
3848  $sql .= " WHERE ".$this->fk_element." = ".$this->id;
3849 
3850  dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
3851  $resql = $this->db->query($sql);
3852  if ($resql)
3853  {
3854  $num = $this->db->num_rows($resql);
3855  $i = 0;
3856  while ($i < $num)
3857  {
3858  $obj = $this->db->fetch_object($resql);
3859 
3860  $pu_ht = $obj->pu_ht;
3861  $qty = $obj->qty;
3862  $total_ht = $obj->total_ht;
3863 
3864  $total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
3865  $total_discount += $total_discount_line;
3866 
3867  $i++;
3868  }
3869  }
3870 
3871  //print $total_discount; exit;
3872  return price2num($total_discount);
3873  }
3874 
3875 
3882  public function getTotalWeightVolume()
3883  {
3884  $totalWeight = 0;
3885  $totalVolume = 0;
3886  // defined for shipment only
3887  $totalOrdered = '';
3888  // defined for shipment only
3889  $totalToShip = '';
3890 
3891  foreach ($this->lines as $line)
3892  {
3893  if (isset($line->qty_asked))
3894  {
3895  if (empty($totalOrdered)) $totalOrdered = 0; // Avoid warning because $totalOrdered is ''
3896  $totalOrdered += $line->qty_asked; // defined for shipment only
3897  }
3898  if (isset($line->qty_shipped))
3899  {
3900  if (empty($totalToShip)) $totalToShip = 0; // Avoid warning because $totalToShip is ''
3901  $totalToShip += $line->qty_shipped; // defined for shipment only
3902  } elseif ($line->element == 'commandefournisseurdispatch' && isset($line->qty))
3903  {
3904  if (empty($totalToShip)) $totalToShip = 0;
3905  $totalToShip += $line->qty; // defined for reception only
3906  }
3907 
3908  // Define qty, weight, volume, weight_units, volume_units
3909  if ($this->element == 'shipping') {
3910  // for shipments
3911  $qty = $line->qty_shipped ? $line->qty_shipped : 0;
3912  } else {
3913  $qty = $line->qty ? $line->qty : 0;
3914  }
3915 
3916  $weight = $line->weight ? $line->weight : 0;
3917  ($weight == 0 && !empty($line->product->weight)) ? $weight = $line->product->weight : 0;
3918  $volume = $line->volume ? $line->volume : 0;
3919  ($volume == 0 && !empty($line->product->volume)) ? $volume = $line->product->volume : 0;
3920 
3921  $weight_units = $line->weight_units;
3922  ($weight_units == 0 && !empty($line->product->weight_units)) ? $weight_units = $line->product->weight_units : 0;
3923  $volume_units = $line->volume_units;
3924  ($volume_units == 0 && !empty($line->product->volume_units)) ? $volume_units = $line->product->volume_units : 0;
3925 
3926  $weightUnit = 0;
3927  $volumeUnit = 0;
3928  if (!empty($weight_units)) $weightUnit = $weight_units;
3929  if (!empty($volume_units)) $volumeUnit = $volume_units;
3930 
3931  if (empty($totalWeight)) $totalWeight = 0; // Avoid warning because $totalWeight is ''
3932  if (empty($totalVolume)) $totalVolume = 0; // Avoid warning because $totalVolume is ''
3933 
3934  //var_dump($line->volume_units);
3935  if ($weight_units < 50) // < 50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3936  {
3937  $trueWeightUnit = pow(10, $weightUnit);
3938  $totalWeight += $weight * $qty * $trueWeightUnit;
3939  } else {
3940  if ($weight_units == 99) {
3941  // conversion 1 Pound = 0.45359237 KG
3942  $trueWeightUnit = 0.45359237;
3943  $totalWeight += $weight * $qty * $trueWeightUnit;
3944  } elseif ($weight_units == 98) {
3945  // conversion 1 Ounce = 0.0283495 KG
3946  $trueWeightUnit = 0.0283495;
3947  $totalWeight += $weight * $qty * $trueWeightUnit;
3948  } else {
3949  $totalWeight += $weight * $qty; // This may be wrong if we mix different units
3950  }
3951  }
3952  if ($volume_units < 50) // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3953  {
3954  //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
3955  $trueVolumeUnit = pow(10, $volumeUnit);
3956  //print $line->volume;
3957  $totalVolume += $volume * $qty * $trueVolumeUnit;
3958  } else {
3959  $totalVolume += $volume * $qty; // This may be wrong if we mix different units
3960  }
3961  }
3962 
3963  return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
3964  }
3965 
3966 
3972  public function setExtraParameters()
3973  {
3974  $this->db->begin();
3975 
3976  $extraparams = (!empty($this->extraparams) ? json_encode($this->extraparams) : null);
3977 
3978  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3979  $sql .= " SET extraparams = ".(!empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
3980  $sql .= " WHERE rowid = ".$this->id;
3981 
3982  dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
3983  $resql = $this->db->query($sql);
3984  if (!$resql)
3985  {
3986  $this->error = $this->db->lasterror();
3987  $this->db->rollback();
3988  return -1;
3989  } else {
3990  $this->db->commit();
3991  return 1;
3992  }
3993  }
3994 
3995 
3996  // --------------------
3997  // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
3998  // --------------------
3999 
4000  /* This is to show add lines */
4001 
4011  public function formAddObjectLine($dateSelector, $seller, $buyer, $defaulttpldir = '/core/tpl')
4012  {
4013  global $conf, $user, $langs, $object, $hookmanager, $extrafields;
4014  global $form;
4015 
4016  // Line extrafield
4017  if (!is_object($extrafields))
4018  {
4019  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4020  $extrafields = new ExtraFields($this->db);
4021  }
4022  $extrafields->fetch_name_optionals_label($this->table_element_line);
4023 
4024  // Output template part (modules that overwrite templates must declare this into descriptor)
4025  // Use global variables + $dateSelector + $seller and $buyer
4026  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook 'formAddObjectLine'.
4027  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4028  foreach ($dirtpls as $module => $reldir)
4029  {
4030  if (!empty($module))
4031  {
4032  $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
4033  } else {
4034  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_create.tpl.php';
4035  }
4036 
4037  if (empty($conf->file->strict_mode)) {
4038  $res = @include $tpl;
4039  } else {
4040  $res = include $tpl; // for debug
4041  }
4042  if ($res) break;
4043  }
4044  }
4045 
4046 
4047 
4048  /* This is to show array of line of details */
4049 
4050 
4065  public function printObjectLines($action, $seller, $buyer, $selected = 0, $dateSelector = 0, $defaulttpldir = '/core/tpl')
4066  {
4067  global $conf, $hookmanager, $langs, $user, $form, $extrafields, $object;
4068  // TODO We should not use global var for this
4069  global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
4070 
4071  // Define usemargins
4072  $usemargins = 0;
4073  if (!empty($conf->margin->enabled) && !empty($this->element) && in_array($this->element, array('facture', 'facturerec', 'propal', 'commande'))) $usemargins = 1;
4074 
4075  $num = count($this->lines);
4076 
4077  // Line extrafield
4078  if (!is_object($extrafields))
4079  {
4080  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4081  $extrafields = new ExtraFields($this->db);
4082  }
4083  $extrafields->fetch_name_optionals_label($this->table_element_line);
4084 
4085  $parameters = array('num'=>$num, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$this->table_element_line);
4086  $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4087  if (empty($reshook))
4088  {
4089  // Output template part (modules that overwrite templates must declare this into descriptor)
4090  // Use global variables + $dateSelector + $seller and $buyer
4091  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook.
4092  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4093  foreach ($dirtpls as $module => $reldir)
4094  {
4095  if (!empty($module))
4096  {
4097  $tpl = dol_buildpath($reldir.'/objectline_title.tpl.php');
4098  } else {
4099  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_title.tpl.php';
4100  }
4101  if (empty($conf->file->strict_mode)) {
4102  $res = @include $tpl;
4103  } else {
4104  $res = include $tpl; // for debug
4105  }
4106  if ($res) break;
4107  }
4108  }
4109 
4110  $i = 0;
4111 
4112  print "<!-- begin printObjectLines() --><tbody>\n";
4113  foreach ($this->lines as $line)
4114  {
4115  //Line extrafield
4116  $line->fetch_optionals();
4117 
4118  //if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
4119  if (is_object($hookmanager)) // Old code is commented on preceding line.
4120  {
4121  if (empty($line->fk_parent_line))
4122  {
4123  $parameters = array('line'=>$line, 'num'=>$num, 'i'=>$i, 'dateSelector'=>$dateSelector, 'seller'=>$seller, 'buyer'=>$buyer, 'selected'=>$selected, 'table_element_line'=>$line->table_element);
4124  $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4125  } else {
4126  $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);
4127  $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4128  }
4129  }
4130  if (empty($reshook))
4131  {
4132  $this->printObjectLine($action, $line, '', $num, $i, $dateSelector, $seller, $buyer, $selected, $extrafields, $defaulttpldir);
4133  }
4134 
4135  $i++;
4136  }
4137  print "</tbody><!-- end printObjectLines() -->\n";
4138  }
4139 
4157  public function printObjectLine($action, $line, $var, $num, $i, $dateSelector, $seller, $buyer, $selected = 0, $extrafields = null, $defaulttpldir = '/core/tpl')
4158  {
4159  global $conf, $langs, $user, $object, $hookmanager;
4160  global $form;
4161  global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
4162 
4163  $object_rights = $this->getRights();
4164 
4165  $element = $this->element;
4166 
4167  $text = ''; $description = '';
4168 
4169  // Line in view mode
4170  if ($action != 'editline' || $selected != $line->id)
4171  {
4172  // Product
4173  if ($line->fk_product > 0)
4174  {
4175  $product_static = new Product($this->db);
4176  $product_static->fetch($line->fk_product);
4177 
4178  $product_static->ref = $line->ref; //can change ref in hook
4179  $product_static->label = $line->label; //can change label in hook
4180 
4181  $text = $product_static->getNomUrl(1);
4182 
4183  // Define output language and label
4184  if (!empty($conf->global->MAIN_MULTILANGS))
4185  {
4186  if (property_exists($this, 'socid') && !is_object($this->thirdparty))
4187  {
4188  dol_print_error('', 'Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
4189  return;
4190  }
4191 
4192  $prod = new Product($this->db);
4193  $prod->fetch($line->fk_product);
4194 
4195  $outputlangs = $langs;
4196  $newlang = '';
4197  if (empty($newlang) && GETPOST('lang_id', 'aZ09')) $newlang = GETPOST('lang_id', 'aZ09');
4198  if (!empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang) && is_object($this->thirdparty)) $newlang = $this->thirdparty->default_lang; // To use language of customer
4199  if (!empty($newlang))
4200  {
4201  $outputlangs = new Translate("", $conf);
4202  $outputlangs->setDefaultLang($newlang);
4203  }
4204 
4205  $label = (!empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
4206  } else {
4207  $label = $line->product_label;
4208  }
4209 
4210  $text .= ' - '.(!empty($line->label) ? $line->label : $label);
4211  $description .= (!empty($conf->global->PRODUIT_DESC_IN_FORM) ? '' : dol_htmlentitiesbr($line->description)); // Description is what to show on popup. We shown nothing if already into desc.
4212  }
4213 
4214  $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU');
4215 
4216  // Output template part (modules that overwrite templates must declare this into descriptor)
4217  // Use global variables + $dateSelector + $seller and $buyer
4218  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
4219  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4220  foreach ($dirtpls as $module => $reldir)
4221  {
4222  if (!empty($module))
4223  {
4224  $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
4225  } else {
4226  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_view.tpl.php';
4227  }
4228 
4229  if (empty($conf->file->strict_mode)) {
4230  $res = @include $tpl;
4231  } else {
4232  $res = include $tpl; // for debug
4233  }
4234  if ($res) break;
4235  }
4236  }
4237 
4238  // Line in update mode
4239  if ($this->statut == 0 && $action == 'editline' && $selected == $line->id)
4240  {
4241  $label = (!empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
4242 
4243  $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx / 100)), 'MU');
4244 
4245  // Output template part (modules that overwrite templates must declare this into descriptor)
4246  // Use global variables + $dateSelector + $seller and $buyer
4247  // Note: This is deprecated. If you need to overwrite the tpl file, use instead the hook printObjectLine and printObjectSubLine.
4248  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4249  foreach ($dirtpls as $module => $reldir)
4250  {
4251  if (!empty($module))
4252  {
4253  $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
4254  } else {
4255  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/objectline_edit.tpl.php';
4256  }
4257 
4258  if (empty($conf->file->strict_mode)) {
4259  $res = @include $tpl;
4260  } else {
4261  $res = include $tpl; // for debug
4262  }
4263  if ($res) break;
4264  }
4265  }
4266  }
4267 
4268 
4269  /* This is to show array of line of details of source object */
4270 
4271 
4282  public function printOriginLinesList($restrictlist = '', $selectedLines = array())
4283  {
4284  global $langs, $hookmanager, $conf, $form;
4285 
4286  print '<tr class="liste_titre">';
4287  print '<td>'.$langs->trans('Ref').'</td>';
4288  print '<td>'.$langs->trans('Description').'</td>';
4289  print '<td class="right">'.$langs->trans('VATRate').'</td>';
4290  print '<td class="right">'.$langs->trans('PriceUHT').'</td>';
4291  if (!empty($conf->multicurrency->enabled)) print '<td class="right">'.$langs->trans('PriceUHTCurrency').'</td>';
4292  print '<td class="right">'.$langs->trans('Qty').'</td>';
4293  if (!empty($conf->global->PRODUCT_USE_UNITS))
4294  {
4295  print '<td class="left">'.$langs->trans('Unit').'</td>';
4296  }
4297  print '<td class="right">'.$langs->trans('ReductionShort').'</td>';
4298  print '<td class="center">'.$form->showCheckAddButtons('checkforselect', 1).'</td>';
4299  print '</tr>';
4300  $i = 0;
4301 
4302  if (!empty($this->lines))
4303  {
4304  foreach ($this->lines as $line)
4305  {
4306  if (is_object($hookmanager) && (($line->product_type == 9 && !empty($line->special_code)) || !empty($line->fk_parent_line)))
4307  {
4308  if (empty($line->fk_parent_line))
4309  {
4310  $parameters = array('line'=>$line, 'i'=>$i);
4311  $action = '';
4312  $hookmanager->executeHooks('printOriginObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4313  }
4314  } else {
4315  $this->printOriginLine($line, '', $restrictlist, '/core/tpl', $selectedLines);
4316  }
4317 
4318  $i++;
4319  }
4320  }
4321  }
4322 
4336  public function printOriginLine($line, $var, $restrictlist = '', $defaulttpldir = '/core/tpl', $selectedLines = array())
4337  {
4338  global $langs, $conf;
4339 
4340  //var_dump($line);
4341  if (!empty($line->date_start))
4342  {
4343  $date_start = $line->date_start;
4344  } else {
4345  $date_start = $line->date_debut_prevue;
4346  if ($line->date_debut_reel) $date_start = $line->date_debut_reel;
4347  }
4348  if (!empty($line->date_end))
4349  {
4350  $date_end = $line->date_end;
4351  } else {
4352  $date_end = $line->date_fin_prevue;
4353  if ($line->date_fin_reel) $date_end = $line->date_fin_reel;
4354  }
4355 
4356  $this->tpl['id'] = $line->id;
4357 
4358  $this->tpl['label'] = '';
4359  if (!empty($line->fk_parent_line)) $this->tpl['label'] .= img_picto('', 'rightarrow');
4360 
4361  if (($line->info_bits & 2) == 2) // TODO Not sure this is used for source object
4362  {
4363  $discount = new DiscountAbsolute($this->db);
4364  $discount->fk_soc = $this->socid;
4365  $this->tpl['label'] .= $discount->getNomUrl(0, 'discount');
4366  } elseif (!empty($line->fk_product))
4367  {
4368  $productstatic = new Product($this->db);
4369  $productstatic->id = $line->fk_product;
4370  $productstatic->ref = $line->ref;
4371  $productstatic->type = $line->fk_product_type;
4372  if (empty($productstatic->ref)) {
4373  $line->fetch_product();
4374  $productstatic = $line->product;
4375  }
4376 
4377  $this->tpl['label'] .= $productstatic->getNomUrl(1);
4378  $this->tpl['label'] .= ' - '.(!empty($line->label) ? $line->label : $line->product_label);
4379  // Dates
4380  if ($line->product_type == 1 && ($date_start || $date_end))
4381  {
4382  $this->tpl['label'] .= get_date_range($date_start, $date_end);
4383  }
4384  } else {
4385  $this->tpl['label'] .= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''), 'service') : img_object($langs->trans(''), 'product')));
4386  if (!empty($line->desc)) {
4387  $this->tpl['label'] .= $line->desc;
4388  } else {
4389  $this->tpl['label'] .= ($line->label ? '&nbsp;'.$line->label : '');
4390  }
4391 
4392  // Dates
4393  if ($line->product_type == 1 && ($date_start || $date_end))
4394  {
4395  $this->tpl['label'] .= get_date_range($date_start, $date_end);
4396  }
4397  }
4398 
4399  if (!empty($line->desc))
4400  {
4401  if ($line->desc == '(CREDIT_NOTE)') // TODO Not sure this is used for source object
4402  {
4403  $discount = new DiscountAbsolute($this->db);
4404  $discount->fetch($line->fk_remise_except);
4405  $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote", $discount->getNomUrl(0));
4406  } elseif ($line->desc == '(DEPOSIT)') // TODO Not sure this is used for source object
4407  {
4408  $discount = new DiscountAbsolute($this->db);
4409  $discount->fetch($line->fk_remise_except);
4410  $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit", $discount->getNomUrl(0));
4411  } elseif ($line->desc == '(EXCESS RECEIVED)')
4412  {
4413  $discount = new DiscountAbsolute($this->db);
4414  $discount->fetch($line->fk_remise_except);
4415  $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived", $discount->getNomUrl(0));
4416  } elseif ($line->desc == '(EXCESS PAID)')
4417  {
4418  $discount = new DiscountAbsolute($this->db);
4419  $discount->fetch($line->fk_remise_except);
4420  $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid", $discount->getNomUrl(0));
4421  } else {
4422  $this->tpl['description'] = dol_trunc($line->desc, 60);
4423  }
4424  } else {
4425  $this->tpl['description'] = '&nbsp;';
4426  }
4427 
4428  // VAT Rate
4429  $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
4430  $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
4431  if (!empty($line->vat_src_code) && !preg_match('/\(/', $this->tpl['vat_rate'])) $this->tpl['vat_rate'] .= ' ('.$line->vat_src_code.')';
4432 
4433  $this->tpl['price'] = price($line->subprice);
4434  $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
4435  $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
4436  if (!empty($conf->global->PRODUCT_USE_UNITS)) $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
4437  $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
4438 
4439  // Is the line strike or not
4440  $this->tpl['strike'] = 0;
4441  if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) $this->tpl['strike'] = 1;
4442 
4443  // Output template part (modules that overwrite templates must declare this into descriptor)
4444  // Use global variables + $dateSelector + $seller and $buyer
4445  $dirtpls = array_merge($conf->modules_parts['tpl'], array($defaulttpldir));
4446  foreach ($dirtpls as $module => $reldir)
4447  {
4448  if (!empty($module))
4449  {
4450  $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
4451  } else {
4452  $tpl = DOL_DOCUMENT_ROOT.$reldir.'/originproductline.tpl.php';
4453  }
4454 
4455  if (empty($conf->file->strict_mode)) {
4456  $res = @include $tpl;
4457  } else {
4458  $res = include $tpl; // for debug
4459  }
4460  if ($res) break;
4461  }
4462  }
4463 
4464 
4465  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4476  public function add_element_resource($resource_id, $resource_type, $busy = 0, $mandatory = 0)
4477  {
4478  // phpcs:enable
4479  $this->db->begin();
4480 
4481  $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_resources (";
4482  $sql .= "resource_id";
4483  $sql .= ", resource_type";
4484  $sql .= ", element_id";
4485  $sql .= ", element_type";
4486  $sql .= ", busy";
4487  $sql .= ", mandatory";
4488  $sql .= ") VALUES (";
4489  $sql .= $resource_id;
4490  $sql .= ", '".$this->db->escape($resource_type)."'";
4491  $sql .= ", '".$this->db->escape($this->id)."'";
4492  $sql .= ", '".$this->db->escape($this->element)."'";
4493  $sql .= ", '".$this->db->escape($busy)."'";
4494  $sql .= ", '".$this->db->escape($mandatory)."'";
4495  $sql .= ")";
4496 
4497  dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
4498  if ($this->db->query($sql))
4499  {
4500  $this->db->commit();
4501  return 1;
4502  } else {
4503  $this->error = $this->db->lasterror();
4504  $this->db->rollback();
4505  return 0;
4506  }
4507  }
4508 
4509  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4518  public function delete_resource($rowid, $element, $notrigger = 0)
4519  {
4520  // phpcs:enable
4521  global $user;
4522 
4523  $this->db->begin();
4524 
4525  $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_resources";
4526  $sql .= " WHERE rowid=".$rowid;
4527 
4528  dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
4529 
4530  $resql = $this->db->query($sql);
4531  if (!$resql)
4532  {
4533  $this->error = $this->db->lasterror();
4534  $this->db->rollback();
4535  return -1;
4536  } else {
4537  if (!$notrigger)
4538  {
4539  $result = $this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
4540  if ($result < 0) { $this->db->rollback(); return -1; }
4541  }
4542  $this->db->commit();
4543  return 1;
4544  }
4545  }
4546 
4547 
4553  public function __clone()
4554  {
4555  // Force a copy of this->lines, otherwise it will point to same object.
4556  if (isset($this->lines) && is_array($this->lines))
4557  {
4558  $nboflines = count($this->lines);
4559  for ($i = 0; $i < $nboflines; $i++)
4560  {
4561  $this->lines[$i] = clone $this->lines[$i];
4562  }
4563  }
4564  }
4565 
4579  protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams = null)
4580  {
4581  global $conf, $langs, $user, $hookmanager, $action;
4582 
4583  $srctemplatepath = '';
4584 
4585  $parameters = array('modelspath'=>$modelspath, 'modele'=>$modele, 'outputlangs'=>$outputlangs, 'hidedetails'=>$hidedetails, 'hidedesc'=>$hidedesc, 'hideref'=>$hideref, 'moreparams'=>$moreparams);
4586  $reshook = $hookmanager->executeHooks('commonGenerateDocument', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
4587 
4588  if (empty($reshook))
4589  {
4590  dol_syslog("commonGenerateDocument modele=".$modele." outputlangs->defaultlang=".(is_object($outputlangs) ? $outputlangs->defaultlang : 'null'));
4591 
4592  if (empty($modele)) {
4593  $this->error = 'BadValueForParameterModele';
4594  return -1;
4595  }
4596 
4597  // Increase limit for PDF build
4598  $err = error_reporting();
4599  error_reporting(0);
4600  @set_time_limit(120);
4601  error_reporting($err);
4602 
4603  // If selected model is a filename template (then $modele="modelname" or "modelname:filename")
4604  $tmp = explode(':', $modele, 2);
4605  if (!empty($tmp[1]))
4606  {
4607  $modele = $tmp[0];
4608  $srctemplatepath = $tmp[1];
4609  }
4610 
4611  // Search template files
4612  $file = '';
4613  $classname = '';
4614  $filefound = '';
4615  $dirmodels = array('/');
4616  if (is_array($conf->modules_parts['models'])) $dirmodels = array_merge($dirmodels, $conf->modules_parts['models']);
4617  foreach ($dirmodels as $reldir)
4618  {
4619  foreach (array('doc', 'pdf') as $prefix)
4620  {
4621  if (in_array(get_class($this), array('Adherent'))) {
4622  // Member module use prefix_modele.class.php
4623  $file = $prefix."_".$modele.".class.php";
4624  } else {
4625  // Other module use prefix_modele.modules.php
4626  $file = $prefix."_".$modele.".modules.php";
4627  }
4628 
4629  // On verifie l'emplacement du modele
4630  $file = dol_buildpath($reldir.$modelspath.$file, 0);
4631  if (file_exists($file)) {
4632  $filefound = $file;
4633  $classname = $prefix.'_'.$modele;
4634  break;
4635  }
4636  }
4637  if ($filefound) break;
4638  }
4639 
4640  // If generator was found
4641  if ($filefound)
4642  {
4643  global $db; // Required to solve a conception default making an include of code using $db instead of $this->db just after.
4644 
4645  require_once $file;
4646 
4647  $obj = new $classname($this->db);
4648 
4649  // If generator is ODT, we must have srctemplatepath defined, if not we set it.
4650  if ($obj->type == 'odt' && empty($srctemplatepath))
4651  {
4652  $varfortemplatedir = $obj->scandir;
4653  if ($varfortemplatedir && !empty($conf->global->$varfortemplatedir))
4654  {
4655  $dirtoscan = $conf->global->$varfortemplatedir;
4656 
4657  $listoffiles = array();
4658 
4659  // Now we add first model found in directories scanned
4660  $listofdir = explode(',', $dirtoscan);
4661  foreach ($listofdir as $key => $tmpdir)
4662  {
4663  $tmpdir = trim($tmpdir);
4664  $tmpdir = preg_replace('/DOL_DATA_ROOT/', DOL_DATA_ROOT, $tmpdir);
4665  if (!$tmpdir) { unset($listofdir[$key]); continue; }
4666  if (is_dir($tmpdir))
4667  {
4668  $tmpfiles = dol_dir_list($tmpdir, 'files', 0, '\.od(s|t)$', '', 'name', SORT_ASC, 0);
4669  if (count($tmpfiles)) $listoffiles = array_merge($listoffiles, $tmpfiles);
4670  }
4671  }
4672 
4673  if (count($listoffiles))
4674  {
4675  foreach ($listoffiles as $record)
4676  {
4677  $srctemplatepath = $record['fullname'];
4678  break;
4679  }
4680  }
4681  }
4682 
4683  if (empty($srctemplatepath))
4684  {
4685  $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
4686  return -1;
4687  }
4688  }
4689 
4690  if ($obj->type == 'odt' && !empty($srctemplatepath))
4691  {
4692  if (!dol_is_file($srctemplatepath))
4693  {
4694  dol_syslog("Failed to locate template file ".$srctemplatepath, LOG_WARNING);
4695  $this->error = 'ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
4696  return -1;
4697  }
4698  }
4699 
4700  // We save charset_output to restore it because write_file can change it if needed for
4701  // output format that does not support UTF8.
4702  $sav_charset_output = $outputlangs->charset_output;
4703 
4704  if (in_array(get_class($this), array('Adherent')))
4705  {
4706  $arrayofrecords = array(); // The write_file of templates of adherent class need this var
4707  $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, $moreparams);
4708  } else {
4709  $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
4710  }
4711  // After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
4712 
4713  if ($resultwritefile > 0)
4714  {
4715  $outputlangs->charset_output = $sav_charset_output;
4716 
4717  // We delete old preview
4718  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4719  dol_delete_preview($this);
4720 
4721  // Index file in database
4722  if (!empty($obj->result['fullpath']))
4723  {
4724  $destfull = $obj->result['fullpath'];
4725  $upload_dir = dirname($destfull);
4726  $destfile = basename($destfull);
4727  $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $upload_dir);
4728 
4729  if (!preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) // If not a tmp dir
4730  {
4731  $filename = basename($destfile);
4732  $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
4733  $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
4734 
4735  include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
4736  $ecmfile = new EcmFiles($this->db);
4737  $result = $ecmfile->fetch(0, '', ($rel_dir ? $rel_dir.'/' : '').$filename);
4738 
4739  // Set the public "share" key
4740  $setsharekey = false;
4741  if ($this->element == 'propal')
4742  {
4743  $useonlinesignature = $conf->global->MAIN_FEATURES_LEVEL; // Replace this with 1 when feature to make online signature is ok
4744  if ($useonlinesignature) $setsharekey = true;
4745  if (!empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey = true;
4746  }
4747  if ($this->element == 'commande' && !empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD)) {
4748  $setsharekey = true;
4749  }
4750  if ($this->element == 'facture' && !empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD)) {
4751  $setsharekey = true;
4752  }
4753  if ($this->element == 'bank_account' && !empty($conf->global->BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD)) {
4754  $setsharekey = true;
4755  }
4756 
4757  if ($setsharekey) {
4758  if (empty($ecmfile->share)) // Because object not found or share not set yet
4759  {
4760  require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
4761  $ecmfile->share = getRandomPassword(true);
4762  }
4763  }
4764 
4765  if ($result > 0)
4766  {
4767  $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
4768  $ecmfile->fullpath_orig = '';
4769  $ecmfile->gen_or_uploaded = 'generated';
4770  $ecmfile->description = ''; // indexed content
4771  $ecmfile->keyword = ''; // keyword content
4772  $result = $ecmfile->update($user);
4773  if ($result < 0) {
4774  setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4775  }
4776  } else {
4777  $ecmfile->entity = $conf->entity;
4778  $ecmfile->filepath = $rel_dir;
4779  $ecmfile->filename = $filename;
4780  $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
4781  $ecmfile->fullpath_orig = '';
4782  $ecmfile->gen_or_uploaded = 'generated';
4783  $ecmfile->description = ''; // indexed content
4784  $ecmfile->keyword = ''; // keyword content
4785  $ecmfile->src_object_type = $this->table_element;
4786  $ecmfile->src_object_id = $this->id;
4787 
4788  $result = $ecmfile->create($user);
4789  if ($result < 0) {
4790  setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4791  }
4792  }
4793 
4794  /*$this->result['fullname']=$destfull;
4795  $this->result['filepath']=$ecmfile->filepath;
4796  $this->result['filename']=$ecmfile->filename;*/
4797  //var_dump($obj->update_main_doc_field);exit;
4798 
4799  // Update the last_main_doc field into main object (if documenent generator has property ->update_main_doc_field set)
4800  $update_main_doc_field = 0;
4801  if (!empty($obj->update_main_doc_field)) $update_main_doc_field = 1;
4802  if ($update_main_doc_field && !empty($this->table_element))
4803  {
4804  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath.'/'.$ecmfile->filename)."'";
4805  $sql .= ' WHERE rowid = '.$this->id;
4806 
4807  $resql = $this->db->query($sql);
4808  if (!$resql) {
4809  dol_print_error($this->db);
4810  } else {
4811  $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
4812  }
4813  }
4814  }
4815  } else {
4816  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);
4817  }
4818 
4819  // Success in building document. We build meta file.
4820  dol_meta_create($this);
4821 
4822  return 1;
4823  } else {
4824  $outputlangs->charset_output = $sav_charset_output;
4825  dol_print_error($this->db, "Error generating document for ".__CLASS__.". Error: ".$obj->error, $obj->errors);
4826  return -1;
4827  }
4828  } else {
4829  if (!$filefound) {
4830  $this->error = $langs->trans("Error").' Failed to load doc generator with modelpaths='.$modelspath.' - modele='.$modele;
4831  dol_print_error('', $this->error);
4832  } else {
4833  $this->error = $langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists", $filefound);
4834  dol_print_error('', $this->error);
4835  }
4836  return -1;
4837  }
4838  } else return $reshook;
4839  }
4840 
4848  public function addThumbs($file)
4849  {
4850  global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini, $quality;
4851 
4852  require_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php'; // This define also $maxwidthsmall, $quality, ...
4853 
4854  $file_osencoded = dol_osencode($file);
4855  if (file_exists($file_osencoded))
4856  {
4857  // Create small thumbs for company (Ratio is near 16/9)
4858  // Used on logon for example
4859  vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
4860 
4861  // Create mini thumbs for company (Ratio is near 16/9)
4862  // Used on menu or for setup page for example
4863  vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
4864  }
4865  }
4866 
4867 
4868  /* Functions common to commonobject and commonobjectline */
4869 
4870  /* For default values */
4871 
4884  public function getDefaultCreateValueFor($fieldname, $alternatevalue = null)
4885  {
4886  global $conf, $_POST;
4887 
4888  // If param here has been posted, we use this value first.
4889  if (GETPOSTISSET($fieldname)) return GETPOST($fieldname, 'alphanohtml', 3);
4890 
4891  if (isset($alternatevalue)) return $alternatevalue;
4892 
4893  $newelement = $this->element;
4894  if ($newelement == 'facture') $newelement = 'invoice';
4895  if ($newelement == 'commande') $newelement = 'order';
4896  if (empty($newelement))
4897  {
4898  dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
4899  return '';
4900  }
4901 
4902  $keyforfieldname = strtoupper($newelement.'_DEFAULT_'.$fieldname);
4903  //var_dump($keyforfieldname);
4904  if (isset($conf->global->$keyforfieldname)) return $conf->global->$keyforfieldname;
4905 
4906  // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
4907  }
4908 
4909 
4910  /* For triggers */
4911 
4912 
4913  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
4924  public function call_trigger($triggerName, $user)
4925  {
4926  // phpcs:enable
4927  global $langs, $conf;
4928 
4929  if (!is_object($langs)) { // If lang was not defined, we set it. It is required by run_triggers.
4930  include_once DOL_DOCUMENT_ROOT.'/core/class/translate.class.php';
4931  $langs = new Translate('', $conf);
4932  }
4933 
4934  include_once DOL_DOCUMENT_ROOT.'/core/class/interfaces.class.php';
4935  $interface = new Interfaces($this->db);
4936  $result = $interface->run_triggers($triggerName, $this, $user, $langs, $conf);
4937 
4938  if ($result < 0)
4939  {
4940  if (!empty($this->errors))
4941  {
4942  $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.
4943  } else {
4944  $this->errors = $interface->errors;
4945  }
4946  }
4947  return $result;
4948  }
4949 
4950 
4951  /* Functions for data in other language */
4952 
4953 
4961  public function fetchValuesForExtraLanguages()
4962  {
4963  // To avoid SQL errors. Probably not the better solution though
4964  if (!$this->element) {
4965  return 0;
4966  }
4967  if (!($this->id > 0)) {
4968  return 0;
4969  }
4970  if (is_array($this->array_languages)) {
4971  return 1;
4972  }
4973 
4974  $this->array_languages = array();
4975 
4976  $element = $this->element;
4977  if ($element == 'categorie') $element = 'categories'; // For compatibility
4978 
4979  // Request to get translation values for object
4980  $sql = "SELECT rowid, property, lang , value";
4981  $sql .= " FROM ".MAIN_DB_PREFIX."object_lang";
4982  $sql .= " WHERE type_object = '".$this->db->escape($element)."'";
4983  $sql .= " AND fk_object = ".$this->id;
4984 
4985  //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
4986  $resql = $this->db->query($sql);
4987  if ($resql)
4988  {
4989  $numrows = $this->db->num_rows($resql);
4990  if ($numrows)
4991  {
4992  $i = 0;
4993  while ($i < $numrows) {
4994  $obj = $this->db->fetch_object($resql);
4995  $key = $obj->property;
4996  $value = $obj->value;
4997  $codelang = $obj->lang;
4998  $type = $this->fields[$key]['type'];
4999 
5000  // we can add this attribute to object
5001  if (preg_match('/date/', $type))
5002  {
5003  $this->array_languages[$key][$codelang] = $this->db->jdate($value);
5004  } else {
5005  $this->array_languages[$key][$codelang] = $value;
5006  }
5007 
5008  $i++;
5009  }
5010  }
5011 
5012  $this->db->free($resql);
5013 
5014  if ($numrows) return $numrows;
5015  else return 0;
5016  } else {
5017  dol_print_error($this->db);
5018  return -1;
5019  }
5020  }
5021 
5028  public function setValuesForExtraLanguages($onlykey = '')
5029  {
5030  global $_POST, $langs;
5031 
5032  // Get extra fields
5033  foreach ($_POST as $postfieldkey => $postfieldvalue) {
5034  $tmparray = explode('-', $postfieldkey);
5035  if ($tmparray[0] != 'field') continue;
5036 
5037  $element = $tmparray[1];
5038  $key = $tmparray[2];
5039  $codelang = $tmparray[3];
5040  //var_dump("postfieldkey=".$postfieldkey." element=".$element." key=".$key." codelang=".$codelang);
5041 
5042  if (!empty($onlykey) && $key != $onlykey) continue;
5043  if ($element != $this->element) continue;
5044 
5045  $key_type = $this->fields[$key]['type'];
5046 
5047  $enabled = 1;
5048  if (isset($this->fields[$key]['enabled']))
5049  {
5050  $enabled = dol_eval($this->fields[$key]['enabled'], 1);
5051  }
5052  /*$perms = 1;
5053  if (isset($this->fields[$key]['perms']))
5054  {
5055  $perms = dol_eval($this->fields[$key]['perms'], 1);
5056  }*/
5057  if (empty($enabled)) continue;
5058  //if (empty($perms)) continue;
5059 
5060  if (in_array($key_type, array('date')))
5061  {
5062  // Clean parameters
5063  // TODO GMT date in memory must be GMT so we should add gm=true in parameters
5064  $value_key = dol_mktime(0, 0, 0, $_POST[$postfieldkey."month"], $_POST[$postfieldkey."day"], $_POST[$postfieldkey."year"]);
5065  } elseif (in_array($key_type, array('datetime')))
5066  {
5067  // Clean parameters
5068  // TODO GMT date in memory must be GMT so we should add gm=true in parameters
5069  $value_key = dol_mktime($_POST[$postfieldkey."hour"], $_POST[$postfieldkey."min"], 0, $_POST[$postfieldkey."month"], $_POST[$postfieldkey."day"], $_POST[$postfieldkey."year"]);
5070  } elseif (in_array($key_type, array('checkbox', 'chkbxlst')))
5071  {
5072  $value_arr = GETPOST($postfieldkey, 'array'); // check if an array
5073  if (!empty($value_arr)) {
5074  $value_key = implode(',', $value_arr);
5075  } else {
5076  $value_key = '';
5077  }
5078  } elseif (in_array($key_type, array('price', 'double')))
5079  {
5080  $value_arr = GETPOST($postfieldkey, 'alpha');
5081  $value_key = price2num($value_arr);
5082  } else {
5083  $value_key = GETPOST($postfieldkey);
5084  if (in_array($key_type, array('link')) && $value_key == '-1') $value_key = '';
5085  }
5086 
5087  $this->array_languages[$key][$codelang] = $value_key;
5088 
5089  /*if ($nofillrequired) {
5090  $langs->load('errors');
5091  setEventMessages($langs->trans('ErrorFieldsRequired').' : '.implode(', ', $error_field_required), null, 'errors');
5092  return -1;
5093  }*/
5094  }
5095 
5096  return 1;
5097  }
5098 
5099 
5100  /* Functions for extrafields */
5101 
5108  public function fetchNoCompute($id)
5109  {
5110  global $conf;
5111 
5112  $savDisableCompute = $conf->disable_compute;
5113  $conf->disable_compute = 1;
5114 
5115  $ret = $this->fetch($id);
5116 
5117  $conf->disable_compute = $savDisableCompute;
5118 
5119  return $ret;
5120  }
5121 
5122  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
5132  public function fetch_optionals($rowid = null, $optionsArray = null)
5133  {
5134  // phpcs:enable
5135  global $conf, $extrafields;
5136 
5137  if (empty($rowid)) $rowid = $this->id;
5138  if (empty($rowid) && isset($this->rowid)) $rowid = $this->rowid; // deprecated
5139 
5140  // To avoid SQL errors. Probably not the better solution though
5141  if (!$this->table_element) {
5142  return 0;
5143  }
5144 
5145  $this->array_options = array();
5146 
5147  if (!is_array($optionsArray))
5148  {
5149  // If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
5150  if (!isset($extrafields) || !is_object($extrafields))
5151  {
5152  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5153  $extrafields = new ExtraFields($this->db);
5154  }
5155 
5156  // Load array of extrafields for elementype = $this->table_element
5157  if (empty($extrafields->attributes[$this->table_element]['loaded']))
5158  {
5159  $extrafields->fetch_name_optionals_label($this->table_element);
5160  }
5161  $optionsArray = (!empty($extrafields->attributes[$this->table_element]['label']) ? $extrafields->attributes[$this->table_element]['label'] : null);
5162  } else {
5163  global $extrafields;
5164  dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
5165  }
5166 
5167  $table_element = $this->table_element;
5168  if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
5169 
5170  // Request to get complementary values
5171  if (is_array($optionsArray) && count($optionsArray) > 0)
5172  {
5173  $sql = "SELECT rowid";
5174  foreach ($optionsArray as $name => $label)
5175  {
5176  if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate')
5177  {
5178  $sql .= ", ".$name;
5179  }
5180  }
5181  $sql .= " FROM ".MAIN_DB_PREFIX.$table_element."_extrafields";
5182  $sql .= " WHERE fk_object = ".((int) $rowid);
5183 
5184  //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
5185  $resql = $this->db->query($sql);
5186  if ($resql)
5187  {
5188  $numrows = $this->db->num_rows($resql);
5189  if ($numrows)
5190  {
5191  $tab = $this->db->fetch_array($resql);
5192 
5193  foreach ($tab as $key => $value)
5194  {
5195  // 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)
5196  if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && !is_int($key))
5197  {
5198  // we can add this attribute to object
5199  if (!empty($extrafields) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date', 'datetime')))
5200  {
5201  //var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
5202  $this->array_options["options_".$key] = $this->db->jdate($value);
5203  } else {
5204  $this->array_options["options_".$key] = $value;
5205  }
5206 
5207  //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
5208  }
5209  }
5210 
5211  // If field is a computed field, value must become result of compute
5212  foreach ($tab as $key => $value) {
5213  if (!empty($extrafields) && !empty($extrafields->attributes[$this->table_element]['computed'][$key]))
5214  {
5215  //var_dump($conf->disable_compute);
5216  if (empty($conf->disable_compute)) {
5217  $this->array_options["options_".$key] = dol_eval($extrafields->attributes[$this->table_element]['computed'][$key], 1, 0);
5218  }
5219  }
5220  }
5221  }
5222 
5223  $this->db->free($resql);
5224 
5225  if ($numrows) return $numrows;
5226  else return 0;
5227  } else {
5228  dol_print_error($this->db);
5229  return -1;
5230  }
5231  }
5232  return 0;
5233  }
5234 
5241  public function deleteExtraFields()
5242  {
5243  global $conf;
5244 
5245  if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;
5246 
5247  $this->db->begin();
5248 
5249  $table_element = $this->table_element;
5250  if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
5251 
5252  $sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
5253  dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
5254  $resql = $this->db->query($sql_del);
5255  if (!$resql)
5256  {
5257  $this->error = $this->db->lasterror();
5258  $this->db->rollback();
5259  return -1;
5260  } else {
5261  $this->db->commit();
5262  return 1;
5263  }
5264  }
5265 
5276  public function insertExtraFields($trigger = '', $userused = null)
5277  {
5278  global $conf, $langs, $user;
5279 
5280  if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;
5281 
5282  if (empty($userused)) $userused = $user;
5283 
5284  $error = 0;
5285 
5286  if (!empty($this->array_options))
5287  {
5288  // Check parameters
5289  $langs->load('admin');
5290  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5291  $extrafields = new ExtraFields($this->db);
5292  $target_extrafields = $extrafields->fetch_name_optionals_label($this->table_element);
5293 
5294  // Eliminate copied source object extra fields that do not exist in target object
5295  $new_array_options = array();
5296  foreach ($this->array_options as $key => $value) {
5297  if (in_array(substr($key, 8), array_keys($target_extrafields))) // We remove the 'options_' from $key for test
5298  $new_array_options[$key] = $value;
5299  elseif (in_array($key, array_keys($target_extrafields))) // We test on $key that does not contains the 'options_' prefix
5300  $new_array_options['options_'.$key] = $value;
5301  }
5302 
5303  foreach ($new_array_options as $key => $value)
5304  {
5305  $attributeKey = substr($key, 8); // Remove 'options_' prefix
5306  $attributeType = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
5307  $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$attributeKey];
5308  $attributeParam = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
5309  $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
5310  $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$attributeKey];
5311 
5312  // Similar code than into insertExtraFields
5313  if ($attributeRequired)
5314  {
5315  $mandatorypb = false;
5316  if ($attributeType == 'link' && $this->array_options[$key] == '-1') $mandatorypb = true;
5317  if ($this->array_options[$key] === '') $mandatorypb = true;
5318  if ($attributeType == 'sellist' && $this->array_options[$key] == '0') $mandatorypb = true;
5319  if ($mandatorypb)
5320  {
5321  dol_syslog("Mandatory extra field ".$key." is empty");
5322  $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
5323  return -1;
5324  }
5325  }
5326 
5327  //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
5328  //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
5329 
5330  if (!empty($attrfieldcomputed))
5331  {
5332  if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS))
5333  {
5334  $value = dol_eval($attrfieldcomputed, 1, 0);
5335  dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
5336  $new_array_options[$key] = $value;
5337  } else {
5338  $new_array_options[$key] = null;
5339  }
5340  }
5341 
5342  switch ($attributeType)
5343  {
5344  case 'int':
5345  if (!is_numeric($value) && $value != '')
5346  {
5347  $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5348  return -1;
5349  } elseif ($value == '')
5350  {
5351  $new_array_options[$key] = null;
5352  }
5353  break;
5354  case 'price':
5355  case 'double':
5356  $value = price2num($value);
5357  if (!is_numeric($value) && $value != '')
5358  {
5359  dol_syslog($langs->trans("ExtraFieldHasWrongValue")." for ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
5360  $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5361  return -1;
5362  } elseif ($value == '')
5363  {
5364  $new_array_options[$key] = null;
5365  }
5366  //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
5367  $new_array_options[$key] = $value;
5368  break;
5369  /*case 'select': // Not required, we chosed value='0' for undefined values
5370  if ($value=='-1')
5371  {
5372  $this->array_options[$key] = null;
5373  }
5374  break;*/
5375  case 'password':
5376  $algo = '';
5377  if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']))
5378  {
5379  // If there is an encryption choice, we use it to crypt data before insert
5380  $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
5381  $algo = reset($tmparrays);
5382  if ($algo != '')
5383  {
5384  //global $action; // $action may be 'create', 'update', 'update_extras'...
5385  //var_dump($action);
5386  //var_dump($this->oldcopy);exit;
5387  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
5388  {
5389  //var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]);
5390  if ($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.
5391  {
5392  $new_array_options[$key] = $this->array_options[$key]; // Value is kept
5393  } else {
5394  // var_dump($algo);
5395  $newvalue = dol_hash($this->array_options[$key], $algo);
5396  $new_array_options[$key] = $newvalue;
5397  }
5398  } else {
5399  $new_array_options[$key] = $this->array_options[$key]; // Value is kept
5400  }
5401  }
5402  } else // Common usage
5403  {
5404  $new_array_options[$key] = $this->array_options[$key];
5405  }
5406  break;
5407  case 'date':
5408  case 'datetime':
5409  // If data is a string instead of a timestamp, we convert it
5410  if (!is_int($this->array_options[$key])) {
5411  $this->array_options[$key] = strtotime($this->array_options[$key]);
5412  }
5413  $new_array_options[$key] = $this->db->idate($this->array_options[$key]);
5414  break;
5415  case 'link':
5416  $param_list = array_keys($attributeParam['options']);
5417  // 0 : ObjectName
5418  // 1 : classPath
5419  $InfoFieldList = explode(":", $param_list[0]);
5420  dol_include_once($InfoFieldList[1]);
5421  if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
5422  {
5423  if ($value == '-1') // -1 is key for no defined in combo list of objects
5424  {
5425  $new_array_options[$key] = '';
5426  } elseif ($value) {
5427  $object = new $InfoFieldList[0]($this->db);
5428  if (is_numeric($value)) $res = $object->fetch($value); // Common case
5429  else $res = $object->fetch('', $value); // For compatibility
5430 
5431  if ($res > 0) $new_array_options[$key] = $object->id;
5432  else {
5433  $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
5434  $this->db->rollback();
5435  return -1;
5436  }
5437  }
5438  } else {
5439  dol_syslog('Error bad setup of extrafield', LOG_WARNING);
5440  }
5441  break;
5442  }
5443  }
5444 
5445  $this->db->begin();
5446 
5447  $table_element = $this->table_element;
5448  if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
5449 
5450  dol_syslog(get_class($this)."::insertExtraFields delete then insert", LOG_DEBUG);
5451 
5452  $sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
5453  $this->db->query($sql_del);
5454 
5455  $sql = "INSERT INTO ".MAIN_DB_PREFIX.$table_element."_extrafields (fk_object";
5456  foreach ($new_array_options as $key => $value)
5457  {
5458  $attributeKey = substr($key, 8); // Remove 'options_' prefix
5459  // Add field of attribut
5460  if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator
5461  $sql .= ",".$attributeKey;
5462  }
5463  // We must insert a default value for fields for other entities that are mandatory to avoid not null error
5464  if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
5465  foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
5466  if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
5467  $sql .= ",".$tmpkey;
5468  }
5469  }
5470  }
5471  $sql .= ") VALUES (".$this->id;
5472 
5473  foreach ($new_array_options as $key => $value) {
5474  $attributeKey = substr($key, 8); // Remove 'options_' prefix
5475  // Add field of attribute
5476  if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') { // Only for other type than separator)
5477  if ($new_array_options[$key] != '' || $new_array_options[$key] == '0') {
5478  $sql .= ",'".$this->db->escape($new_array_options[$key])."'";
5479  } else {
5480  $sql .= ",null";
5481  }
5482  }
5483  }
5484  // We must insert a default value for fields for other entities that are mandatory to avoid not null error
5485  if (!empty($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities']) && is_array($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'])) {
5486  foreach ($extrafields->attributes[$this->table_element]['mandatoryfieldsofotherentities'] as $tmpkey => $tmpval) {
5487  if (!isset($extrafields->attributes[$this->table_element]['type'][$tmpkey])) { // If field not already added previously
5488  if (in_array($tmpval, array('int', 'double', 'price'))) $sql .= ", 0";
5489  else $sql .= ", ''";
5490  }
5491  }
5492  }
5493 
5494  $sql .= ")";
5495 
5496  $resql = $this->db->query($sql);
5497  if (!$resql)
5498  {
5499  $this->error = $this->db->lasterror();
5500  $error++;
5501  }
5502 
5503  if (!$error && $trigger)
5504  {
5505  // Call trigger
5506  $this->context = array('extrafieldaddupdate'=>1);
5507  $result = $this->call_trigger($trigger, $userused);
5508  if ($result < 0) $error++;
5509  // End call trigger
5510  }
5511 
5512  if ($error)
5513  {
5514  $this->db->rollback();
5515  return -1;
5516  } else {
5517  $this->db->commit();
5518  return 1;
5519  }
5520  } else return 0;
5521  }
5522 
5533  public function insertExtraLanguages($trigger = '', $userused = null)
5534  {
5535  global $conf, $langs, $user;
5536 
5537  if (empty($userused)) $userused = $user;
5538 
5539  $error = 0;
5540 
5541  if (!empty($conf->global->MAIN_EXTRALANGUAGES_DISABLED)) return 0; // For avoid conflicts if trigger used
5542 
5543  if (is_array($this->array_languages))
5544  {
5545  $new_array_languages = $this->array_languages;
5546 
5547  foreach ($new_array_languages as $key => $value)
5548  {
5549  $attributeKey = $key;
5550  $attributeType = $this->fields[$attributeKey]['type'];
5551  $attributeLabel = $this->fields[$attributeKey]['label'];
5552 
5553  //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
5554  //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
5555 
5556  switch ($attributeType)
5557  {
5558  case 'int':
5559  if (!is_numeric($value) && $value != '')
5560  {
5561  $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
5562  return -1;
5563  } elseif ($value == '')
5564  {
5565  $new_array_languages[$key] = null;
5566  }
5567  break;
5568  case 'double':
5569  $value = price2num($value);
5570  if (!is_numeric($value) && $value != '')
5571  {
5572  dol_syslog($langs->trans("ExtraLanguageHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
5573  $this->errors[] = $langs->trans("ExtraLanguageHasWrongValue", $attributeLabel);
5574  return -1;
5575  } elseif ($value == '')
5576  {
5577  $new_array_languages[$key] = null;
5578  }
5579  //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
5580  $new_array_languages[$key] = $value;
5581  break;
5582  /*case 'select': // Not required, we chosed value='0' for undefined values
5583  if ($value=='-1')
5584  {
5585  $this->array_options[$key] = null;
5586  }
5587  break;*/
5588  }
5589  }
5590 
5591  $this->db->begin();
5592 
5593  $table_element = $this->table_element;
5594  if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
5595 
5596  dol_syslog(get_class($this)."::insertExtraLanguages delete then insert", LOG_DEBUG);
5597 
5598  foreach ($new_array_languages as $key => $langcodearray) { // $key = 'name', 'town', ...
5599  foreach ($langcodearray as $langcode => $value) {
5600  $sql_del = "DELETE FROM ".MAIN_DB_PREFIX."object_lang";
5601  $sql_del .= " WHERE fk_object = ".$this->id." AND property = '".$this->db->escape($key)."' AND type_object = '".$this->db->escape($table_element)."'";
5602  $sql_del .= " AND lang = '".$this->db->escape($langcode)."'";
5603  $this->db->query($sql_del);
5604 
5605  if ($value !== '') {
5606  $sql = "INSERT INTO ".MAIN_DB_PREFIX."object_lang (fk_object, property, type_object, lang, value";
5607  $sql .= ") VALUES (".$this->id.", '".$this->db->escape($key)."', '".$this->db->escape($table_element)."', '".$this->db->escape($langcode)."', '".$this->db->escape($value)."'";
5608  $sql .= ")";
5609 
5610  $resql = $this->db->query($sql);
5611  if (!$resql)
5612  {
5613  $this->error = $this->db->lasterror();
5614  $error++;
5615  break;
5616  }
5617  }
5618  }
5619  }
5620 
5621  if (!$error && $trigger)
5622  {
5623  // Call trigger
5624  $this->context = array('extralanguagesaddupdate'=>1);
5625  $result = $this->call_trigger($trigger, $userused);
5626  if ($result < 0) $error++;
5627  // End call trigger
5628  }
5629 
5630  if ($error)
5631  {
5632  $this->db->rollback();
5633  return -1;
5634  } else {
5635  $this->db->commit();
5636  return 1;
5637  }
5638  } else return 0;
5639  }
5640 
5651  public function updateExtraField($key, $trigger = null, $userused = null)
5652  {
5653  global $conf, $langs, $user;
5654 
5655  if (!empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0;
5656 
5657  if (empty($userused)) $userused = $user;
5658 
5659  $error = 0;
5660 
5661  if (!empty($this->array_options) && isset($this->array_options["options_".$key]))
5662  {
5663  // Check parameters
5664  $langs->load('admin');
5665  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5666  $extrafields = new ExtraFields($this->db);
5667  $extrafields->fetch_name_optionals_label($this->table_element);
5668 
5669  $value = $this->array_options["options_".$key];
5670 
5671  $attributeType = $extrafields->attributes[$this->table_element]['type'][$key];
5672  $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key];
5673  $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key];
5674  $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
5675  $attrfieldcomputed = $extrafields->attributes[$this->table_element]['computed'][$key];
5676 
5677  // Similar code than into insertExtraFields
5678  if ($attributeRequired)
5679  {
5680  $mandatorypb = false;
5681  if ($attributeType == 'link' && $this->array_options["options_".$key] == '-1') $mandatorypb = true;
5682  if ($this->array_options["options_".$key] === '') $mandatorypb = true;
5683  if ($mandatorypb)
5684  {
5685  dol_syslog("Mandatory extra field options_".$key." is empty");
5686  $this->errors[] = $langs->trans('ErrorFieldRequired', $attributeLabel);
5687  return -1;
5688  }
5689  }
5690 
5691  //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
5692  //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
5693 
5694  if (!empty($attrfieldcomputed))
5695  {
5696  if (!empty($conf->global->MAIN_STORE_COMPUTED_EXTRAFIELDS))
5697  {
5698  $value = dol_eval($attrfieldcomputed, 1, 0);
5699  dol_syslog($langs->trans("Extrafieldcomputed")." sur ".$attributeLabel."(".$value.")", LOG_DEBUG);
5700  $this->array_options["options_".$key] = $value;
5701  } else {
5702  $this->array_options["options_".$key] = null;
5703  }
5704  }
5705 
5706  switch ($attributeType)
5707  {
5708  case 'int':
5709  if (!is_numeric($value) && $value != '')
5710  {
5711  $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5712  return -1;
5713  } elseif ($value === '')
5714  {
5715  $this->array_options["options_".$key] = null;
5716  }
5717  break;
5718  case 'double':
5719  $value = price2num($value);
5720  if (!is_numeric($value) && $value != '')
5721  {
5722  dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
5723  $this->errors[] = $langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5724  return -1;
5725  } elseif ($value === '')
5726  {
5727  $this->array_options["options_".$key] = null;
5728  }
5729  //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
5730  $this->array_options["options_".$key] = $value;
5731  break;
5732  /*case 'select': // Not required, we chosed value='0' for undefined values
5733  if ($value=='-1')
5734  {
5735  $this->array_options[$key] = null;
5736  }
5737  break;*/
5738  case 'price':
5739  $this->array_options["options_".$key] = price2num($this->array_options["options_".$key]);
5740  break;
5741  case 'date':
5742  $this->array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key]);
5743  break;
5744  case 'datetime':
5745  $this->array_options["options_".$key] = $this->db->idate($this->array_options["options_".$key]);
5746  break;
5747  /*
5748  case 'link':
5749  $param_list = array_keys($attributeParam['options']);
5750  // 0 : ObjectName
5751  // 1 : classPath
5752  $InfoFieldList = explode(":", $param_list[0]);
5753  dol_include_once($InfoFieldList[1]);
5754  if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
5755  {
5756  if ($value == '-1') // -1 is key for no defined in combo list of objects
5757  {
5758  $new_array_options[$key] = '';
5759  } elseif ($value) {
5760  $object = new $InfoFieldList[0]($this->db);
5761  if (is_numeric($value)) $res = $object->fetch($value); // Common case
5762  else $res = $object->fetch('', $value); // For compatibility
5763 
5764  if ($res > 0) $new_array_options[$key] = $object->id;
5765  else {
5766  $this->error = "Id/Ref '".$value."' for object '".$object->element."' not found";
5767  $this->db->rollback();
5768  return -1;
5769  }
5770  }
5771  } else {
5772  dol_syslog('Error bad setup of extrafield', LOG_WARNING);
5773  }
5774  break;
5775  */
5776  }
5777 
5778  $this->db->begin();
5779 
5780  $linealreadyfound = 0;
5781 
5782  // 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)
5783  $sql = "SELECT COUNT(rowid) as nb FROM ".MAIN_DB_PREFIX.$this->table_element."_extrafields WHERE fk_object = ".$this->id;
5784  $resql = $this->db->query($sql);
5785  if ($resql) {
5786  $tmpobj = $this->db->fetch_object($resql);
5787  if ($tmpobj) {
5788  $linealreadyfound = $tmpobj->nb;
5789  }
5790  }
5791 
5792  if ($linealreadyfound) {
5793  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element."_extrafields SET ".$key." = '".$this->db->escape($this->array_options["options_".$key])."'";
5794  $sql .= " WHERE fk_object = ".$this->id;
5795  } else {
5796  $result = $this->insertExtraFields('', $user);
5797  if ($result < 0) $error++;
5798  }
5799 
5800  $resql = $this->db->query($sql);
5801  if (!$resql)
5802  {
5803  $error++;
5804  $this->error = $this->db->lasterror();
5805  }
5806  if (!$error && $trigger)
5807  {
5808  // Call trigger
5809  $this->context = array('extrafieldupdate'=>1);
5810  $result = $this->call_trigger($trigger, $userused);
5811  if ($result < 0) $error++;
5812  // End call trigger
5813  }
5814 
5815  if ($error)
5816  {
5817  dol_syslog(__METHOD__.$this->error, LOG_ERR);
5818  $this->db->rollback();
5819  return -1;
5820  } else {
5821  $this->db->commit();
5822  return 1;
5823  }
5824  } else return 0;
5825  }
5826 
5837  public function updateExtraLanguages($key, $trigger = null, $userused = null)
5838  {
5839  global $conf, $langs, $user;
5840 
5841  if (empty($userused)) $userused = $user;
5842 
5843  $error = 0;
5844 
5845  if (!empty($conf->global->MAIN_EXTRALANGUAGES_DISABLED)) return 0; // For avoid conflicts if trigger used
5846 
5847  return 0;
5848  }
5849 
5850 
5865  public function showInputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = 0, $nonewbutton = 0)
5866  {
5867  global $conf, $langs, $form;
5868 
5869  if (!is_object($form))
5870  {
5871  require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5872  $form = new Form($this->db);
5873  }
5874 
5875  if (!empty($this->fields)) {
5876  $val = $this->fields[$key];
5877  }
5878 
5879  $out = '';
5880  $type = '';
5881  $isDependList=0;
5882  $param = array();
5883  $param['options'] = array();
5884  $reg = array();
5885  $size = $this->fields[$key]['size'];
5886  // Because we work on extrafields
5887  if (preg_match('/^(integer|link):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
5888  $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
5889  $type = 'link';
5890  } elseif (preg_match('/^(integer|link):(.*):(.*):(.*)/i', $val['type'], $reg)) {
5891  $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
5892  $type = 'link';
5893  } elseif (preg_match('/^(integer|link):(.*):(.*)/i', $val['type'], $reg)) {
5894  $param['options'] = array($reg[2].':'.$reg[3] => 'N');
5895  $type = 'link';
5896  } elseif (preg_match('/^(sellist):(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
5897  $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4].':'.$reg[5] => 'N');
5898  $type = 'sellist';
5899  } elseif (preg_match('/^(sellist):(.*):(.*):(.*)/i', $val['type'], $reg)) {
5900  $param['options'] = array($reg[2].':'.$reg[3].':'.$reg[4] => 'N');
5901  $type = 'sellist';
5902  } elseif (preg_match('/^(sellist):(.*):(.*)/i', $val['type'], $reg)) {
5903  $param['options'] = array($reg[2].':'.$reg[3] => 'N');
5904  $type = 'sellist';
5905  } elseif (preg_match('/varchar\((\d+)\)/', $val['type'], $reg)) {
5906  $param['options'] = array();
5907  $type = 'varchar';
5908  $size = $reg[1];
5909  } elseif (preg_match('/varchar/', $val['type'])) {
5910  $param['options'] = array();
5911  $type = 'varchar';
5912  } elseif (is_array($this->fields[$key]['arrayofkeyval'])) {
5913  $param['options'] = $this->fields[$key]['arrayofkeyval'];
5914  $type = 'select';
5915  } else {
5916  $param['options'] = array();
5917  $type = $this->fields[$key]['type'];
5918  }
5919 
5920 
5921  $label = $this->fields[$key]['label'];
5922  //$elementtype=$this->fields[$key]['elementtype']; // Seems not used
5923  $default = $this->fields[$key]['default'];
5924  $computed = $this->fields[$key]['computed'];
5925  $unique = $this->fields[$key]['unique'];
5926  $required = $this->fields[$key]['required'];
5927  $autofocusoncreate = $this->fields[$key]['autofocusoncreate'];
5928 
5929  $langfile = $this->fields[$key]['langfile'];
5930  $list = $this->fields[$key]['list'];
5931  $hidden = (in_array(abs($this->fields[$key]['visible']), array(0, 2)) ? 1 : 0);
5932 
5933  $objectid = $this->id;
5934 
5935  if ($computed) {
5936  if (!preg_match('/^search_/', $keyprefix)) return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
5937  else return '';
5938  }
5939 
5940  // Set value of $morecss. For this, we use in priority showsize from parameters, then $val['css'] then autodefine
5941  if (empty($morecss) && !empty($val['css'])) {
5942  $morecss = $val['css'];
5943  } elseif (empty($morecss)) {
5944  if ($type == 'date') {
5945  $morecss = 'minwidth100imp';
5946  } elseif ($type == 'datetime' || $type == 'link') { // link means an foreign key to another primary id
5947  $morecss = 'minwidth200imp';
5948  } elseif (in_array($type, array('int', 'integer', 'price')) || preg_match('/^double(\([0-9],[0-9]\)){0,1}/', $type)) {
5949  $morecss = 'maxwidth75';
5950  } elseif ($type == 'url') {
5951  $morecss = 'minwidth400';
5952  } elseif ($type == 'boolean') {
5953  $morecss = '';
5954  } else {
5955  if (round($size) < 12) {
5956  $morecss = 'minwidth100';
5957  } elseif (round($size) <= 48) {
5958  $morecss = 'minwidth200';
5959  } else {
5960  $morecss = 'minwidth400';
5961  }
5962  }
5963  }
5964 
5965  if (in_array($type, array('date'))) {
5966  $tmp = explode(',', $size);
5967  $newsize = $tmp[0];
5968  $showtime = 0;
5969 
5970  // Do not show current date when field not required (see selectDate() method)
5971  if (!$required && $value == '') $value = '-1';
5972 
5973  // TODO Must also support $moreparam
5974  $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
5975  } elseif (in_array($type, array('datetime'))) {
5976  $tmp = explode(',', $size);
5977  $newsize = $tmp[0];
5978  $showtime = 1;
5979 
5980  // Do not show current date when field not required (see selectDate() method)
5981  if (!$required && $value == '') $value = '-1';
5982 
5983  // TODO Must also support $moreparam
5984  $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1, '', '', '', 1, '', '', 'tzuserrel');
5985  } elseif (in_array($type, array('duration'))) {
5986  $out = $form->select_duration($keyprefix.$key.$keysuffix, $value, 0, 'text', 0, 1);
5987  } elseif (in_array($type, array('int', 'integer'))) {
5988  $tmp = explode(',', $size);
5989  $newsize = $tmp[0];
5990  $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$newsize.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
5991  } elseif (in_array($type, array('real'))) {
5992  $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
5993  } elseif (preg_match('/varchar/', $type)) {
5994  $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$size.'" value="'.dol_escape_htmltag($value).'"'.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
5995  } elseif (in_array($type, array('mail', 'phone', 'url'))) {
5996  $out = '<input type="text" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').($autofocusoncreate ? ' autofocus' : '').'>';
5997  } elseif (preg_match('/^text/', $type)) {
5998  if (!preg_match('/search_/', $keyprefix)) // If keyprefix is search_ or search_options_, we must just use a simple text field
5999  {
6000  require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
6001  $doleditor = new DolEditor($keyprefix.$key.$keysuffix, $value, '', 200, 'dolibarr_notes', 'In', false, false, false, ROWS_5, '90%');
6002  $out = $doleditor->Create(1);
6003  } else {
6004  $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
6005  }
6006  } elseif (preg_match('/^html/', $type)) {
6007  if (!preg_match('/search_/', $keyprefix)) { // If keyprefix is search_ or search_options_, we must just use a simple text field
6008  require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
6009  $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%');
6010  $out = $doleditor->Create(1);
6011  } else {
6012  $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam ? $moreparam : '').'>';
6013  }
6014  } elseif ($type == 'boolean') {
6015  $checked = '';
6016  if (!empty($value)) {
6017  $checked = ' checked value="1" ';
6018  } else {
6019  $checked = ' value="1" ';
6020  }
6021  $out = '<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam ? $moreparam : '').'>';
6022  } elseif ($type == 'price') {
6023  if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
6024  $value = price($value);
6025  }
6026  $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> '.$langs->getCurrencySymbol($conf->currency);
6027  } elseif (preg_match('/^double(\([0-9],[0-9]\)){0,1}/', $type)) {
6028  if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
6029  $value = price($value);
6030  }
6031  $out = '<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'> ';
6032  } elseif ($type == 'select') {
6033  $out = '';
6034  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
6035  {
6036  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
6037  $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
6038  }
6039 
6040  $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
6041  if ((!isset($this->fields[$key]['default'])) || ($this->fields[$key]['notnull'] != 1))$out .= '<option value="0">&nbsp;</option>';
6042  foreach ($param['options'] as $key => $val)
6043  {
6044  if ((string) $key == '') continue;
6045  list($val, $parent) = explode('|', $val);
6046  $out .= '<option value="'.$key.'"';
6047  $out .= (((string) $value == (string) $key) ? ' selected' : '');
6048  $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
6049  $out .= '>'.$val.'</option>';
6050  }
6051  $out .= '</select>';
6052  } elseif ($type == 'sellist') {
6053  $out = '';
6054  if (!empty($conf->use_javascript_ajax) && !empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2)) {
6055  include_once DOL_DOCUMENT_ROOT.'/core/lib/ajax.lib.php';
6056  $out .= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
6057  }
6058 
6059  $out .= '<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '').'>';
6060  if (is_array($param['options'])) {
6061  $param_list = array_keys($param['options']);
6062  $InfoFieldList = explode(":", $param_list[0]);
6063  $parentName = '';
6064  $parentField = '';
6065  // 0 : tableName
6066  // 1 : label field name
6067  // 2 : key fields name (if differ of rowid)
6068  // 3 : key field parent (for dependent lists)
6069  // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
6070  $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
6071 
6072  if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
6073  if (strpos($InfoFieldList[4], 'extra.') !== false) {
6074  $keyList = 'main.'.$InfoFieldList[2].' as rowid';
6075  } else {
6076  $keyList = $InfoFieldList[2].' as rowid';
6077  }
6078  }
6079  if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
6080  list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
6081  $keyList .= ', '.$parentField;
6082  }
6083 
6084  $fields_label = explode('|', $InfoFieldList[1]);
6085  if (is_array($fields_label)) {
6086  $keyList .= ', ';
6087  $keyList .= implode(', ', $fields_label);
6088  }
6089 
6090  $sqlwhere = '';
6091  $sql = 'SELECT '.$keyList;
6092  $sql .= ' FROM '.MAIN_DB_PREFIX.$InfoFieldList[0];
6093  if (!empty($InfoFieldList[4]))
6094  {
6095  // can use SELECT request
6096  if (strpos($InfoFieldList[4], '$SEL$') !== false) {
6097  $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
6098  }
6099 
6100  // current object id can be use into filter
6101  if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
6102  $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
6103  } else {
6104  $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
6105  }
6106  //We have to join on extrafield table
6107  if (strpos($InfoFieldList[4], 'extra') !== false)
6108  {
6109  $sql .= ' as main, '.MAIN_DB_PREFIX.$InfoFieldList[0].'_extrafields as extra';
6110  $sqlwhere .= ' WHERE extra.fk_object=main.'.$InfoFieldList[2].' AND '.$InfoFieldList[4];
6111  } else {
6112  $sqlwhere .= ' WHERE '.$InfoFieldList[4];
6113  }
6114  } else {
6115  $sqlwhere .= ' WHERE 1=1';
6116  }
6117  // Some tables may have field, some other not. For the moment we disable it.
6118  if (in_array($InfoFieldList[0], array('tablewithentity')))
6119  {
6120  $sqlwhere .= ' AND entity = '.$conf->entity;
6121  }
6122  $sql .= $sqlwhere;
6123  //print $sql;
6124 
6125  $sql .= ' ORDER BY '.implode(', ', $fields_label);
6126 
6127  dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
6128  $resql = $this->db->query($sql);
6129  if ($resql)
6130  {
6131  $out .= '<option value="0">&nbsp;</option>';
6132  $num = $this->db->num_rows($resql);
6133  $i = 0;
6134  while ($i < $num)
6135  {
6136  $labeltoshow = '';
6137  $obj = $this->db->fetch_object($resql);
6138 
6139  // Several field into label (eq table:code|libelle:rowid)
6140  $notrans = false;
6141  $fields_label = explode('|', $InfoFieldList[1]);
6142  if (count($fields_label) > 1)
6143  {
6144  $notrans = true;
6145  foreach ($fields_label as $field_toshow)
6146  {
6147  $labeltoshow .= $obj->$field_toshow.' ';
6148  }
6149  } else {
6150  $labeltoshow = $obj->{$InfoFieldList[1]};
6151  }
6152  $labeltoshow = dol_trunc($labeltoshow, 45);
6153 
6154  if ($value == $obj->rowid)
6155  {
6156  foreach ($fields_label as $field_toshow)
6157  {
6158  $translabel = $langs->trans($obj->$field_toshow);
6159  if ($translabel != $obj->$field_toshow) {
6160  $labeltoshow = dol_trunc($translabel, 18).' ';
6161  } else {
6162  $labeltoshow = dol_trunc($obj->$field_toshow, 18).' ';
6163  }
6164  }
6165  $out .= '<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
6166  } else {
6167  if (!$notrans)
6168  {
6169  $translabel = $langs->trans($obj->{$InfoFieldList[1]});
6170  if ($translabel != $obj->{$InfoFieldList[1]}) {
6171  $labeltoshow = dol_trunc($translabel, 18);
6172  } else {
6173  $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
6174  }
6175  }
6176  if (empty($labeltoshow)) $labeltoshow = '(not defined)';
6177  if ($value == $obj->rowid)
6178  {
6179  $out .= '<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
6180  }
6181 
6182  if (!empty($InfoFieldList[3]) && $parentField)
6183  {
6184  $parent = $parentName.':'.$obj->{$parentField};
6185  $isDependList=1;
6186  }
6187 
6188  $out .= '<option value="'.$obj->rowid.'"';
6189  $out .= ($value == $obj->rowid ? ' selected' : '');
6190  $out .= (!empty($parent) ? ' parent="'.$parent.'"' : '');
6191  $out .= '>'.$labeltoshow.'</option>';
6192  }
6193 
6194  $i++;
6195  }
6196  $this->db->free($resql);
6197  } else {
6198  print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
6199  }
6200  }
6201  $out .= '</select>';
6202  } elseif ($type == 'checkbox') {
6203  $value_arr = explode(',', $value);
6204  $out = $form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options']) ?null:$param['options']), $value_arr, '', 0, '', 0, '100%');
6205  } elseif ($type == 'radio') {
6206  $out = '';
6207  foreach ($param['options'] as $keyopt => $val)
6208  {
6209  $out .= '<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam ? $moreparam : '');
6210  $out .= ' value="'.$keyopt.'"';
6211  $out .= ' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
6212  $out .= ($value == $keyopt ? 'checked' : '');
6213  $out .= '/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$val.'</label><br>';
6214  }
6215  } elseif ($type == 'chkbxlst') {
6216  if (is_array($value)) {
6217  $value_arr = $value;
6218  } else {
6219  $value_arr = explode(',', $value);
6220  }
6221 
6222  if (is_array($param['options'])) {
6223  $param_list = array_keys($param['options']);
6224  $InfoFieldList = explode(":", $param_list[0]);
6225  $parentName = '';
6226  $parentField = '';
6227  // 0 : tableName
6228  // 1 : label field name
6229  // 2 : key fields name (if differ of rowid)
6230  // 3 : key field parent (for dependent lists)
6231  // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
6232  $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2].' as rowid');
6233 
6234  if (count($InfoFieldList) > 3 && !empty($InfoFieldList[3])) {
6235  list ($parentName, $parentField) = explode('|', $InfoFieldList[3]);
6236  $keyList .= ', '.$parentField;
6237  }
6238  if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
6239  if (strpos($InfoFieldList[4], 'extra.') !== false) {
6240  $keyList = 'main.'.$InfoFieldList[2].' as rowid';
6241  } else {
6242  $keyList = $InfoFieldList[2].' as rowid';
6243  }
6244  }
6245 
6246  $fields_label = explode('|', $InfoFieldList[1]);
6247  if (is_array($fields_label)) {
6248  $keyList .= ', ';
6249  $keyList .= implode(', ', $fields_label);
6250  }
6251 
6252  $sqlwhere = '';
6253  $sql = 'SELECT '.$keyList;
6254  $sql .= ' FROM '.MAIN_DB_PREFIX.$InfoFieldList[0];
6255  if (!empty($InfoFieldList[4])) {
6256  // can use SELECT request
6257  if (strpos($InfoFieldList[4], '$SEL$') !== false) {
6258  $InfoFieldList[4] = str_replace('$SEL$', 'SELECT', $InfoFieldList[4]);
6259  }
6260 
6261  // current object id can be use into filter
6262  if (strpos($InfoFieldList[4], '$ID$') !== false && !empty($objectid)) {
6263  $InfoFieldList[4] = str_replace('$ID$', $objectid, $InfoFieldList[4]);
6264  } else {
6265  $InfoFieldList[4] = str_replace('$ID$', '0', $InfoFieldList[4]);
6266  }
6267 
6268  // We have to join on extrafield table
6269  if (strpos($InfoFieldList[4], 'extra') !== false) {
6270  $sql .= ' as main, '.MAIN_DB_PREFIX.$InfoFieldList[0].'_extrafields as extra';
6271  $sqlwhere .= ' WHERE extra.fk_object=main.'.$InfoFieldList[2].' AND '.$InfoFieldList[4];
6272  } else {
6273  $sqlwhere .= ' WHERE '.$InfoFieldList[4];
6274  }
6275  } else {
6276  $sqlwhere .= ' WHERE 1=1';
6277  }
6278  // Some tables may have field, some other not. For the moment we disable it.
6279  if (in_array($InfoFieldList[0], array('tablewithentity')))
6280  {
6281  $sqlwhere .= ' AND entity = '.$conf->entity;
6282  }
6283  // $sql.=preg_replace('/^ AND /','',$sqlwhere);
6284  // print $sql;
6285 
6286  $sql .= $sqlwhere;
6287  dol_syslog(get_class($this).'::showInputField type=chkbxlst', LOG_DEBUG);
6288  $resql = $this->db->query($sql);
6289  if ($resql) {
6290  $num = $this->db->num_rows($resql);
6291  $i = 0;
6292 
6293  $data = array();
6294 
6295  while ($i < $num) {
6296  $labeltoshow = '';
6297  $obj = $this->db->fetch_object($resql);
6298 
6299  $notrans = false;
6300  // Several field into label (eq table:code|libelle:rowid)
6301  $fields_label = explode('|', $InfoFieldList[1]);
6302  if (count($fields_label) > 1) {
6303  $notrans = true;
6304  foreach ($fields_label as $field_toshow) {
6305  $labeltoshow .= $obj->$field_toshow.' ';
6306  }
6307  } else {
6308  $labeltoshow = $obj->{$InfoFieldList[1]};
6309  }
6310  $labeltoshow = dol_trunc($labeltoshow, 45);
6311 
6312  if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
6313  foreach ($fields_label as $field_toshow) {
6314  $translabel = $langs->trans($obj->$field_toshow);
6315  if ($translabel != $obj->$field_toshow) {
6316  $labeltoshow = dol_trunc($translabel, 18).' ';
6317  } else {
6318  $labeltoshow = dol_trunc($obj->$field_toshow, 18).' ';
6319  }
6320  }
6321 
6322  $data[$obj->rowid] = $labeltoshow;
6323  } else {
6324  if (!$notrans) {
6325  $translabel = $langs->trans($obj->{$InfoFieldList[1]});
6326  if ($translabel != $obj->{$InfoFieldList[1]}) {
6327  $labeltoshow = dol_trunc($translabel, 18);
6328  } else {
6329  $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
6330  }
6331  }
6332  if (empty($labeltoshow)) {
6333  $labeltoshow = '(not defined)';
6334  }
6335 
6336  if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
6337  $data[$obj->rowid] = $labeltoshow;
6338  }
6339 
6340  if (!empty($InfoFieldList[3]) && $parentField) {
6341  $parent = $parentName.':'.$obj->{$parentField};
6342  $isDependList=1;
6343  }
6344 
6345  $data[$obj->rowid] = $labeltoshow;
6346  }
6347 
6348  $i++;
6349  }
6350  $this->db->free($resql);
6351 
6352  $out = $form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, '', 0, '', 0, '100%');
6353  } else {
6354  print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
6355  }
6356  }
6357  } elseif ($type == 'link') {
6358  $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath[:AddCreateButtonOrNot[:Filter]]'
6359  $param_list_array = explode(':', $param_list[0]);
6360  $showempty = (($required && $default != '') ? 0 : 1);
6361 
6362  if (!preg_match('/search_/', $keyprefix)) {
6363  if (!empty($param_list_array[2])) { // If the entry into $fields is set to add a create button
6364  if (!empty($this->fields[$key]['picto'])) {
6365  $morecss .= ' widthcentpercentminusxx';
6366  } else {
6367  $morecss .= ' widthcentpercentminusx';
6368  }
6369  } else {
6370  if (!empty($this->fields[$key]['picto'])) {
6371  $morecss .= ' widthcentpercentminusx';
6372  }
6373  }
6374  }
6375 
6376  $out = $form->selectForForms($param_list[0], $keyprefix.$key.$keysuffix, $value, $showempty, '', '', $morecss, $moreparam, 0, empty($val['disabled']) ? 0 : 1);
6377 
6378  if (!empty($param_list_array[2])) { // If the entry into $fields is set to add a create button
6379  if (!GETPOSTISSET('backtopage') && empty($val['disabled']) && empty($nonewbutton)) // To avoid to open several times the 'Create Object' button and to avoid to have button if field is protected by a "disabled".
6380  {
6381  list($class, $classfile) = explode(':', $param_list[0]);
6382  if (file_exists(dol_buildpath(dirname(dirname($classfile)).'/card.php'))) $url_path = dol_buildpath(dirname(dirname($classfile)).'/card.php', 1);
6383  else $url_path = dol_buildpath(dirname(dirname($classfile)).'/'.strtolower($class).'_card.php', 1);
6384  $paramforthenewlink = '';
6385  $paramforthenewlink .= (GETPOSTISSET('action') ? '&action='.GETPOST('action', 'aZ09') : '');
6386  $paramforthenewlink .= (GETPOSTISSET('id') ? '&id='.GETPOST('id', 'int') : '');
6387  $paramforthenewlink .= '&fk_'.strtolower($class).'=--IDFORBACKTOPAGE--';
6388  // TODO Add Javascript code to add input fields already filled into $paramforthenewlink so we won't loose them when going back to main page
6389  $out .= '<a class="butActionNew" title="'.$langs->trans("New").'" href="'.$url_path.'?action=create&backtopage='.urlencode($_SERVER['PHP_SELF'].($paramforthenewlink ? '?'.$paramforthenewlink : '')).'"><span class="fa fa-plus-circle valignmiddle"></span></a>';
6390  }
6391  }
6392  } elseif ($type == 'password') {
6393  // If prefix is 'search_', field is used as a filter, we use a common text field.
6394  $out = '<input type="'.($keyprefix == 'search_' ? 'text' : 'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam ? $moreparam : '').'>';
6395  } elseif ($type == 'array') {
6396  $newval = $val;
6397  $newval['type'] = 'varchar(256)';
6398 
6399  $out = '';
6400  if (!empty($value)) {
6401  foreach ($value as $option) {
6402  $out .= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
6403  $out .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $morecss).'<br></span>';
6404  }
6405  }
6406  $out .= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
6407 
6408  $newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
6409  $newInput .= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $morecss).'<br></span>';
6410 
6411  if (!empty($conf->use_javascript_ajax)) {
6412  $out .= '
6413  <script>
6414  $(document).ready(function() {
6415  $("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
6416  $("'.dol_escape_js($newInput).'").insertBefore(this);
6417  });
6418 
6419  $(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
6420  $(this).parent().remove();
6421  });
6422  });
6423  </script>';
6424  }
6425  }
6426  if (!empty($hidden)) {
6427  $out = '<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
6428  }
6429 
6430  if ($isDependList==1) {
6431  $out .= $this->getJSListDependancies('_common');
6432  }
6433  /* Add comments
6434  if ($type == 'date') $out.=' (YYYY-MM-DD)';
6435  elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
6436  */
6437  return $out;
6438  }
6439 
6453  public function showOutputField($val, $key, $value, $moreparam = '', $keysuffix = '', $keyprefix = '', $morecss = '')
6454  {
6455  global $conf, $langs, $form;
6456 
6457  if (!is_object($form))
6458  {
6459  require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
6460  $form = new Form($this->db);
6461  }
6462 
6463  $objectid = $this->id;
6464  $label = $val['label'];
6465  $type = $val['type'];
6466  $size = $val['css'];
6467  $reg = array();
6468 
6469  // Convert var to be able to share same code than showOutputField of extrafields
6470  if (preg_match('/varchar\((\d+)\)/', $type, $reg))
6471  {
6472  $type = 'varchar'; // convert varchar(xx) int varchar
6473  $size = $reg[1];
6474  } elseif (preg_match('/varchar/', $type)) $type = 'varchar'; // convert varchar(xx) int varchar
6475  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) $type = 'select';
6476  if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) $type = 'link';
6477 
6478  $default = $val['default'];
6479  $computed = $val['computed'];
6480  $unique = $val['unique'];
6481  $required = $val['required'];
6482  $param = array();
6483  $param['options'] = array();
6484 
6485  if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) $param['options'] = $val['arrayofkeyval'];
6486  if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) {
6487  $type = 'link';
6488  $param['options'] = array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
6489  } elseif (preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
6490  $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4] => 'N');
6491  $type = 'sellist';
6492  } elseif (preg_match('/^sellist:(.*):(.*):(.*)/i', $val['type'], $reg)) {
6493  $param['options'] = array($reg[1].':'.$reg[2].':'.$reg[3] => 'N');
6494  $type = 'sellist';
6495  } elseif (preg_match('/^sellist:(.*):(.*)/i', $val['type'], $reg)) {
6496  $param['options'] = array($reg[1].':'.$reg[2] => 'N');
6497  $type = 'sellist';
6498  }
6499 
6500  $langfile = $val['langfile'];
6501  $list = $val['list'];
6502  $help = $val['help'];
6503  $hidden = (($val['visible'] == 0) ? 1 : 0); // If zero, we are sure it is hidden, otherwise we show. If it depends on mode (view/create/edit form or list, this must be filtered by caller)
6504 
6505  if ($hidden) return '';
6506 
6507  // If field is a computed field, value must become result of compute
6508  if ($computed)
6509  {
6510  // Make the eval of compute string
6511  //var_dump($computed);
6512  $value = dol_eval($computed, 1, 0);
6513  }
6514 
6515  if (empty($morecss))
6516  {
6517  if ($type == 'date') {
6518  $morecss = 'minwidth100imp';
6519  } elseif ($type == 'datetime' || $type == 'timestamp') {
6520  $morecss = 'minwidth200imp';
6521  } elseif (in_array($type, array('int', 'double', 'price'))) {
6522  $morecss = 'maxwidth75';
6523  } elseif ($type == 'url') {
6524  $morecss = 'minwidth400';
6525  } elseif ($type == 'boolean') {
6526  $morecss = '';
6527  } else {
6528  if (round($size) < 12) {
6529  $morecss = 'minwidth100';
6530  } elseif (round($size) <= 48) {
6531  $morecss = 'minwidth200';
6532  } else {
6533  $morecss = 'minwidth400';
6534  }
6535  }
6536  }
6537 
6538  // Format output value differently according to properties of field
6539  if ($key == 'ref' && method_exists($this, 'getNomUrl')) $value = $this->getNomUrl(1, '', 0, '', 1);
6540  elseif ($key == 'status' && method_exists($this, 'getLibStatut')) $value = $this->getLibStatut(3);
6541  elseif ($type == 'date') {
6542  if (!empty($value)) {
6543  $value = dol_print_date($value, 'day'); // We suppose dates without time are always gmt (storage of course + output)
6544  } else {
6545  $value = '';
6546  }
6547  } elseif ($type == 'datetime' || $type == 'timestamp') {
6548  if (!empty($value)) {
6549  $value = dol_print_date($value, 'dayhour', 'tzuserrel');
6550  } else {
6551  $value = '';
6552  }
6553  } elseif ($type == 'duration') {
6554  include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
6555  if (!is_null($value) && $value !== '') {
6556  $value = convertSecondToTime($value, 'allhourmin');
6557  }
6558  } elseif ($type == 'double' || $type == 'real') {
6559  if (!is_null($value) && $value !== '') {
6560  $value = price($value);
6561  }
6562  } elseif ($type == 'boolean') {
6563  $checked = '';
6564  if (!empty($value)) {
6565  $checked = ' checked ';
6566  }
6567  $value = '<input type="checkbox" '.$checked.' '.($moreparam ? $moreparam : '').' readonly disabled>';
6568  } elseif ($type == 'mail') {
6569  $value = dol_print_email($value, 0, 0, 0, 64, 1, 1);
6570  } elseif ($type == 'url') {
6571  $value = dol_print_url($value, '_blank', 32, 1);
6572  } elseif ($type == 'phone') {
6573  $value = dol_print_phone($value, '', 0, 0, '', '&nbsp;', 1);
6574  } elseif ($type == 'price')
6575  {
6576  if (!is_null($value) && $value !== '') {
6577  $value = price($value, 0, $langs, 0, 0, -1, $conf->currency);
6578  }
6579  } elseif ($type == 'select') {
6580  $value = $param['options'][$value];
6581  } elseif ($type == 'sellist') {
6582  $param_list = array_keys($param['options']);
6583  $InfoFieldList = explode(":", $param_list[0]);
6584 
6585  $selectkey = "rowid";
6586  $keyList = 'rowid';
6587 
6588  if (count($InfoFieldList) > 4 && !empty($InfoFieldList[4])) {
6589  $selectkey = $InfoFieldList[2];
6590  $keyList = $InfoFieldList[2].' as rowid';
6591  }
6592 
6593  $fields_label = explode('|', $InfoFieldList[1]);
6594  if (is_array($fields_label)) {
6595  $keyList .= ', ';
6596  $keyList .= implode(', ', $fields_label);
6597  }
6598 
6599  $sql = 'SELECT '.$keyList;
6600  $sql .= ' FROM '.MAIN_DB_PREFIX.$InfoFieldList[0];
6601  if (strpos($InfoFieldList[4], 'extra') !== false)
6602  {
6603  $sql .= ' as main';
6604  }
6605  if ($selectkey == 'rowid' && empty($value)) {
6606  $sql .= " WHERE ".$selectkey."=0";
6607  } elseif ($selectkey == 'rowid') {
6608  $sql .= " WHERE ".$selectkey."=".$this->db->escape($value);
6609  } else {
6610  $sql .= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
6611  }
6612 
6613  //$sql.= ' AND entity = '.$conf->entity;
6614 
6615  dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
6616  $resql = $this->db->query($sql);
6617  if ($resql)
6618  {
6619  $value = ''; // value was used, so now we reste it to use it to build final output
6620 
6621  $obj = $this->db->fetch_object($resql);
6622 
6623  // Several field into label (eq table:code|libelle:rowid)
6624  $fields_label = explode('|', $InfoFieldList[1]);
6625 
6626  if (is_array($fields_label) && count($fields_label) > 1)
6627  {
6628  foreach ($fields_label as $field_toshow)
6629  {
6630  $translabel = '';
6631  if (!empty($obj->$field_toshow)) {
6632  $translabel = $langs->trans($obj->$field_toshow);
6633  }
6634  if ($translabel != $field_toshow) {
6635  $value .= dol_trunc($translabel, 18).' ';
6636  } else {
6637  $value .= $obj->$field_toshow.' ';
6638  }
6639  }
6640  } else {
6641  $translabel = '';
6642  if (!empty($obj->{$InfoFieldList[1]})) {
6643  $translabel = $langs->trans($obj->{$InfoFieldList[1]});
6644  }
6645  if ($translabel != $obj->{$InfoFieldList[1]}) {
6646  $value = dol_trunc($translabel, 18);
6647  } else {
6648  $value = $obj->{$InfoFieldList[1]};
6649  }
6650  }
6651  } else dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
6652  } elseif ($type == 'radio') {
6653  $value = $param['options'][$value];
6654  } elseif ($type == 'checkbox') {
6655  $value_arr = explode(',', $value);
6656  $value = '';
6657  if (is_array($value_arr) && count($value_arr) > 0)
6658  {
6659  $toprint = array();
6660  foreach ($value_arr as $keyval=>$valueval) {
6661  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$param['options'][$valueval].'</li>';
6662  }
6663  $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
6664  }
6665  } elseif ($type == 'chkbxlst') {
6666  $value_arr = explode(',', $value);
6667 
6668  $param_list = array_keys($param['options']);
6669  $InfoFieldList = explode(":", $param_list[0]);
6670 
6671  $selectkey = "rowid";
6672  $keyList = 'rowid';
6673 
6674  if (count($InfoFieldList) >= 3) {
6675  $selectkey = $InfoFieldList[2];
6676  $keyList = $InfoFieldList[2].' as rowid';
6677  }
6678 
6679  $fields_label = explode('|', $InfoFieldList[1]);
6680  if (is_array($fields_label)) {
6681  $keyList .= ', ';
6682  $keyList .= implode(', ', $fields_label);
6683  }
6684 
6685  $sql = 'SELECT '.$keyList;
6686  $sql .= ' FROM '.MAIN_DB_PREFIX.$InfoFieldList[0];
6687  if (strpos($InfoFieldList[4], 'extra') !== false) {
6688  $sql .= ' as main';
6689  }
6690  // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
6691  // $sql.= ' AND entity = '.$conf->entity;
6692 
6693  dol_syslog(get_class($this).':showOutputField:$type=chkbxlst', LOG_DEBUG);
6694  $resql = $this->db->query($sql);
6695  if ($resql) {
6696  $value = ''; // value was used, so now we reste it to use it to build final output
6697  $toprint = array();
6698  while ($obj = $this->db->fetch_object($resql)) {
6699  // Several field into label (eq table:code|libelle:rowid)
6700  $fields_label = explode('|', $InfoFieldList[1]);
6701  if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
6702  if (is_array($fields_label) && count($fields_label) > 1) {
6703  foreach ($fields_label as $field_toshow) {
6704  $translabel = '';
6705  if (!empty($obj->$field_toshow)) {
6706  $translabel = $langs->trans($obj->$field_toshow);
6707  }
6708  if ($translabel != $field_toshow) {
6709  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.dol_trunc($translabel, 18).'</li>';
6710  } else {
6711  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$obj->$field_toshow.'</li>';
6712  }
6713  }
6714  } else {
6715  $translabel = '';
6716  if (!empty($obj->{$InfoFieldList[1]})) {
6717  $translabel = $langs->trans($obj->{$InfoFieldList[1]});
6718  }
6719  if ($translabel != $obj->{$InfoFieldList[1]}) {
6720  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.dol_trunc($translabel, 18).'</li>';
6721  } else {
6722  $toprint[] = '<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #bbb">'.$obj->{$InfoFieldList[1]}.'</li>';
6723  }
6724  }
6725  }
6726  }
6727  $value = '<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
6728  } else {
6729  dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
6730  }
6731  } elseif ($type == 'link') {
6732  $out = '';
6733 
6734  // only if something to display (perf)
6735  if ($value)
6736  {
6737  $param_list = array_keys($param['options']); // $param_list='ObjectName:classPath'
6738 
6739  $InfoFieldList = explode(":", $param_list[0]);
6740  $classname = $InfoFieldList[0];
6741  $classpath = $InfoFieldList[1];
6742  $getnomurlparam = (empty($InfoFieldList[2]) ? 3 : $InfoFieldList[2]);
6743  if (!empty($classpath))
6744  {
6745  dol_include_once($InfoFieldList[1]);
6746  if ($classname && class_exists($classname))
6747  {
6748  $object = new $classname($this->db);
6749  $object->fetch($value);
6750  $value = $object->getNomUrl($getnomurlparam);
6751  }
6752  } else {
6753  dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6754  return 'Error bad setup of extrafield';
6755  }
6756  } else $value = '';
6757  } elseif (preg_match('/^(text|html)/', $type)) {
6758  $value = dol_htmlentitiesbr($value);
6759  } elseif ($type == 'password') {
6760  $value = preg_replace('/./i', '*', $value);
6761  } elseif ($type == 'array') {
6762  $value = implode('<br>', $value);
6763  }
6764 
6765  //print $type.'-'.$size.'-'.$value;
6766  $out = $value;
6767 
6768  return $out;
6769  }
6770 
6771 
6784  public function showOptionals($extrafields, $mode = 'view', $params = null, $keysuffix = '', $keyprefix = '', $onetrtd = 0)
6785  {
6786  global $db, $conf, $langs, $action, $form, $hookmanager;
6787 
6788  if (!is_object($form)) $form = new Form($db);
6789 
6790  $out = '';
6791 
6792  $parameters = array();
6793  $reshook = $hookmanager->executeHooks('showOptionals', $parameters, $this, $action); // Note that $action and $object may have been modified by hook
6794  if (empty($reshook))
6795  {
6796  if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label']) > 0)
6797  {
6798  $out .= "\n";
6799  $out .= '<!-- showOptionals --> ';
6800  $out .= "\n";
6801 
6802  $extrafields_collapse_num = '';
6803  $e = 0;
6804  foreach ($extrafields->attributes[$this->table_element]['label'] as $key=>$label)
6805  {
6806  // Show only the key field in params
6807  if (is_array($params) && array_key_exists('onlykey', $params) && $key != $params['onlykey']) continue;
6808 
6809  // Test on 'enabled' ('enabled' is different than 'list' = 'visibility')
6810  $enabled = 1;
6811  if ($enabled && isset($extrafields->attributes[$this->table_element]['enabled'][$key]))
6812  {
6813  $enabled = dol_eval($extrafields->attributes[$this->table_element]['enabled'][$key], 1);
6814  }
6815  if (empty($enabled)) continue;
6816 
6817  $visibility = 1;
6818  if ($visibility && isset($extrafields->attributes[$this->table_element]['list'][$key]))
6819  {
6820  $visibility = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1);
6821  }
6822 
6823  $perms = 1;
6824  if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key]))
6825  {
6826  $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1);
6827  }
6828 
6829  if (($mode == 'create') && abs($visibility) != 1 && abs($visibility) != 3) continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list
6830  elseif (($mode == 'edit') && abs($visibility) != 1 && abs($visibility) != 3 && abs($visibility) != 4) continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list and <> 4 = not visible at the creation
6831  elseif ($mode == 'view' && empty($visibility)) continue;
6832  if (empty($perms)) continue;
6833  // Load language if required
6834  if (!empty($extrafields->attributes[$this->table_element]['langfile'][$key])) {
6835  $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
6836  }
6837 
6838  $colspan = '';
6839  if (is_array($params) && count($params) > 0) {
6840  if (array_key_exists('cols', $params)) {
6841  $colspan = $params['cols'];
6842  } elseif (array_key_exists('colspan', $params)) { // For backward compatibility. Use cols instead now.
6843  $reg = array();
6844  if (preg_match('/colspan="(\d+)"/', $params['colspan'], $reg)) {
6845  $colspan = $reg[1];
6846  } else {
6847  $colspan = $params['colspan'];
6848  }
6849  }
6850  }
6851 
6852  switch ($mode) {
6853  case "view":
6854  $value = $this->array_options["options_".$key.$keysuffix]; // Value may be clean or formated later
6855  break;
6856  case "create":
6857  case "edit":
6858  // We get the value of property found with GETPOST so it takes into account:
6859  // default values overwrite, restore back to list link, ... (but not 'default value in database' of field)
6860  $check = 'alphanohtml';
6861  if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text'))) {
6862  $check = 'restricthtml';
6863  }
6864  $getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, $check, 3); // GETPOST can get value from GET, POST or setup of default values overwrite.
6865  // GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
6866  if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix))
6867  {
6868  if (is_array($getposttemp)) {
6869  // $getposttemp is an array but following code expects a comma separated string
6870  $value = implode(",", $getposttemp);
6871  } else {
6872  $value = $getposttemp;
6873  }
6874  } else {
6875  $value = $this->array_options["options_".$key]; // No GET, no POST, no default value, so we take value of object.
6876  }
6877  //var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
6878  break;
6879  }
6880 
6881  // Output value of the current field
6882  if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate')
6883  {
6884  $extrafields_collapse_num = '';
6885  $extrafield_param = $extrafields->attributes[$this->table_element]['param'][$key];
6886  if (!empty($extrafield_param) && is_array($extrafield_param)) {
6887  $extrafield_param_list = array_keys($extrafield_param['options']);
6888 
6889  if (count($extrafield_param_list) > 0) {
6890  $extrafield_collapse_display_value = intval($extrafield_param_list[0]);
6891 
6892  if ($extrafield_collapse_display_value == 1 || $extrafield_collapse_display_value == 2) {
6893  $extrafields_collapse_num = $extrafields->attributes[$this->table_element]['pos'][$key];
6894  }
6895  }
6896  }
6897 
6898  $out .= $extrafields->showSeparator($key, $this, ($colspan + 1));
6899  } else {
6900  $class = (!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
6901  $csstyle = '';
6902  if (is_array($params) && count($params) > 0) {
6903  if (array_key_exists('class', $params)) {
6904  $class .= $params['class'].' ';
6905  }
6906  if (array_key_exists('style', $params)) {
6907  $csstyle = $params['style'];
6908  }
6909  }
6910 
6911  // add html5 elements
6912  $domData = ' data-element="extrafield"';
6913  $domData .= ' data-targetelement="'.$this->element.'"';
6914  $domData .= ' data-targetid="'.$this->id.'"';
6915 
6916  $html_id = (empty($this->id) ? '' : 'extrarow-'.$this->element.'_'.$key.'_'.$this->id);
6917 
6918  if (!empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0) { $colspan = '0'; }
6919 
6920  if ($action == 'selectlines') { $colspan++; }
6921 
6922  // Convert date into timestamp format (value in memory must be a timestamp)
6923  if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date')))
6924  {
6925  $datenotinstring = $this->array_options['options_'.$key];
6926  if (!is_numeric($this->array_options['options_'.$key])) // For backward compatibility
6927  {
6928  $datenotinstring = $this->db->jdate($datenotinstring);
6929  }
6930  $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)) ? dol_mktime(12, 0, 0, GETPOST($keyprefix.'options_'.$key.$keysuffix."month", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."day", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."year", 'int', 3)) : $datenotinstring;
6931  }
6932  if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('datetime')))
6933  {
6934  $datenotinstring = $this->array_options['options_'.$key];
6935  if (!is_numeric($this->array_options['options_'.$key])) // For backward compatibility
6936  {
6937  $datenotinstring = $this->db->jdate($datenotinstring);
6938  }
6939  $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)) ? dol_mktime(GETPOST($keyprefix.'options_'.$key.$keysuffix."hour", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."min", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."sec", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."month", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."day", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."year", 'int', 3), 'tzuserrel') : $datenotinstring;
6940  }
6941  // Convert float submited string into real php numeric (value in memory must be a php numeric)
6942  if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('price', 'double')))
6943  {
6944  $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) ? price2num($value) : $this->array_options['options_'.$key];
6945  }
6946 
6947  // HTML, text, select, integer and varchar: take into account default value in database if in create mode
6948  if (in_array($extrafields->attributes[$this->table_element]['type'][$key], array('html', 'text', 'varchar', 'select', 'int')))
6949  {
6950  if ($action == 'create') $value = (GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix) || $value) ? $value : $extrafields->attributes[$this->table_element]['default'][$key];
6951  }
6952 
6953  $labeltoshow = $langs->trans($label);
6954  $helptoshow = $langs->trans($extrafields->attributes[$this->table_element]['help'][$key]);
6955 
6956  $out .= '<tr '.($html_id ? 'id="'.$html_id.'" ' : '').$csstyle.' class="'.$class.$this->element.'_extras_'.$key.' trextrafields_collapse'.$extrafields_collapse_num.'" '.$domData.' >';
6957  if (!empty($conf->global->MAIN_VIEW_LINE_NUMBER) && $action == 'view') {
6958  $out .= '<td></td>';
6959  }
6960  $out .= '<td class="wordbreak';
6961  //$out .= "titlefield";
6962  //if (GETPOST('action', 'restricthtml') == 'create') $out.='create';
6963  // BUG #11554 : For public page, use red dot for required fields, instead of bold label
6964  $tpl_context = isset($params["tpl_context"]) ? $params["tpl_context"] : "none";
6965  if ($tpl_context == "public") { // Public page : red dot instead of fieldrequired characters
6966  $out .= '">';
6967  if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) $out .= $form->textwithpicto($labeltoshow, $helptoshow);
6968  else $out .= $labeltoshow;
6969  if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) $out .= '&nbsp;<font color="red">*</font>';
6970  } else {
6971  if ($mode != 'view' && !empty($extrafields->attributes[$this->table_element]['required'][$key])) $out .= ' fieldrequired';
6972  $out .= '">';
6973  if (!empty($extrafields->attributes[$this->table_element]['help'][$key])) $out .= $form->textwithpicto($labeltoshow, $helptoshow);
6974  else $out .= $labeltoshow;
6975  }
6976  $out .= '</td>';
6977 
6978  $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
6979 
6980  $out .= '<td '.($html_id ? 'id="'.$html_id.'" ' : '').'class="'.$this->element.'_extras_'.$key.'" '.($colspan ? ' colspan="'.$colspan.'"' : '').'>';
6981 
6982  switch ($mode) {
6983  case "view":
6984  $out .= $extrafields->showOutputField($key, $value);
6985  break;
6986  case "create":
6987  $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
6988  break;
6989  case "edit":
6990  $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id, $this->table_element);
6991  break;
6992  }
6993 
6994  $out .= '</td>';
6995 
6996  /*for($ii = 0; $ii < ($colspan - 1); $ii++)
6997  {
6998  $out .='<td class="'.$this->element.'_extras_'.$key.'"></td>';
6999  }*/
7000 
7001  if (!empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) $out .= '</tr>';
7002  else $out .= '</tr>';
7003  $e++;
7004  }
7005  }
7006  $out .= "\n";
7007  // Add code to manage list depending on others
7008  if (!empty($conf->use_javascript_ajax)) {
7009  $out .= $this->getJSListDependancies();
7010  }
7011 
7012  $out .= '<!-- /showOptionals --> '."\n";
7013  }
7014  }
7015 
7016  $out .= $hookmanager->resPrint;
7017 
7018  return $out;
7019  }
7020 
7025  public function getJSListDependancies($type = '_extra')
7026  {
7027  $out .= '
7028  <script>
7029  jQuery(document).ready(function() {
7030  function showOptions'.$type.'(child_list, parent_list, orig_select)
7031  {
7032  var val = $("select[name=\""+parent_list+"\"]").val();
7033  var parentVal = parent_list + ":" + val;
7034  if(val > 0) {
7035  var options = orig_select.find("option[parent=\""+parentVal+"\"]").clone();
7036  $("select[name=\""+child_list+"\"] option[parent]").remove();
7037  $("select[name=\""+child_list+"\"]").append(options);
7038  } else {
7039  var options = orig_select.find("option[parent]").clone();
7040  $("select[name=\""+child_list+"\"] option[parent]").remove();
7041  $("select[name=\""+child_list+"\"]").append(options);
7042  }
7043  }
7044  function setListDependencies'.$type.'() {
7045  jQuery("select option[parent]").parent().each(function() {
7046  var orig_select = {};
7047  var child_list = $(this).attr("name");
7048  orig_select[child_list] = $(this).clone();
7049  var parent = $(this).find("option[parent]:first").attr("parent");
7050  var infos = parent.split(":");
7051  var parent_list = infos[0];
7052  $("select[name=\""+parent_list+"\"]").change(function() {
7053  showOptions'.$type.'(child_list, parent_list, orig_select[child_list]);
7054  });
7055  });
7056  }
7057 
7058  setListDependencies'.$type.'();
7059  });
7060  </script>'."\n";
7061  return $out;
7062  }
7063 
7068  public function getRights()
7069  {
7070  global $user;
7071 
7072  $element = $this->element;
7073  if ($element == 'facturerec') $element = 'facture';
7074 
7075  return $user->rights->{$element};
7076  }
7077 
7090  public static function commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors = 0)
7091  {
7092  foreach ($tables as $table)
7093  {
7094  $sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_soc = '.$dest_id.' WHERE fk_soc = '.$origin_id;
7095 
7096  if (!$db->query($sql))
7097  {
7098  if ($ignoreerrors) return true; // TODO Not enough. If there is A-B on kept thirdarty and B-C on old one, we must get A-B-C after merge. Not A-B.
7099  //$this->errors = $db->lasterror();
7100  return false;
7101  }
7102  }
7103 
7104  return true;
7105  }
7106 
7119  public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
7120  {
7121  global $conf;
7122 
7123  $buyPrice = 0;
7124 
7125  if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) // In most cases, test here is false
7126  {
7127  $buyPrice = $unitPrice * (1 - $discountPercent / 100);
7128  } else {
7129  // Get cost price for margin calculation
7130  if (!empty($fk_product))
7131  {
7132  if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice')
7133  {
7134  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7135  $product = new Product($this->db);
7136  $result = $product->fetch($fk_product);
7137  if ($result <= 0)
7138  {
7139  $this->errors[] = 'ErrorProductIdDoesNotExists';
7140  return -1;
7141  }
7142  if ($product->cost_price > 0)
7143  {
7144  $buyPrice = $product->cost_price;
7145  } elseif ($product->pmp > 0)
7146  {
7147  $buyPrice = $product->pmp;
7148  }
7149  } elseif (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp')
7150  {
7151  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
7152  $product = new Product($this->db);
7153  $result = $product->fetch($fk_product);
7154  if ($result <= 0)
7155  {
7156  $this->errors[] = 'ErrorProductIdDoesNotExists';
7157  return -1;
7158  }
7159  if ($product->pmp > 0)
7160  {
7161  $buyPrice = $product->pmp;
7162  }
7163  }
7164 
7165  if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1', 'pmp', 'costprice')))
7166  {
7167  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
7168  $productFournisseur = new ProductFournisseur($this->db);
7169  if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0)
7170  {
7171  $buyPrice = $productFournisseur->fourn_unitprice;
7172  } elseif ($result < 0)
7173  {
7174  $this->errors[] = $productFournisseur->error;
7175  return -2;
7176  }
7177  }
7178  }
7179  }
7180  return $buyPrice;
7181  }
7182 
7183  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
7201  public function show_photos($modulepart, $sdir, $size = 0, $nbmax = 0, $nbbyrow = 5, $showfilename = 0, $showaction = 0, $maxHeight = 120, $maxWidth = 160, $nolink = 0, $notitle = 0, $usesharelink = 0)
7202  {
7203  // phpcs:enable
7204  global $conf, $user, $langs;
7205 
7206  include_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
7207  include_once DOL_DOCUMENT_ROOT.'/core/lib/images.lib.php';
7208 
7209  $sortfield = 'position_name';
7210  $sortorder = 'asc';
7211 
7212  $dir = $sdir.'/';
7213  $pdir = '/';
7214 
7215  $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
7216  $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart);
7217 
7218  // For backward compatibility
7219  if ($modulepart == 'product') {
7220  if (!empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) {
7221  $dir = $sdir.'/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
7222  $pdir = '/'.get_exdir($this->id, 2, 0, 0, $this, $modulepart).$this->id."/photos/";
7223  }
7224  }
7225 
7226  // Defined relative dir to DOL_DATA_ROOT
7227  $relativedir = '';
7228  if ($dir) {
7229  $relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT, '/').'/', '', $dir);
7230  $relativedir = preg_replace('/^[\\/]/', '', $relativedir);
7231  $relativedir = preg_replace('/[\\/]$/', '', $relativedir);
7232  }
7233 
7234  $dirthumb = $dir.'thumbs/';
7235  $pdirthumb = $pdir.'thumbs/';
7236 
7237  $return = '<!-- Photo -->'."\n";
7238  $nbphoto = 0;
7239 
7240  $filearray = dol_dir_list($dir, "files", 0, '', '(\.meta|_preview.*\.png)$', $sortfield, (strtolower($sortorder) == 'desc' ?SORT_DESC:SORT_ASC), 1);
7241 
7242  /*if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) // For backward compatiblity, we scan also old dirs
7243  {
7244  $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
7245  $filearray=array_merge($filearray, $filearrayold);
7246  }*/
7247 
7248  completeFileArrayWithDatabaseInfo($filearray, $relativedir);
7249 
7250  if (count($filearray)) {
7251  if ($sortfield && $sortorder) {
7252  $filearray = dol_sort_array($filearray, $sortfield, $sortorder);
7253  }
7254 
7255  foreach ($filearray as $key => $val) {
7256  $photo = '';
7257  $file = $val['name'];
7258 
7259  //if (! utf8_check($file)) $file=utf8_encode($file); // To be sure file is stored in UTF8 in memory
7260 
7261  //if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
7262  if (image_format_supported($file) >= 0) {
7263  $nbphoto++;
7264  $photo = $file;
7265  $viewfilename = $file;
7266 
7267  if ($size == 1 || $size == 'small') { // Format vignette
7268  // Find name of thumb file
7269  $photo_vignette = basename(getImageFileNameForSize($dir.$file, '_small'));
7270  if (!dol_is_file($dirthumb.$photo_vignette)) $photo_vignette = '';
7271 
7272  // Get filesize of original file
7273  $imgarray = dol_getImageSize($dir.$photo);
7274 
7275  if ($nbbyrow > 0)
7276  {
7277  if ($nbphoto == 1) $return .= '<table class="valigntop center centpercent" style="border: 0; padding: 2px; border-spacing: 2px; border-collapse: separate;">';
7278 
7279  if ($nbphoto % $nbbyrow == 1) $return .= '<tr class="center valignmiddle" style="border: 1px">';
7280  $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%" class="photo">';
7281  } elseif ($nbbyrow < 0) $return .= '<div class="inline-block">';
7282 
7283  $return .= "\n";
7284 
7285  $relativefile = preg_replace('/^\//', '', $pdir.$photo);
7286  if (empty($nolink))
7287  {
7288  $urladvanced = getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
7289  if ($urladvanced) $return .= '<a href="'.$urladvanced.'">';
7290  else $return .= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank">';
7291  }
7292 
7293  // Show image (width height=$maxHeight)
7294  // Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
7295  $alt = $langs->transnoentitiesnoconv('File').': '.$relativefile;
7296  $alt .= ' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
7297  if ($notitle) $alt = '';
7298 
7299  if ($usesharelink) {
7300  if ($val['share']) {
7301  if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight) {
7302  $return .= '<!-- Show original file (thumb not yet available with shared links) -->';
7303  $return .= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
7304  } else {
7305  $return .= '<!-- Show original file -->';
7306  $return .= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
7307  }
7308  } else {
7309  $return .= '<!-- Show nophoto file (because file is not shared) -->';
7310  $return .= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
7311  }
7312  } else {
7313  if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight) {
7314  $return .= '<!-- Show thumb -->';
7315  $return .= '<img class="photo photowithmargin maxwidth150onsmartphone" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdirthumb.$photo_vignette).'" title="'.dol_escape_htmltag($alt).'">';
7316  } else {
7317  $return .= '<!-- Show original file -->';
7318  $return .= '<img class="photo photowithmargin" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" title="'.dol_escape_htmltag($alt).'">';
7319  }
7320  }
7321 
7322  if (empty($nolink)) $return .= '</a>';
7323  $return .= "\n";
7324 
7325  if ($showfilename) $return .= '<br>'.$viewfilename;
7326  if ($showaction)
7327  {
7328  $return .= '<br>';
7329  // On propose la generation de la vignette si elle n'existe pas et si la taille est superieure aux limites
7330  if ($photo_vignette && (image_format_supported($photo) > 0) && ($this->imgWidth > $maxWidth || $this->imgHeight > $maxHeight))
7331  {
7332  $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=addthumb&amp;file='.urlencode($pdir.$viewfilename).'">'.img_picto($langs->trans('GenerateThumb'), 'refresh').'&nbsp;&nbsp;</a>';
7333  }
7334  // Special cas for product
7335  if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
7336  {
7337  // Link to resize
7338  $return .= '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?modulepart='.urlencode('produit|service').'&id='.$this->id.'&amp;file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"), 'resize', '').'</a> &nbsp; ';
7339 
7340  // Link to delete
7341  $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;token='.newToken().'&amp;file='.urlencode($pdir.$viewfilename).'">';
7342  $return .= img_delete().'</a>';
7343  }
7344  }
7345  $return .= "\n";
7346 
7347  if ($nbbyrow > 0)
7348  {
7349  $return .= '</td>';
7350  if (($nbphoto % $nbbyrow) == 0) $return .= '</tr>';
7351  } elseif ($nbbyrow < 0) $return .= '</div>';
7352  }
7353 
7354  if (empty($size)) { // Format origine
7355  $return .= '<img class="photo photowithmargin" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
7356 
7357  if ($showfilename) $return .= '<br>'.$viewfilename;
7358  if ($showaction)
7359  {
7360  // Special case for product
7361  if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
7362  {
7363  // Link to resize
7364  $return .= '<a href="'.DOL_URL_ROOT.'/core/photos_resize.php?modulepart='.urlencode('produit|service').'&id='.$this->id.'&amp;file='.urlencode($pdir.$viewfilename).'" title="'.dol_escape_htmltag($langs->trans("Resize")).'">'.img_picto($langs->trans("Resize"), 'resize', '').'</a> &nbsp; ';
7365 
7366  // Link to delete
7367  $return .= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;token='.newToken().'&amp;file='.urlencode($pdir.$viewfilename).'">';
7368  $return .= img_delete().'</a>';
7369  }
7370  }
7371  }
7372 
7373  // On continue ou on arrete de boucler ?
7374  if ($nbmax && $nbphoto >= $nbmax) break;
7375  }
7376  }
7377 
7378  if ($size == 1 || $size == 'small')
7379  {
7380  if ($nbbyrow > 0)
7381  {
7382  // Ferme tableau
7383  while ($nbphoto % $nbbyrow)
7384  {
7385  $return .= '<td style="width: '.ceil(100 / $nbbyrow).'%">&nbsp;</td>';
7386  $nbphoto++;
7387  }
7388 
7389  if ($nbphoto) $return .= '</table>';
7390  }
7391  }
7392  }
7393 
7394  $this->nbphoto = $nbphoto;
7395 
7396  return $return;
7397  }
7398 
7399 
7406  protected function isArray($info)
7407  {
7408  if (is_array($info)) {
7409  if (isset($info['type']) && $info['type'] == 'array') return true;
7410  else return false;
7411  }
7412  return false;
7413  }
7414 
7421  public function isDate($info)
7422  {
7423  if (isset($info['type']) && ($info['type'] == 'date' || $info['type'] == 'datetime' || $info['type'] == 'timestamp')) return true;
7424  return false;
7425  }
7426 
7433  public function isDuration($info)
7434  {
7435  if (is_array($info)) {
7436  if (isset($info['type']) && ($info['type'] == 'duration')) return true;
7437  else return false;
7438  } else return false;
7439  }
7440 
7447  public function isInt($info)
7448  {
7449  if (is_array($info)) {
7450  if (isset($info['type']) && ($info['type'] == 'int' || preg_match('/^integer/i', $info['type']))) return true;
7451  else return false;
7452  } else return false;
7453  }
7454 
7461  public function isFloat($info)
7462  {
7463  if (is_array($info)) {
7464  if (isset($info['type']) && (preg_match('/^(double|real|price)/i', $info['type']))) return true;
7465  else return false;
7466  }
7467  return false;
7468  }
7469 
7476  public function isText($info)
7477  {
7478  if (is_array($info)) {
7479  if (isset($info['type']) && $info['type'] == 'text') return true;
7480  else return false;
7481  }
7482  return false;
7483  }
7484 
7491  protected function canBeNull($info)
7492  {
7493  if (is_array($info)) {
7494  if (isset($info['notnull']) && $info['notnull'] != '1') return true;
7495  else return false;
7496  }
7497  return true;
7498  }
7499 
7506  protected function isForcedToNullIfZero($info)
7507  {
7508  if (is_array($info)) {
7509  if (isset($info['notnull']) && $info['notnull'] == '-1') return true;
7510  else return false;
7511  }
7512  return false;
7513  }
7514 
7521  protected function isIndex($info)
7522  {
7523  if (is_array($info)) {
7524  if (isset($info['index']) && $info['index'] == true) return true;
7525  else return false;
7526  }
7527  return false;
7528  }
7529 
7530 
7538  protected function setSaveQuery()
7539  {
7540  global $conf;
7541 
7542  $queryarray = array();
7543  foreach ($this->fields as $field => $info) // Loop on definition of fields
7544  {
7545  // Depending on field type ('datetime', ...)
7546  if ($this->isDate($info))
7547  {
7548  if (empty($this->{$field})) {
7549  $queryarray[$field] = null;
7550  } else {
7551  $queryarray[$field] = $this->db->idate($this->{$field});
7552  }
7553  } elseif ($this->isArray($info))
7554  {
7555  if (!empty($this->{$field})) {
7556  if (!is_array($this->{$field})) {
7557  $this->{$field} = array($this->{$field});
7558  }
7559  $queryarray[$field] = serialize($this->{$field});
7560  } else {
7561  $queryarray[$field] = null;
7562  }
7563  } elseif ($this->isDuration($info))
7564  {
7565  // $this->{$field} may be null, '', 0, '0', 123, '123'
7566  if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
7567  if (!isset($this->{$field})) {
7568  $queryarray[$field] = 0;
7569  } else {
7570  $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
7571  }
7572  }
7573  else $queryarray[$field] = null;
7574  } elseif ($this->isInt($info) || $this->isFloat($info))
7575  {
7576  if ($field == 'entity' && is_null($this->{$field})) $queryarray[$field] = $conf->entity;
7577  else {
7578  // $this->{$field} may be null, '', 0, '0', 123, '123'
7579  if ((isset($this->{$field}) && $this->{$field} != '') || !empty($info['notnull'])) {
7580  if (!isset($this->{$field})) {
7581  $queryarray[$field] = 0;
7582  } elseif ($this->isInt($info)) {
7583  $queryarray[$field] = (int) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
7584  } elseif ($this->isFloat($info)) {
7585  $queryarray[$field] = (double) $this->{$field}; // If '0', it may be set to null later if $info['notnull'] == -1
7586  }
7587  } else $queryarray[$field] = null;
7588  }
7589  } else {
7590  $queryarray[$field] = $this->{$field};
7591  }
7592 
7593  if ($info['type'] == 'timestamp' && empty($queryarray[$field])) unset($queryarray[$field]);
7594  if (!empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) $queryarray[$field] = null; // May force 0 to null
7595  }
7596 
7597  return $queryarray;
7598  }
7599 
7606  public function setVarsFromFetchObj(&$obj)
7607  {
7608  global $db;
7609 
7610  foreach ($this->fields as $field => $info)
7611  {
7612  if ($this->isDate($info)) {
7613  if (is_null($obj->{$field}) || $obj->{$field} === '' || $obj->{$field} === '0000-00-00 00:00:00' || $obj->{$field} === '1000-01-01 00:00:00') $this->{$field} = '';
7614  else $this->{$field} = $db->jdate($obj->{$field});
7615  } elseif ($this->isArray($info))
7616  {
7617  if (!empty($obj->{$field})) {
7618  $this->{$field} = @unserialize($obj->{$field});
7619  // Hack for data not in UTF8
7620  if ($this->{$field } === false) @unserialize(utf8_decode($obj->{$field}));
7621  } else {
7622  $this->{$field} = array();
7623  }
7624  } elseif ($this->isInt($info)) {
7625  if ($field == 'rowid') $this->id = (int) $obj->{$field};
7626  else {
7627  if ($this->isForcedToNullIfZero($info)) {
7628  if (empty($obj->{$field})) $this->{$field} = null;
7629  else $this->{$field} = (double) $obj->{$field};
7630  } else {
7631  if (!is_null($obj->{$field}) || (isset($info['notnull']) && $info['notnull'] == 1)) {
7632  $this->{$field} = (int) $obj->{$field};
7633  } else {
7634  $this->{$field} = null;
7635  }
7636  }
7637  }
7638  } elseif ($this->isFloat($info)) {
7639  if ($this->isForcedToNullIfZero($info)) {
7640  if (empty($obj->{$field})) $this->{$field} = null;
7641  else $this->{$field} = (double) $obj->{$field};
7642  } else {
7643  if (!is_null($obj->{$field}) || (isset($info['notnull']) && $info['notnull'] == 1)) {
7644  $this->{$field} = (double) $obj->{$field};
7645  } else {
7646  $this->{$field} = null;
7647  }
7648  }
7649  } else {
7650  $this->{$field} = $obj->{$field};
7651  }
7652  }
7653 
7654  // If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
7655  if (!isset($this->fields['ref']) && isset($this->id)) $this->ref = $this->id;
7656  }
7657 
7663  protected function getFieldList()
7664  {
7665  $keys = array_keys($this->fields);
7666  return implode(',', $keys);
7667  }
7668 
7676  protected function quote($value, $fieldsentry)
7677  {
7678  if (is_null($value)) return 'NULL';
7679  elseif (preg_match('/^(int|double|real|price)/i', $fieldsentry['type'])) return $this->db->escape("$value");
7680  elseif ($fieldsentry['type'] == 'boolean') {
7681  if ($value) return 'true';
7682  else return 'false';
7683  }
7684  else return "'".$this->db->escape($value)."'";
7685  }
7686 
7687 
7695  public function createCommon(User $user, $notrigger = false)
7696  {
7697  global $langs;
7698  dol_syslog(get_class($this)."::createCommon create", LOG_DEBUG);
7699 
7700  $error = 0;
7701 
7702  $now = dol_now();
7703 
7704  $fieldvalues = $this->setSaveQuery();
7705 
7706  if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) $fieldvalues['date_creation'] = $this->db->idate($now);
7707  if (array_key_exists('fk_user_creat', $fieldvalues) && !($fieldvalues['fk_user_creat'] > 0)) $fieldvalues['fk_user_creat'] = $user->id;
7708  unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
7709  if (array_key_exists('ref', $fieldvalues)) $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
7710 
7711  $keys = array();
7712  $values = array(); // Array to store string forged for SQL syntax
7713  foreach ($fieldvalues as $k => $v) {
7714  $keys[$k] = $k;
7715  $value = $this->fields[$k];
7716  $values[$k] = $this->quote($v, $value); // May return string 'NULL' if $value is null
7717  }
7718 
7719  // Clean and check mandatory
7720  foreach ($keys as $key)
7721  {
7722  // If field is an implicit foreign key field
7723  if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key] = '';
7724  if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key] = '';
7725 
7726  if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && is_null($this->fields[$key]['default']))
7727  {
7728  $error++;
7729  $this->errors[] = $langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7730  }
7731 
7732  // If value is null and there is a default value for field
7733  if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && (!isset($values[$key]) || $values[$key] === 'NULL') && !is_null($this->fields[$key]['default']))
7734  {
7735  $values[$key] = $this->fields[$key]['default'];
7736  }
7737 
7738  // If field is an implicit foreign key field
7739  if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) {
7740  if (isset($this->fields[$key]['default'])) $values[$key] = $this->fields[$key]['default'];
7741  else $values[$key] = 'null';
7742  }
7743  if (!empty($this->fields[$key]['foreignkey']) && empty($values[$key])) $values[$key] = 'null';
7744  }
7745 
7746  if ($error) return -1;
7747 
7748  $this->db->begin();
7749 
7750  if (!$error)
7751  {
7752  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
7753  $sql .= ' ('.implode(", ", $keys).')';
7754  $sql .= ' VALUES ('.implode(", ", $values).')';
7755 
7756  $res = $this->db->query($sql);
7757  if ($res === false) {
7758  $error++;
7759  $this->errors[] = $this->db->lasterror();
7760  }
7761  }
7762 
7763  if (!$error)
7764  {
7765  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.$this->table_element);
7766  }
7767 
7768  // If we have a field ref with a default value of (PROV)
7769  if (!$error)
7770  {
7771  if (key_exists('ref', $this->fields) && $this->fields['ref']['notnull'] > 0 && !is_null($this->fields['ref']['default']) && $this->fields['ref']['default'] == '(PROV)')
7772  {
7773  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ref = '(PROV".$this->id.")' WHERE (ref = '(PROV)' OR ref = '') AND rowid = ".$this->id;
7774  $resqlupdate = $this->db->query($sql);
7775 
7776  if ($resqlupdate === false)
7777  {
7778  $error++;
7779  $this->errors[] = $this->db->lasterror();
7780  } else {
7781  $this->ref = '(PROV'.$this->id.')';
7782  }
7783  }
7784  }
7785 
7786  // Create extrafields
7787  if (!$error)
7788  {
7789  $result = $this->insertExtraFields();
7790  if ($result < 0) $error++;
7791  }
7792 
7793  // Create lines
7794  if (!empty($this->table_element_line) && !empty($this->fk_element))
7795  {
7796  $num = (is_array($this->lines) ? count($this->lines) : 0);
7797  for ($i = 0; $i < $num; $i++)
7798  {
7799  $line = $this->lines[$i];
7800 
7801  $keyforparent = $this->fk_element;
7802  $line->$keyforparent = $this->id;
7803 
7804  // Test and convert into object this->lines[$i]. When coming from REST API, we may still have an array
7805  //if (! is_object($line)) $line=json_decode(json_encode($line), false); // convert recursively array into object.
7806  if (!is_object($line)) $line = (object) $line;
7807 
7808  $result = $line->create($user, 1);
7809  if ($result < 0)
7810  {
7811  $this->error = $this->db->lasterror();
7812  $this->db->rollback();
7813  return -1;
7814  }
7815  }
7816  }
7817 
7818  // Triggers
7819  if (!$error && !$notrigger)
7820  {
7821  // Call triggers
7822  $result = $this->call_trigger(strtoupper(get_class($this)).'_CREATE', $user);
7823  if ($result < 0) { $error++; }
7824  // End call triggers
7825  }
7826 
7827  // Commit or rollback
7828  if ($error) {
7829  $this->db->rollback();
7830  return -1;
7831  } else {
7832  $this->db->commit();
7833  return $this->id;
7834  }
7835  }
7836 
7837 
7846  public function fetchCommon($id, $ref = null, $morewhere = '')
7847  {
7848  if (empty($id) && empty($ref) && empty($morewhere)) return -1;
7849 
7850  $fieldlist = $this->getFieldList();
7851  if (empty($fieldlist)) return 0;
7852 
7853  $sql = 'SELECT '.$fieldlist;
7854  $sql .= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
7855 
7856  if (!empty($id)) $sql .= ' WHERE rowid = '.$id;
7857  elseif (!empty($ref)) $sql .= " WHERE ref = ".$this->quote($ref, $this->fields['ref']);
7858  else $sql .= ' WHERE 1 = 1'; // usage with empty id and empty ref is very rare
7859  if (empty($id) && isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) $sql .= ' AND entity IN ('.getEntity($this->table_element).')';
7860  if ($morewhere) $sql .= $morewhere;
7861  $sql .= ' LIMIT 1'; // This is a fetch, to be sure to get only one record
7862 
7863  $res = $this->db->query($sql);
7864  if ($res)
7865  {
7866  $obj = $this->db->fetch_object($res);
7867  if ($obj)
7868  {
7869  $this->setVarsFromFetchObj($obj);
7870 
7871  // Retrieve all extrafield
7872  // fetch optionals attributes and labels
7873  $this->fetch_optionals();
7874 
7875  return $this->id;
7876  } else {
7877  return 0;
7878  }
7879  } else {
7880  $this->error = $this->db->lasterror();
7881  $this->errors[] = $this->error;
7882  return -1;
7883  }
7884  }
7885 
7892  public function fetchLinesCommon($morewhere = '')
7893  {
7894  $objectlineclassname = get_class($this).'Line';
7895  if (!class_exists($objectlineclassname))
7896  {
7897  $this->error = 'Error, class '.$objectlineclassname.' not found during call of fetchLinesCommon';
7898  return -1;
7899  }
7900 
7901  $objectline = new $objectlineclassname($this->db);
7902 
7903  $sql = 'SELECT '.$objectline->getFieldList();
7904  $sql .= ' FROM '.MAIN_DB_PREFIX.$objectline->table_element;
7905  $sql .= ' WHERE fk_'.$this->element.' = '.$this->id;
7906  if ($morewhere) $sql .= $morewhere;
7907  if (isset($objectline->fields['position'])) {
7908  $sql .= $this->db->order('position', 'ASC');
7909  }
7910 
7911  $resql = $this->db->query($sql);
7912  if ($resql)
7913  {
7914  $num_rows = $this->db->num_rows($resql);
7915  $i = 0;
7916  while ($i < $num_rows)
7917  {
7918  $obj = $this->db->fetch_object($resql);
7919  if ($obj)
7920  {
7921  $newline = new $objectlineclassname($this->db);
7922  $newline->setVarsFromFetchObj($obj);
7923 
7924  $this->lines[] = $newline;
7925  }
7926  $i++;
7927  }
7928 
7929  return 1;
7930  } else {
7931  $this->error = $this->db->lasterror();
7932  $this->errors[] = $this->error;
7933  return -1;
7934  }
7935  }
7936 
7944  public function updateCommon(User $user, $notrigger = false)
7945  {
7946  global $conf, $langs;
7947  dol_syslog(get_class($this)."::updateCommon update", LOG_DEBUG);
7948 
7949  $error = 0;
7950 
7951  $now = dol_now();
7952 
7953  $fieldvalues = $this->setSaveQuery();
7954 
7955  if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) $fieldvalues['date_modification'] = $this->db->idate($now);
7956  if (array_key_exists('fk_user_modif', $fieldvalues) && !($fieldvalues['fk_user_modif'] > 0)) $fieldvalues['fk_user_modif'] = $user->id;
7957  unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
7958  if (array_key_exists('ref', $fieldvalues)) $fieldvalues['ref'] = dol_string_nospecial($fieldvalues['ref']); // If field is a ref, we sanitize data
7959 
7960  // Add quotes and escape on fields with type string
7961  $keys = array();
7962  $values = array();
7963  $tmp = array();
7964  foreach ($fieldvalues as $k => $v) {
7965  $keys[$k] = $k;
7966  $value = $this->fields[$k];
7967  $values[$k] = $this->quote($v, $value);
7968  $tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
7969  }
7970 
7971  // Clean and check mandatory fields
7972  foreach ($keys as $key)
7973  {
7974  if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key] = ''; // This is an implicit foreign key field
7975  if (!empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key] = ''; // This is an explicit foreign key field
7976 
7977  //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
7978  /*
7979  if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
7980  {
7981  $error++;
7982  $this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7983  }*/
7984  }
7985 
7986  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET '.implode(', ', $tmp).' WHERE rowid='.$this->id;
7987 
7988  $this->db->begin();
7989  if (!$error)
7990  {
7991  $res = $this->db->query($sql);
7992  if ($res === false)
7993  {
7994  $error++;
7995  $this->errors[] = $this->db->lasterror();
7996  }
7997  }
7998 
7999  // Update extrafield
8000  if (!$error)
8001  {
8002  $result = $this->insertExtraFields();
8003  if ($result < 0)
8004  {
8005  $error++;
8006  }
8007  }
8008 
8009  // Triggers
8010  if (!$error && !$notrigger)
8011  {
8012  // Call triggers
8013  $result = $this->call_trigger(strtoupper(get_class($this)).'_MODIFY', $user);
8014  if ($result < 0) { $error++; } //Do also here what you must do to rollback action if trigger fail
8015  // End call triggers
8016  }
8017 
8018  // Commit or rollback
8019  if ($error) {
8020  $this->db->rollback();
8021  return -1;
8022  } else {
8023  $this->db->commit();
8024  return $this->id;
8025  }
8026  }
8027 
8036  public function deleteCommon(User $user, $notrigger = false, $forcechilddeletion = 0)
8037  {
8038  dol_syslog(get_class($this)."::deleteCommon delete", LOG_DEBUG);
8039 
8040  $error = 0;
8041 
8042  $this->db->begin();
8043 
8044  if ($forcechilddeletion) // Force also delete of childtables that should lock deletion in standard case when option force is off
8045  {
8046  foreach ($this->childtables as $table)
8047  {
8048  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$table.' WHERE '.$this->fk_element.' = '.$this->id;
8049  $resql = $this->db->query($sql);
8050  if (!$resql)
8051  {
8052  $this->error = $this->db->lasterror();
8053  $this->errors[] = $this->error;
8054  $this->db->rollback();
8055  return -1;
8056  }
8057  }
8058  } elseif (!empty($this->fk_element) && !empty($this->childtables)) // If object has childs linked with a foreign key field, we check all child tables.
8059  {
8060  $objectisused = $this->isObjectUsed($this->id);
8061  if (!empty($objectisused))
8062  {
8063  dol_syslog(get_class($this)."::deleteCommon Can't delete record as it has some child", LOG_WARNING);
8064  $this->error = 'ErrorRecordHasChildren';
8065  $this->errors[] = $this->error;
8066  $this->db->rollback();
8067  return 0;
8068  }
8069  }
8070 
8071  // Delete cascade first
8072  if (is_array($this->childtablesoncascade) && !empty($this->childtablesoncascade)) {
8073  foreach ($this->childtablesoncascade as $table)
8074  {
8075  $deleteFromObject = explode(':', $table);
8076  if (count($deleteFromObject) >= 2) {
8077  $className = str_replace('@', '', $deleteFromObject[0]);
8078  $filePath = $deleteFromObject[1];
8079  $columnName = $deleteFromObject[2];
8080  if (dol_include_once($filePath)) {
8081  $childObject = new $className($this->db);
8082  if (method_exists($childObject, 'deleteByParentField')) {
8083  $result = $childObject->deleteByParentField($this->id, $columnName);
8084  if ($result < 0) {
8085  $error++;
8086  $this->errors[] = $childObject->error;
8087  break;
8088  }
8089  } else {
8090  $error++;
8091  $this->errors[] = "You defined a cascade delete on an object $childObject but there is no method deleteByParentField for it";
8092  break;
8093  }
8094  } else {
8095  $error++;
8096  $this->errors[] = 'Cannot include child class file '.$filePath;
8097  break;
8098  }
8099  } else {
8100  // Delete record in child table
8101  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$table.' WHERE '.$this->fk_element.' = '.$this->id;
8102 
8103  $resql = $this->db->query($sql);
8104  if (!$resql) {
8105  $error++;
8106  $this->error = $this->db->lasterror();
8107  $this->errors[] = $this->error;
8108  break;
8109  }
8110  }
8111  }
8112  }
8113 
8114  if (!$error) {
8115  if (!$notrigger) {
8116  // Call triggers
8117  $result = $this->call_trigger(strtoupper(get_class($this)).'_DELETE', $user);
8118  if ($result < 0) { $error++; } // Do also here what you must do to rollback action if trigger fail
8119  // End call triggers
8120  }
8121  }
8122 
8123  // Delete llx_ecm_files
8124  if (!$error) {
8125  $res = $this->deleteEcmFiles(1); // Deleting files physically is done later with the dol_delete_dir_recursive
8126  if (!$res) {
8127  $error++;
8128  }
8129  }
8130 
8131  if (!$error && !empty($this->isextrafieldmanaged))
8132  {
8133  $result = $this->deleteExtraFields();
8134  if ($result < 0) { $error++; }
8135  }
8136 
8137  if (!$error)
8138  {
8139  $sql = 'DELETE FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE rowid='.$this->id;
8140 
8141  $res = $this->db->query($sql);
8142  if ($res === false) {
8143  $error++;
8144  $this->errors[] = $this->db->lasterror();
8145  }
8146  }
8147 
8148  // Commit or rollback
8149  if ($error) {
8150  $this->db->rollback();
8151  return -1;
8152  } else {
8153  $this->db->commit();
8154  return 1;
8155  }
8156  }
8157 
8166  public function deleteByParentField($parentId = 0, $parentField = '')
8167  {
8168  global $user;
8169 
8170  $error = 0;
8171  $deleted = 0;
8172 
8173  if (!empty($parentId) && !empty($parentField)) {
8174  $this->db->begin();
8175 
8176  $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$this->table_element;
8177  $sql .= ' WHERE '.$parentField.' = '.(int) $parentId;
8178 
8179  $resql = $this->db->query($sql);
8180  if (!$resql) {
8181  $this->errors[] = $this->db->lasterror();
8182  $error++;
8183  } else {
8184  while ($obj = $this->db->fetch_object($resql)) {
8185  $result = $this->fetch($obj->rowid);
8186  if ($result < 0) {
8187  $error++;
8188  $this->errors[] = $this->error;
8189  } else {
8190  if (get_class($this) == 'Contact') { // TODO special code because delete() for contact has not been standardized like other delete.
8191  $result = $this->delete();
8192  } else {
8193  $result = $this->delete($user);
8194  }
8195  if ($result < 0) {
8196  $error++;
8197  $this->errors[] = $this->error;
8198  } else {
8199  $deleted++;
8200  }
8201  }
8202  }
8203  }
8204 
8205  if (empty($error)) {
8206  $this->db->commit();
8207  return $deleted;
8208  } else {
8209  $this->error = implode(', ', $this->errors);
8210  $this->db->rollback();
8211  return $error * -1;
8212  }
8213  }
8214 
8215  return $deleted;
8216  }
8217 
8226  public function deleteLineCommon(User $user, $idline, $notrigger = false)
8227  {
8228  global $conf;
8229 
8230  $error = 0;
8231 
8232  $tmpforobjectclass = get_class($this);
8233  $tmpforobjectlineclass = ucfirst($tmpforobjectclass).'Line';
8234 
8235  // Call trigger
8236  $result = $this->call_trigger('LINE'.strtoupper($tmpforobjectclass).'_DELETE', $user);
8237  if ($result < 0) return -1;
8238  // End call triggers
8239 
8240  $this->db->begin();
8241 
8242  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element_line;
8243  $sql .= " WHERE rowid=".$idline;
8244 
8245  dol_syslog(get_class($this)."::deleteLineCommon", LOG_DEBUG);
8246  $resql = $this->db->query($sql);
8247  if (!$resql)
8248  {
8249  $this->error = "Error ".$this->db->lasterror();
8250  $error++;
8251  }
8252 
8253  if (empty($error)) {
8254  // Remove extrafields
8255  $tmpobjectline = new $tmpforobjectlineclass($this->db);
8256  if (!isset($tmpobjectline->isextrafieldmanaged) || !empty($tmpobjectline->isextrafieldmanaged)) {
8257  $tmpobjectline->id = $idline;
8258  $result = $tmpobjectline->deleteExtraFields();
8259  if ($result < 0)
8260  {
8261  $error++;
8262  $this->error = "Error ".get_class($this)."::deleteLineCommon deleteExtraFields error -4 ".$tmpobjectline->error;
8263  }
8264  }
8265  }
8266 
8267  if (empty($error)) {
8268  $this->db->commit();
8269  return 1;
8270  } else {
8271  dol_syslog(get_class($this)."::deleteLineCommon ERROR:".$this->error, LOG_ERR);
8272  $this->db->rollback();
8273  return -1;
8274  }
8275  }
8276 
8277 
8287  public function setStatusCommon($user, $status, $notrigger = 0, $triggercode = '')
8288  {
8289  $error = 0;
8290 
8291  $this->db->begin();
8292 
8293  $statusfield = 'status';
8294  if ($this->element == 'don' || $this->element == 'donation') $statusfield = 'fk_statut';
8295 
8296  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
8297  $sql .= " SET ".$statusfield." = ".((int) $status);
8298  $sql .= " WHERE rowid = ".$this->id;
8299 
8300  if ($this->db->query($sql))
8301  {
8302  if (!$error)
8303  {
8304  $this->oldcopy = clone $this;
8305  }
8306 
8307  if (!$error && !$notrigger) {
8308  // Call trigger
8309  $result = $this->call_trigger($triggercode, $user);
8310  if ($result < 0) $error++;
8311  }
8312 
8313  if (!$error) {
8314  $this->status = $status;
8315  $this->db->commit();
8316  return 1;
8317  } else {
8318  $this->db->rollback();
8319  return -1;
8320  }
8321  } else {
8322  $this->error = $this->db->error();
8323  $this->db->rollback();
8324  return -1;
8325  }
8326  }
8327 
8328 
8335  public function initAsSpecimenCommon()
8336  {
8337  global $user;
8338 
8339  $this->id = 0;
8340  $this->specimen = 1;
8341  $fields = array(
8342  'label' => 'This is label',
8343  'ref' => 'ABCD1234',
8344  'description' => 'This is a description',
8345  'qty' => 123.12,
8346  'note_public' => 'Public note',
8347  'note_private' => 'Private note',
8348  'date_creation' => (dol_now() - 3600 * 48),
8349  'date_modification' => (dol_now() - 3600 * 24),
8350  'fk_user_creat' => $user->id,
8351  'fk_user_modif' => $user->id,
8352  'date' => dol_now(),
8353  );
8354  foreach ($fields as $key => $value) {
8355  if (array_key_exists($key, $this->fields)) $this->{$key} = $value;
8356  }
8357  return 1;
8358  }
8359 
8360 
8361  /* Part for comments */
8362 
8367  public function fetchComments()
8368  {
8369  require_once DOL_DOCUMENT_ROOT.'/core/class/comment.class.php';
8370 
8371  $comment = new Comment($this->db);
8372  $result = $comment->fetchAllFor($this->element, $this->id);
8373  if ($result < 0) {
8374  $this->errors = array_merge($this->errors, $comment->errors);
8375  return -1;
8376  } else {
8377  $this->comments = $comment->comments;
8378  }
8379  return count($this->comments);
8380  }
8381 
8387  public function getNbComments()
8388  {
8389  return count($this->comments);
8390  }
8391 
8398  public function trimParameters($parameters)
8399  {
8400  if (!is_array($parameters)) return;
8401  foreach ($parameters as $parameter) {
8402  if (isset($this->$parameter)) {
8403  $this->$parameter = trim($this->$parameter);
8404  }
8405  }
8406  }
8407 
8408  /* Part for categories/tags */
8409 
8420  public function getCategoriesCommon($type_categ)
8421  {
8422  require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
8423 
8424  // Get current categories
8425  $c = new Categorie($this->db);
8426  $existing = $c->containing($this->id, $type_categ, 'id');
8427 
8428  return $existing;
8429  }
8430 
8442  public function setCategoriesCommon($categories, $type_categ)
8443  {
8444  require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
8445 
8446  // Handle single category
8447  if (!is_array($categories)) {
8448  $categories = array($categories);
8449  }
8450 
8451  // Get current categories
8452  $c = new Categorie($this->db);
8453  $existing = $c->containing($this->id, $type_categ, 'id');
8454 
8455  // Diff
8456  if (is_array($existing)) {
8457  $to_del = array_diff($existing, $categories);
8458  $to_add = array_diff($categories, $existing);
8459  } else {
8460  $to_del = array(); // Nothing to delete
8461  $to_add = $categories;
8462  }
8463 
8464  $error = 0;
8465 
8466  // Process
8467  foreach ($to_del as $del) {
8468  if ($c->fetch($del) > 0) {
8469  $c->del_type($this, $type_categ);
8470  }
8471  }
8472  foreach ($to_add as $add) {
8473  if ($c->fetch($add) > 0)
8474  {
8475  $result = $c->add_type($this, $type_categ);
8476  if ($result < 0)
8477  {
8478  $error++;
8479  $this->error = $c->error;
8480  $this->errors = $c->errors;
8481  break;
8482  }
8483  }
8484  }
8485 
8486  return $error ? -1 : 1;
8487  }
8488 
8497  public function cloneCategories($fromId, $toId, $type = '')
8498  {
8499  $this->db->begin();
8500 
8501  if (empty($type)) $type = $this->table_element;
8502 
8503  require_once DOL_DOCUMENT_ROOT.'/categories/class/categorie.class.php';
8504  $categorystatic = new Categorie($this->db);
8505 
8506  $sql = "INSERT INTO ".MAIN_DB_PREFIX."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type])." (fk_categorie, fk_product)";
8507  $sql .= " SELECT fk_categorie, $toId FROM ".MAIN_DB_PREFIX."categorie_".(empty($categorystatic->MAP_CAT_TABLE[$type]) ? $type : $categorystatic->MAP_CAT_TABLE[$type]);
8508  $sql .= " WHERE fk_product = ".((int) $fromId);
8509 
8510  if (!$this->db->query($sql))
8511  {
8512  $this->error = $this->db->lasterror();
8513  $this->db->rollback();
8514  return -1;
8515  }
8516 
8517  $this->db->commit();
8518  return 1;
8519  }
8520 
8527  public function deleteEcmFiles($mode = 0)
8528  {
8529  global $conf;
8530 
8531  $this->db->begin();
8532 
8533  // Delete in database with mode 0
8534  if ($mode == 0) {
8535  switch ($this->element) {
8536  case 'propal':
8537  $element = 'propale';
8538  break;
8539  case 'product':
8540  $element = 'produit';
8541  break;
8542  case 'order_supplier':
8543  $element = 'fournisseur/commande';
8544  break;
8545  case 'invoice_supplier':
8546  $element = 'fournisseur/facture/'.get_exdir($this->id, 2, 0, 1, $this, 'invoice_supplier');
8547  break;
8548  case 'shipping':
8549  $element = 'expedition/sending';
8550  break;
8551  default:
8552  $element = $this->element;
8553  }
8554 
8555  // Delete ecm_files extrafields
8556  $sql = "DELETE FROM ".MAIN_DB_PREFIX."ecm_files_extrafields WHERE fk_object IN (";
8557  $sql .= " SELECT rowid FROM ".MAIN_DB_PREFIX."ecm_files WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
8558  $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".$conf->entity; // No need of getEntity here
8559  $sql .= ")";
8560 
8561  if (!$this->db->query($sql)) {
8562  $this->error = $this->db->lasterror();
8563  $this->db->rollback();
8564  return false;
8565  }
8566 
8567  // Delete ecm_files
8568  $sql = "DELETE FROM ".MAIN_DB_PREFIX."ecm_files";
8569  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%'";
8570  $sql .= " AND filepath = '".$this->db->escape($element)."/".$this->db->escape($this->ref)."' AND entity = ".$conf->entity; // No need of getEntity here
8571 
8572  if (!$this->db->query($sql)) {
8573  $this->error = $this->db->lasterror();
8574  $this->db->rollback();
8575  return false;
8576  }
8577  }
8578 
8579  // Delete in database with mode 1
8580  if ($mode == 1) {
8581  $sql = 'DELETE FROM '.MAIN_DB_PREFIX."ecm_files_extrafields";
8582  $sql .= " WHERE fk_object IN (SELECT rowid FROM ".MAIN_DB_PREFIX."ecm_files WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? '' : '@'.$this->module))."' AND src_object_id = ".$this->id.")";
8583  $resql = $this->db->query($sql);
8584  if (!$resql) {
8585  $this->error = $this->db->lasterror();
8586  $this->db->rollback();
8587  return false;
8588  }
8589 
8590  $sql = 'DELETE FROM '.MAIN_DB_PREFIX."ecm_files";
8591  $sql .= " WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? '' : '@'.$this->module))."' AND src_object_id = ".$this->id;
8592  $resql = $this->db->query($sql);
8593  if (!$resql) {
8594  $this->error = $this->db->lasterror();
8595  $this->db->rollback();
8596  return false;
8597  }
8598  }
8599 
8600  $this->db->commit();
8601  return true;
8602  }
8603 }
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname= '')
Make an include_once using default root and alternate root if it fails.
getCategoriesCommon($type_categ)
Sets object to given categories.
deleteCommon(User $user, $notrigger=false, $forcechilddeletion=0)
Delete object in database.
setExtraParameters()
Set extra parameters.
getFullAddress($withcountry=0, $sep="\n", $withregion=0, $extralangcode= '')
Return full address of contact.
getKanbanView($option= '')
Return clicable link of object (with eventually picto)
getIdContact($source, $code, $status=0)
Return id of contacts for a source and a contact code.
static isExistingObject($element, $id, $ref= '', $ref_ext= '')
Check an object id/ref exists If you don&#39;t need/want to instantiate object and just need to know if o...
if(!empty($arrayfields['u.datec']['checked'])) print_liste_field_titre("DateCreationShort"u if(!empty($arrayfields['u.tms']['checked'])) print_liste_field_titre("DateModificationShort"u if(!empty($arrayfields['u.statut']['checked'])) print_liste_field_titre("Status"u statut
Definition: list.php:632
isFloat($info)
Function test if type is float.
if(!empty($arrayfields['country.code_iso']['checked'])) print_liste_field_titre($arrayfields['country.code_iso']['label'] country if(!empty($arrayfields['typent.code']['checked'])) print_liste_field_titre($arrayfields['typent.code']['label'] typent code
Definition: list.php:566
isInt($info)
Function test if type is integer.
printObjectLines($action, $seller, $buyer, $selected=0, $dateSelector=0, $defaulttpldir= '/core/tpl')
Return HTML table for object lines TODO Move this into an output class file (htmlline.class.php) If lines are into a template, title must also be into a template But for the moment we don&#39;t know if it&#39;s possible as we keep a method available on overloaded objects.
dol_format_address($object, $withcountry=0, $sep="\n", $outputlangs= '', $mode=0, $extralangcode= '')
Return a formated address (part address/zip/town/state) according to country rules.
setUpperOrLowerCase()
Set to upper or ucwords/lower if needed.
fetchLinesCommon($morewhere= '')
Load object in memory from the database.
isText($info)
Function test if type is text.
updateRangOfLine($rowid, $rang)
Update position of line (rang)
</td >< tdcolspan="3">< spanclass="opacitymedium"></span ></td ></tr >< trclass="liste_total"> CREANCES DETTES< tdcolspan="3"class="right"></td >< tdcolspan="3"class="right"></td ></tr > CREANCES DETTES RECETTES DEPENSES trips CREANCES DETTES Y m expensereport p date_valid Y m expensereport pe datep $db idate($date_start)."' AND $column < p rowid
foreach($object->fields as $key=> $val) if(is_array($extrafields->attributes[$object->table_element]['label'])&&count($extrafields->attributes[$object->table_element]['label']) > 0) $object fields
cloneCategories($fromId, $toId, $type= '')
Copy related categories to another object.
dol_now($mode= 'auto')
Return date for now.
line_order($renum=false, $rowidorder= 'ASC', $fk_parent_line=true)
Save a new position (field rang) for details lines.
setCategoriesCommon($categories, $type_categ)
Sets object to given categories.
delete_linked_contact($source= '', $code= '')
Delete all links between an object $this and all its contacts.
Class to manage Dolibarr users.
Definition: user.class.php:44
dol_strtoupper($string, $encoding="UTF-8")
Convert a string to upper.
setProject($projectid)
Link element with a project.
add_contact($fk_socpeople, $type_contact, $source= 'external', $notrigger=0)
Add a link between element $this-&gt;element and a contact.
swapContactStatus($rowid)
Update status of a contact linked to object.
isDate($info)
Function test if type is date.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
delete_resource($rowid, $element, $notrigger=0)
Delete a link to resource line.
quote($value, $fieldsentry)
Add quote to field value if necessary.
initAsSpecimenCommon()
Initialise object with example values Id must be 0 if object instance is a specimen.
createCommon(User $user, $notrigger=false)
Create object into database.
getFullName($langs, $option=0, $nameorder=-1, $maxlen=0)
Return full name (civility+&#39; &#39;+name+&#39; &#39;+lastname)
isArray($info)
Function test if type is array.
$conf db name
Only used if Module[ID]Name translation string is not found.
Definition: repair.php:108
canBeNull($info)
Function test if field can be null.
$conf db
API class for accounts.
Definition: inc.php:54
image_format_supported($file, $acceptsvg=0)
Return if a filename is file name of a supported image format.
Definition: images.lib.php:39
insertExtraFields($trigger= '', $userused=null)
Add/Update all extra fields values for the current object.
setVarsFromFetchObj(&$obj)
Function to load data from a SQL pointer into properties of current object $this. ...
getRangOfLine($rowid)
Get position of line (rang)
dol_string_nospecial($str, $newstr= '_', $badcharstoreplace= '')
Clean a string from all punctuation characters to use it as a ref or login.
fetchComments()
Load comments linked with current task.
Class to manage categories.
update_note($note, $suffix= '')
Update note of element.
line_max($fk_parent_line=0)
Get max value used for position of line (rang)
setBankAccount($fk_account, $notrigger=false, $userused=null)
Change the bank account.
load_previous_next_ref($filter, $fieldid, $nodbprefix=0)
Load properties id_previous and id_next by comparing $fieldid with $this-&gt;ref.
$conf db user
Definition: repair.php:109
if(!empty($arrayfields['s.nom']['checked'])) print_liste_field_titre($arrayfields['s.nom']['label'] s nom
Definition: list.php:560
deleteEcmFiles($mode=0)
Delete related files of object in database.
updateObjectLinked($sourceid=null, $sourcetype= '', $targetid=null, $targettype= '')
Update object linked of a current object.
showOptionals($extrafields, $mode= 'view', $params=null, $keysuffix= '', $keyprefix= '', $onetrtd=0)
Function to show lines of extrafields with output datas.
img_picto($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt= '', $morecss= '', $marginleftonlyshort=2)
Show picto whatever it&#39;s its name (generic function)
getFormatedSupplierRef($objref)
Return supplier ref for screen output.
getBannerAddress($htmlkey, $object)
Return full address for banner.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename= '', $restricttologhandler= '', $logcontext=null)
Write log message into outputs.
getChildrenOfLine($id, $includealltree=0)
Get children of line.
getFormatedCustomerRef($objref)
Return customer ref for screen output.
setWarehouse($warehouse_id)
Change the warehouse.
add_element_resource($resource_id, $resource_type, $busy=0, $mandatory=0)
Add resources to the current object : add entry into llx_element_resources Need $this-&gt;element &amp; $thi...
deleteExtraFields()
Delete all extra fields values for the current object.
updateCommon(User $user, $notrigger=false)
Update object into database.
getState($id, $withcode= '', $dbtouse=0, $withregion=0, $outputlangs= '', $entconv=1)
Return state translated from an id.
setShippingMethod($shipping_method_id, $notrigger=false, $userused=null)
Change the shipping method.
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this-&gt;array_options This method is in most cases call...
deleteObjectLinked($sourceid=null, $sourcetype= '', $targetid=null, $targettype= '', $rowid= '')
Delete all links between an object $this.
liste_contact($status=-1, $source= 'external', $list=0, $code= '')
Get array of all contacts for an object.
setDocModel($user, $modelpdf)
Set last model used by doc generator.
dol_ucwords($string, $encoding="UTF-8")
Convert first character of all the words of a string to upper.
Class to manage comment.
deleteLineCommon(User $user, $idline, $notrigger=false)
Delete a line of object in database.
setStatut($status, $elementId=null, $elementType= '', $trigkey= '')
Set status of an object.
if(!empty($conf->facture->enabled)&&$user->rights->facture->lire) if((!empty($conf->fournisseur->enabled)&&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)||!empty($conf->supplier_invoice->enabled))&&$user->rights->fournisseur->facture->lire) if(!empty($conf->don->enabled)&&$user->rights->don->lire) if(!empty($conf->tax->enabled)&&$user->rights->tax->charges->lire) if(!empty($conf->facture->enabled)&&!empty($conf->commande->enabled)&&$user->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) if(!empty($conf->facture->enabled)&&$user->rights->facture->lire) if((!empty($conf->fournisseur->enabled)&&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)||!empty($conf->supplier_invoice->enabled))&&$user->rights->fournisseur->facture->lire) $resql
Social contributions to pay.
Definition: index.php:1232
dol_print_error($db= '', $error= '', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
isForcedToNullIfZero($info)
Function test if field is forced to null if zero or empty.
newToken()
Return the value of token currently saved into session with name &#39;newtoken&#39;.
setStatusCommon($user, $status, $notrigger=0, $triggercode= '')
Set to a status.
getFieldList()
Function to concat keys of fields.
print $_SERVER["PHP_SELF"] n
Edit parameters.
Definition: categories.php:101
dol_trunc($string, $size=40, $trunc= 'right', $stringencoding= 'UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding &#39;...&#39; if string larger than length.
update_ref_ext($ref_ext)
Update external ref of element.
getNbComments()
Return nb comments already posted.
isIndex($info)
Function test if is indexed.
deleteByParentField($parentId=0, $parentField= '')
Delete all child object from a parent ID.
add_object_linked($origin=null, $origin_id=null)
Add objects linked in llx_element_element.
errorsToString()
Method to output saved errors.
update_price($exclspec=0, $roundingadjust= 'none', $nodatabaseupdate=0, $seller=null)
Update total_ht, total_ttc, total_vat, total_localtax1, total_localtax2 for an object (sum of lines)...
img_delete($titlealt= 'default', $other= 'class="pictodelete"', $morecss= '')
Show delete logo.
setSaveQuery()
Function to prepare a part of the query for insert.
isDuration($info)
Function test if type is duration.
if(!defined('CSRFCHECK_WITH_TOKEN')) define('CSRFCHECK_WITH_TOKEN'
Draft customers invoices.
getAdvancedPreviewUrl($modulepart, $relativepath, $alldata=0, $param= '')
Return URL we can use for advanced preview links.
delete_contact($rowid, $notrigger=0)
Delete a link to contact line.
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition: repair.php:105
getCountry($searchkey, $withcode= '', $dbtouse=0, $outputlangs= '', $entconv=1, $searchlabel= '')
Return country label, code or id from an id, code or label.
Parent class of all other business classes (invoices, contracts, proposals, orders, ...)
if(!empty($search_group)) natural_search(array("g.nom"g note
Definition: list.php:122
dol_strtolower($string, $encoding="UTF-8")
Convert a string to lower.
fetchCommon($id, $ref=null, $morewhere= '')
Load object in memory from the database.
dolGetFirstLastname($firstname, $lastname, $nameorder=-1)
Return firstname and lastname in correct order.
trimParameters($parameters)
Trim object parameters.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $keepmoretags= '', $escapeonlyhtmltags=0)
Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields...
insertExtraLanguages($trigger= '', $userused=null)
Add/Update all extra fields values for the current object.