dolibarr  9.0.0
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-2015 Juanjo Menent <jmenent@2byte.es>
5  * Copyright (C) 2012-2013 Christophe Battarel <christophe.battarel@altairis.fr>
6  * Copyright (C) 2011-2018 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.dolibarr@gmail.com>
11  * Copyright (C) 2016 Bahfir abbes <dolipar@dolipar.org>
12  * Copyright (C) 2017 ATM Consulting <support@atm-consulting.fr>
13  * Copyright (C) 2017 Nicolas ZABOURI <info@inovea-conseil.com>
14  * Copyright (C) 2017 Rui Strecht <rui.strecht@aliartalentos.com>
15  * Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
30  */
31 
42 abstract class CommonObject
43 {
47  public $db;
48 
52  public $id;
53 
58  public $error;
59 
63  public $errors=array();
64 
68  public $element;
69 
73  public $table_element;
74 
78  public $table_element_line='';
79 
83  public $import_key;
84 
88  public $array_options=array();
89 
93  public $linkedObjectsIds;
94 
98  public $linkedObjects;
99 
103  public $oldcopy;
104 
108  protected $table_ref_field = '';
109 
110 
111 
112  // Following vars are used by some objects only. We keep this property here in CommonObject to be able to provide common method using them.
113 
117  public $context=array();
118 
122  public $canvas;
123 
128  public $project;
129 
134  public $fk_project;
135 
140  public $projet;
141 
146  public $contact;
147 
152  public $contact_id;
153 
158  public $thirdparty;
159 
164  public $user;
165 
170  public $origin;
171 
176  public $origin_id;
177 
181  public $ref;
182 
186  public $ref_previous;
187 
191  public $ref_next;
192 
196  public $ref_ext;
197 
202  public $statut;
203 
208  public $country;
209 
214  public $country_id;
215 
220  public $country_code;
221 
226  public $state;
227 
232  public $state_id;
233 
238  public $state_code;
239 
244  public $region;
245 
250  public $region_code;
251 
256  public $barcode_type;
257 
262  public $barcode_type_code;
263 
268  public $barcode_type_label;
269 
274  public $barcode_type_coder;
275 
280  public $mode_reglement_id;
281 
286  public $cond_reglement_id;
287 
293  public $cond_reglement;
294 
300  public $fk_delivery_address;
301 
306  public $shipping_method_id;
307 
312  public $modelpdf;
313 
318  public $last_main_doc;
319 
324  public $fk_account;
325 
330  public $note_public;
331 
336  public $note_private;
337 
342  public $note;
343 
348  public $total_ht;
349 
354  public $total_tva;
355 
360  public $total_localtax1;
361 
366  public $total_localtax2;
367 
372  public $total_ttc;
373 
377  public $lines;
378 
383  public $comments=array();
384 
389  public $fk_incoterms;
390 
395  public $libelle_incoterms;
396 
401  public $location_incoterms;
402 
403  public $name;
404  public $lastname;
405  public $firstname;
406  public $civility_id;
407 
408  // Dates
409  public $date_creation; // Date creation
410  public $date_validation; // Date validation
411  public $date_modification; // Date last change (tms field)
412 
413 
414 
415  // No constructor as it is an abstract class
416 
427  static function isExistingObject($element, $id, $ref='', $ref_ext='')
428  {
429  global $db,$conf;
430 
431  $sql = "SELECT rowid, ref, ref_ext";
432  $sql.= " FROM ".MAIN_DB_PREFIX.$element;
433  $sql.= " WHERE entity IN (".getEntity($element).")" ;
434 
435  if ($id > 0) $sql.= " AND rowid = ".$db->escape($id);
436  else if ($ref) $sql.= " AND ref = '".$db->escape($ref)."'";
437  else if ($ref_ext) $sql.= " AND ref_ext = '".$db->escape($ref_ext)."'";
438  else {
439  $error='ErrorWrongParameters';
440  dol_print_error(get_class()."::isExistingObject ".$error, LOG_ERR);
441  return -1;
442  }
443  if ($ref || $ref_ext) $sql.= " AND entity = ".$conf->entity;
444 
445  dol_syslog(get_class()."::isExistingObject", LOG_DEBUG);
446  $resql = $db->query($sql);
447  if ($resql)
448  {
449  $num=$db->num_rows($resql);
450  if ($num > 0) return 1;
451  else return 0;
452  }
453  return -1;
454  }
455 
461  function errorsToString()
462  {
463  return $this->error.(is_array($this->errors)?(($this->error!=''?', ':'').join(', ',$this->errors)):'');
464  }
465 
475  function getFullName($langs,$option=0,$nameorder=-1,$maxlen=0)
476  {
477  //print "lastname=".$this->lastname." name=".$this->name." nom=".$this->nom."<br>\n";
478  $lastname=$this->lastname;
479  $firstname=$this->firstname;
480  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:'')))));
481 
482  $ret='';
483  if ($option && $this->civility_id)
484  {
485  if ($langs->transnoentitiesnoconv("Civility".$this->civility_id)!="Civility".$this->civility_id) $ret.=$langs->transnoentitiesnoconv("Civility".$this->civility_id).' ';
486  else $ret.=$this->civility_id.' ';
487  }
488 
489  $ret.=dolGetFirstLastname($firstname, $lastname, $nameorder);
490 
491  return dol_trunc($ret,$maxlen);
492  }
493 
502  function getFullAddress($withcountry=0, $sep="\n", $withregion=0)
503  {
504  if ($withcountry && $this->country_id && (empty($this->country_code) || empty($this->country)))
505  {
506  require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php';
507  $tmparray=getCountry($this->country_id,'all');
508  $this->country_code=$tmparray['code'];
509  $this->country =$tmparray['label'];
510  }
511 
512  if ($withregion && $this->state_id && (empty($this->state_code) || empty($this->state) || empty($this->region) || empty($this->region_cpde)))
513  {
514  require_once DOL_DOCUMENT_ROOT .'/core/lib/company.lib.php';
515  $tmparray=getState($this->state_id,'all',0,1);
516  $this->state_code =$tmparray['code'];
517  $this->state =$tmparray['label'];
518  $this->region_code =$tmparray['region_code'];
519  $this->region =$tmparray['region'];
520  }
521 
522  return dol_format_address($this, $withcountry, $sep);
523  }
524 
525 
533  function getBannerAddress($htmlkey, $object)
534  {
535  global $conf, $langs;
536 
537  $countriesusingstate=array('AU','US','IN','GB','ES','UK','TR'); // See also option MAIN_FORCE_STATE_INTO_ADDRESS
538 
539  $contactid=0;
540  $thirdpartyid=0;
541  if ($this->element == 'societe')
542  {
543  $thirdpartyid=$this->id;
544  }
545  if ($this->element == 'contact')
546  {
547  $contactid=$this->id;
548  $thirdpartyid=$object->fk_soc;
549  }
550  if ($this->element == 'user')
551  {
552  $contactid=$this->contact_id;
553  $thirdpartyid=$object->fk_soc;
554  }
555 
556  $out='<!-- BEGIN part to show address block -->';
557 
558  $outdone=0;
559  $coords = $this->getFullAddress(1,', ',$conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT);
560  if ($coords)
561  {
562  if (! empty($conf->use_javascript_ajax))
563  {
564  $namecoords = $this->getFullName($langs,1).'<br>'.$coords;
565  // hideonsmatphone because copyToClipboard call jquery dialog that does not work with jmobile
566  $out.='<a href="#" class="hideonsmartphone" onclick="return copyToClipboard(\''.dol_escape_js($namecoords).'\',\''.dol_escape_js($langs->trans("HelpCopyToClipboard")).'\');">';
567  $out.=img_picto($langs->trans("Address"), 'object_address.png');
568  $out.='</a> ';
569  }
570  $out.=dol_print_address($coords, 'address_'.$htmlkey.'_'.$this->id, $this->element, $this->id, 1, ', '); $outdone++;
571  $outdone++;
572  }
573 
574  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
575  && empty($conf->global->SOCIETE_DISABLE_STATE) && $this->state)
576  {
577  if (!empty($conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT) && $conf->global->MAIN_SHOW_REGION_IN_STATE_SELECT == 1 && $this->region) {
578  $out.=($outdone?' - ':'').$this->region.' - '.$this->state;
579  }
580  else {
581  $out.=($outdone?' - ':'').$this->state;
582  }
583  $outdone++;
584  }
585 
586  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>':'');
587  if (! empty($this->phone) && empty($this->phone_pro)) { // For objects that store pro phone into ->phone
588  $out.=dol_print_phone($this->phone,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
589  }
590  if (! empty($this->phone_pro)) {
591  $out.=dol_print_phone($this->phone_pro,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
592  }
593  if (! empty($this->phone_mobile)) {
594  $out.=dol_print_phone($this->phone_mobile,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','mobile',$langs->trans("PhoneMobile")); $outdone++;
595  }
596  if (! empty($this->phone_perso)) {
597  $out.=dol_print_phone($this->phone_perso,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePerso")); $outdone++;
598  }
599  if (! empty($this->office_phone)) {
600  $out.=dol_print_phone($this->office_phone,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','phone',$langs->trans("PhonePro")); $outdone++;
601  }
602  if (! empty($this->user_mobile)) {
603  $out.=dol_print_phone($this->user_mobile,$this->country_code,$contactid,$thirdpartyid,'AC_TEL','&nbsp;','mobile',$langs->trans("PhoneMobile")); $outdone++;
604  }
605  if (! empty($this->fax)) {
606  $out.=dol_print_phone($this->fax,$this->country_code,$contactid,$thirdpartyid,'AC_FAX','&nbsp;','fax',$langs->trans("Fax")); $outdone++;
607  }
608  if (! empty($this->office_fax)) {
609  $out.=dol_print_phone($this->office_fax,$this->country_code,$contactid,$thirdpartyid,'AC_FAX','&nbsp;','fax',$langs->trans("Fax")); $outdone++;
610  }
611 
612  $out.='<div style="clear: both;"></div>';
613  $outdone=0;
614  if (! empty($this->email))
615  {
616  $out.=dol_print_email($this->email,$this->id,$object->id,'AC_EMAIL',0,0,1);
617  $outdone++;
618  }
619  if (! empty($this->url))
620  {
621  $out.=dol_print_url($this->url,'_goout',0,1);
622  $outdone++;
623  }
624  $out.='<div style="clear: both;">';
625  if (! empty($conf->socialnetworks->enabled))
626  {
627  if ($this->skype) $out.=dol_print_socialnetworks($this->skype,$this->id,$object->id,'skype');
628  $outdone++;
629  }
630  if (! empty($conf->socialnetworks->enabled))
631  {
632  if ($this->twitter) $out.=dol_print_socialnetworks($this->twitter,$this->id,$object->id,'twitter');
633  $outdone++;
634  }
635  if (! empty($conf->socialnetworks->enabled))
636  {
637  if ($this->facebook) $out.=dol_print_socialnetworks($this->facebook,$this->id,$object->id,'facebook');
638  $outdone++;
639  }
640  $out.='</div>';
641 
642  $out.='<!-- END Part to show address block -->';
643 
644  return $out;
645  }
646 
655  function getLastMainDocLink($modulepart, $initsharekey=0, $relativelink=0)
656  {
657  global $user, $dolibarr_main_url_root;
658 
659  if (empty($this->last_main_doc))
660  {
661  return ''; // No way to known which document name to use
662  }
663 
664  include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
665  $ecmfile=new EcmFiles($this->db);
666  $result = $ecmfile->fetch(0, '', $this->last_main_doc);
667  if ($result < 0)
668  {
669  $this->error = $ecmfile->error;
670  $this->errors = $ecmfile->errors;
671  return -1;
672  }
673 
674  if (empty($ecmfile->id))
675  {
676  // Add entry into index
677  if ($initsharekey)
678  {
679  require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
680  // TODO We can't, we dont' have full path of file, only last_main_doc adn ->element, so we must rebuild full path first
681  /*
682  $ecmfile->filepath = $rel_dir;
683  $ecmfile->filename = $filename;
684  $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
685  $ecmfile->fullpath_orig = '';
686  $ecmfile->gen_or_uploaded = 'generated';
687  $ecmfile->description = ''; // indexed content
688  $ecmfile->keyword = ''; // keyword content
689  $ecmfile->share = getRandomPassword(true);
690  $result = $ecmfile->create($user);
691  if ($result < 0)
692  {
693  $this->error = $ecmfile->error;
694  $this->errors = $ecmfile->errors;
695  }
696  */
697  }
698  else return '';
699  }
700  elseif (empty($ecmfile->share))
701  {
702  // Add entry into index
703  if ($initsharekey)
704  {
705  require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
706  $ecmfile->share = getRandomPassword(true);
707  $ecmfile->update($user);
708  }
709  else return '';
710  }
711 
712  // Define $urlwithroot
713  $urlwithouturlroot=preg_replace('/'.preg_quote(DOL_URL_ROOT,'/').'$/i','',trim($dolibarr_main_url_root));
714  $urlwithroot=$urlwithouturlroot.DOL_URL_ROOT; // This is to use external domain name found into config file
715  //$urlwithroot=DOL_MAIN_URL_ROOT; // This is to use same domain name than current
716 
717  $forcedownload=0;
718 
719  $paramlink='';
720  //if (! empty($modulepart)) $paramlink.=($paramlink?'&':'').'modulepart='.$modulepart; // For sharing with hash (so public files), modulepart is not required.
721  //if (! empty($ecmfile->entity)) $paramlink.='&entity='.$ecmfile->entity; // For sharing with hash (so public files), entity is not required.
722  //$paramlink.=($paramlink?'&':'').'file='.urlencode($filepath); // No need of name of file for public link, we will use the hash
723  if (! empty($ecmfile->share)) $paramlink.=($paramlink?'&':'').'hashp='.$ecmfile->share; // Hash for public share
724  if ($forcedownload) $paramlink.=($paramlink?'&':'').'attachment=1';
725 
726  if ($relativelink)
727  {
728  $linktoreturn='document.php'.($paramlink?'?'.$paramlink:'');
729  }
730  else
731  {
732  $linktoreturn=$urlwithroot.'/document.php'.($paramlink?'?'.$paramlink:'');
733  }
734 
735  // Here $ecmfile->share is defined
736  return $linktoreturn;
737  }
738 
739 
740  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
750  function add_contact($fk_socpeople, $type_contact, $source='external',$notrigger=0)
751  {
752  // phpcs:enable
753  global $user,$langs;
754 
755 
756  dol_syslog(get_class($this)."::add_contact $fk_socpeople, $type_contact, $source, $notrigger");
757 
758  // Check parameters
759  if ($fk_socpeople <= 0)
760  {
761  $langs->load("errors");
762  $this->error=$langs->trans("ErrorWrongValueForParameterX","1");
763  dol_syslog(get_class($this)."::add_contact ".$this->error,LOG_ERR);
764  return -1;
765  }
766  if (! $type_contact)
767  {
768  $langs->load("errors");
769  $this->error=$langs->trans("ErrorWrongValueForParameterX","2");
770  dol_syslog(get_class($this)."::add_contact ".$this->error,LOG_ERR);
771  return -2;
772  }
773 
774  $id_type_contact=0;
775  if (is_numeric($type_contact))
776  {
777  $id_type_contact=$type_contact;
778  }
779  else
780  {
781  // We look for id type_contact
782  $sql = "SELECT tc.rowid";
783  $sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
784  $sql.= " WHERE tc.element='".$this->db->escape($this->element)."'";
785  $sql.= " AND tc.source='".$this->db->escape($source)."'";
786  $sql.= " AND tc.code='".$this->db->escape($type_contact)."' AND tc.active=1";
787  //print $sql;
788  $resql=$this->db->query($sql);
789  if ($resql)
790  {
791  $obj = $this->db->fetch_object($resql);
792  if ($obj) $id_type_contact=$obj->rowid;
793  }
794  }
795 
796  if ($id_type_contact == 0)
797  {
798  $this->error='CODE_NOT_VALID_FOR_THIS_ELEMENT';
799  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");
800  return -3;
801  }
802 
803  $datecreate = dol_now();
804 
805  // Socpeople must have already been added by some trigger, then we have to check it to avoid DB_ERROR_RECORD_ALREADY_EXISTS error
806  $TListeContacts=$this->liste_contact(-1, $source);
807  $already_added=false;
808  if(!empty($TListeContacts)) {
809  foreach($TListeContacts as $array_contact) {
810  if($array_contact['status'] == 4 && $array_contact['id'] == $fk_socpeople && $array_contact['fk_c_type_contact'] == $id_type_contact) {
811  $already_added=true;
812  break;
813  }
814  }
815  }
816 
817  if(!$already_added) {
818 
819  $this->db->begin();
820 
821  // Insert into database
822  $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_contact";
823  $sql.= " (element_id, fk_socpeople, datecreate, statut, fk_c_type_contact) ";
824  $sql.= " VALUES (".$this->id.", ".$fk_socpeople." , " ;
825  $sql.= "'".$this->db->idate($datecreate)."'";
826  $sql.= ", 4, ". $id_type_contact;
827  $sql.= ")";
828 
829  $resql=$this->db->query($sql);
830  if ($resql)
831  {
832  if (! $notrigger)
833  {
834  $result=$this->call_trigger(strtoupper($this->element).'_ADD_CONTACT', $user);
835  if ($result < 0)
836  {
837  $this->db->rollback();
838  return -1;
839  }
840  }
841 
842  $this->db->commit();
843  return 1;
844  }
845  else
846  {
847  if ($this->db->errno() == 'DB_ERROR_RECORD_ALREADY_EXISTS')
848  {
849  $this->error=$this->db->errno();
850  $this->db->rollback();
851  echo 'err rollback';
852  return -2;
853  }
854  else
855  {
856  $this->error=$this->db->error();
857  $this->db->rollback();
858  return -1;
859  }
860  }
861  } else return 0;
862  }
863 
864  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
872  function copy_linked_contact($objFrom, $source='internal')
873  {
874  // phpcs:enable
875  $contacts = $objFrom->liste_contact(-1, $source);
876  foreach($contacts as $contact)
877  {
878  if ($this->add_contact($contact['id'], $contact['fk_c_type_contact'], $contact['source']) < 0)
879  {
880  $this->error=$this->db->lasterror();
881  return -1;
882  }
883  }
884  return 1;
885  }
886 
887  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
897  function update_contact($rowid, $statut, $type_contact_id=0, $fk_socpeople=0)
898  {
899  // phpcs:enable
900  // Insert into database
901  $sql = "UPDATE ".MAIN_DB_PREFIX."element_contact set";
902  $sql.= " statut = ".$statut;
903  if ($type_contact_id) $sql.= ", fk_c_type_contact = '".$type_contact_id ."'";
904  if ($fk_socpeople) $sql.= ", fk_socpeople = '".$fk_socpeople ."'";
905  $sql.= " where rowid = ".$rowid;
906  $resql=$this->db->query($sql);
907  if ($resql)
908  {
909  return 0;
910  }
911  else
912  {
913  $this->error=$this->db->lasterror();
914  return -1;
915  }
916  }
917 
918  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
926  function delete_contact($rowid, $notrigger=0)
927  {
928  // phpcs:enable
929  global $user;
930 
931 
932  $this->db->begin();
933 
934  $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
935  $sql.= " WHERE rowid =".$rowid;
936 
937  dol_syslog(get_class($this)."::delete_contact", LOG_DEBUG);
938  if ($this->db->query($sql))
939  {
940  if (! $notrigger)
941  {
942  $result=$this->call_trigger(strtoupper($this->element).'_DELETE_CONTACT', $user);
943  if ($result < 0) { $this->db->rollback(); return -1; }
944  }
945 
946  $this->db->commit();
947  return 1;
948  }
949  else
950  {
951  $this->error=$this->db->lasterror();
952  $this->db->rollback();
953  return -1;
954  }
955  }
956 
957  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
965  function delete_linked_contact($source='',$code='')
966  {
967  // phpcs:enable
968  $temp = array();
969  $typeContact = $this->liste_type_contact($source,'',0,0,$code);
970 
971  foreach($typeContact as $key => $value)
972  {
973  array_push($temp,$key);
974  }
975  $listId = implode(",", $temp);
976 
977  $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_contact";
978  $sql.= " WHERE element_id = ".$this->id;
979  if ($listId)
980  $sql.= " AND fk_c_type_contact IN (".$listId.")";
981 
982  dol_syslog(get_class($this)."::delete_linked_contact", LOG_DEBUG);
983  if ($this->db->query($sql))
984  {
985  return 1;
986  }
987  else
988  {
989  $this->error=$this->db->lasterror();
990  return -1;
991  }
992  }
993 
994  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1004  function liste_contact($statut=-1,$source='external',$list=0,$code='')
1005  {
1006  // phpcs:enable
1007  global $langs;
1008 
1009  $tab=array();
1010 
1011  $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
1012  if ($source == 'internal') $sql.=", '-1' as socid, t.statut as statuscontact, t.login, t.photo";
1013  if ($source == 'external' || $source == 'thirdparty') $sql.=", t.fk_soc as socid, t.statut as statuscontact";
1014  $sql.= ", t.civility as civility, t.lastname as lastname, t.firstname, t.email";
1015  $sql.= ", tc.source, tc.element, tc.code, tc.libelle";
1016  $sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact tc";
1017  $sql.= ", ".MAIN_DB_PREFIX."element_contact ec";
1018  if ($source == 'internal') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."user t on ec.fk_socpeople = t.rowid";
1019  if ($source == 'external'|| $source == 'thirdparty') $sql.=" LEFT JOIN ".MAIN_DB_PREFIX."socpeople t on ec.fk_socpeople = t.rowid";
1020  $sql.= " WHERE ec.element_id =".$this->id;
1021  $sql.= " AND ec.fk_c_type_contact=tc.rowid";
1022  $sql.= " AND tc.element='".$this->db->escape($this->element)."'";
1023  if ($code) $sql.= " AND tc.code = '".$this->db->escape($code)."'";
1024  if ($source == 'internal') $sql.= " AND tc.source = 'internal'";
1025  if ($source == 'external' || $source == 'thirdparty') $sql.= " AND tc.source = 'external'";
1026  $sql.= " AND tc.active=1";
1027  if ($statut >= 0) $sql.= " AND ec.statut = '".$statut."'";
1028  $sql.=" ORDER BY t.lastname ASC";
1029 
1030  dol_syslog(get_class($this)."::liste_contact", LOG_DEBUG);
1031  $resql=$this->db->query($sql);
1032  if ($resql)
1033  {
1034  $num=$this->db->num_rows($resql);
1035  $i=0;
1036  while ($i < $num)
1037  {
1038  $obj = $this->db->fetch_object($resql);
1039 
1040  if (! $list)
1041  {
1042  $transkey="TypeContact_".$obj->element."_".$obj->source."_".$obj->code;
1043  $libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
1044  $tab[$i]=array('source'=>$obj->source,'socid'=>$obj->socid,'id'=>$obj->id,
1045  'nom'=>$obj->lastname, // For backward compatibility
1046  'civility'=>$obj->civility, 'lastname'=>$obj->lastname, 'firstname'=>$obj->firstname, 'email'=>$obj->email, 'login'=>$obj->login, 'photo'=>$obj->photo, 'statuscontact'=>$obj->statuscontact,
1047  'rowid'=>$obj->rowid, 'code'=>$obj->code, 'libelle'=>$libelle_type, 'status'=>$obj->statuslink, 'fk_c_type_contact'=>$obj->fk_c_type_contact);
1048  }
1049  else
1050  {
1051  $tab[$i]=$obj->id;
1052  }
1053 
1054  $i++;
1055  }
1056 
1057  return $tab;
1058  }
1059  else
1060  {
1061  $this->error=$this->db->lasterror();
1062  dol_print_error($this->db);
1063  return -1;
1064  }
1065  }
1066 
1067 
1074  function swapContactStatus($rowid)
1075  {
1076  $sql = "SELECT ec.datecreate, ec.statut, ec.fk_socpeople, ec.fk_c_type_contact,";
1077  $sql.= " tc.code, tc.libelle";
1078  //$sql.= ", s.fk_soc";
1079  $sql.= " FROM (".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as tc)";
1080  //$sql.= " LEFT JOIN ".MAIN_DB_PREFIX."socpeople as s ON ec.fk_socpeople=s.rowid"; // Si contact de type external, alors il est lie a une societe
1081  $sql.= " WHERE ec.rowid =".$rowid;
1082  $sql.= " AND ec.fk_c_type_contact=tc.rowid";
1083  $sql.= " AND tc.element = '".$this->db->escape($this->element)."'";
1084 
1085  dol_syslog(get_class($this)."::swapContactStatus", LOG_DEBUG);
1086  $resql=$this->db->query($sql);
1087  if ($resql)
1088  {
1089  $obj = $this->db->fetch_object($resql);
1090  $newstatut = ($obj->statut == 4) ? 5 : 4;
1091  $result = $this->update_contact($rowid, $newstatut);
1092  $this->db->free($resql);
1093  return $result;
1094  }
1095  else
1096  {
1097  $this->error=$this->db->error();
1098  dol_print_error($this->db);
1099  return -1;
1100  }
1101  }
1102 
1103  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1114  function liste_type_contact($source='internal', $order='position', $option=0, $activeonly=0, $code='')
1115  {
1116  // phpcs:enable
1117  global $langs;
1118 
1119  if (empty($order)) $order='position';
1120  if ($order == 'position') $order.=',code';
1121 
1122  $tab = array();
1123  $sql = "SELECT DISTINCT tc.rowid, tc.code, tc.libelle, tc.position";
1124  $sql.= " FROM ".MAIN_DB_PREFIX."c_type_contact as tc";
1125  $sql.= " WHERE tc.element='".$this->db->escape($this->element)."'";
1126  if ($activeonly == 1) $sql.= " AND tc.active=1"; // only the active types
1127  if (! empty($source) && $source != 'all') $sql.= " AND tc.source='".$this->db->escape($source)."'";
1128  if (! empty($code)) $sql.= " AND tc.code='".$this->db->escape($code)."'";
1129  $sql.= $this->db->order($order,'ASC');
1130 
1131  //print "sql=".$sql;
1132  $resql=$this->db->query($sql);
1133  if ($resql)
1134  {
1135  $num=$this->db->num_rows($resql);
1136  $i=0;
1137  while ($i < $num)
1138  {
1139  $obj = $this->db->fetch_object($resql);
1140 
1141  $transkey="TypeContact_".$this->element."_".$source."_".$obj->code;
1142  $libelle_type=($langs->trans($transkey)!=$transkey ? $langs->trans($transkey) : $obj->libelle);
1143  if (empty($option)) $tab[$obj->rowid]=$libelle_type;
1144  else $tab[$obj->code]=$libelle_type;
1145  $i++;
1146  }
1147  return $tab;
1148  }
1149  else
1150  {
1151  $this->error=$this->db->lasterror();
1152  //dol_print_error($this->db);
1153  return null;
1154  }
1155  }
1156 
1168  function getIdContact($source,$code,$status=0)
1169  {
1170  global $conf;
1171 
1172  $result=array();
1173  $i=0;
1174  //cas particulier pour les expeditions
1175  if($this->element=='shipping' && $this->origin_id != 0) {
1176  $id=$this->origin_id;
1177  $element='commande';
1178  } else {
1179  $id=$this->id;
1180  $element=$this->element;
1181  }
1182 
1183  $sql = "SELECT ec.fk_socpeople";
1184  $sql.= " FROM ".MAIN_DB_PREFIX."element_contact as ec,";
1185  if ($source == 'internal') $sql.= " ".MAIN_DB_PREFIX."user as c,";
1186  if ($source == 'external') $sql.= " ".MAIN_DB_PREFIX."socpeople as c,";
1187  $sql.= " ".MAIN_DB_PREFIX."c_type_contact as tc";
1188  $sql.= " WHERE ec.element_id = ".$id;
1189  $sql.= " AND ec.fk_socpeople = c.rowid";
1190  if ($source == 'internal') $sql.= " AND c.entity IN (".getEntity('user').")";
1191  if ($source == 'external') $sql.= " AND c.entity IN (".getEntity('societe').")";
1192  $sql.= " AND ec.fk_c_type_contact = tc.rowid";
1193  $sql.= " AND tc.element = '".$element."'";
1194  $sql.= " AND tc.source = '".$source."'";
1195  $sql.= " AND tc.code = '".$code."'";
1196  $sql.= " AND tc.active = 1";
1197  if ($status) $sql.= " AND ec.statut = ".$status;
1198 
1199  dol_syslog(get_class($this)."::getIdContact", LOG_DEBUG);
1200  $resql=$this->db->query($sql);
1201  if ($resql)
1202  {
1203  while ($obj = $this->db->fetch_object($resql))
1204  {
1205  $result[$i]=$obj->fk_socpeople;
1206  $i++;
1207  }
1208  }
1209  else
1210  {
1211  $this->error=$this->db->error();
1212  return null;
1213  }
1214 
1215  return $result;
1216  }
1217 
1218  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1225  function fetch_contact($contactid=null)
1226  {
1227  // phpcs:enable
1228  if (empty($contactid)) $contactid=$this->contactid;
1229 
1230  if (empty($contactid)) return 0;
1231 
1232  require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
1233  $contact = new Contact($this->db);
1234  $result=$contact->fetch($contactid);
1235  $this->contact = $contact;
1236  return $result;
1237  }
1238 
1239  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1246  function fetch_thirdparty($force_thirdparty_id=0)
1247  {
1248  // phpcs:enable
1249  global $conf;
1250 
1251  if (empty($this->socid) && empty($this->fk_soc) && empty($this->fk_thirdparty) && empty($force_thirdparty_id))
1252  return 0;
1253 
1254  require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php';
1255 
1256  $idtofetch = isset($this->socid) ? $this->socid : (isset($this->fk_soc) ? $this->fk_soc : $this->fk_thirdparty);
1257  if ($force_thirdparty_id)
1258  $idtofetch = $force_thirdparty_id;
1259 
1260  if ($idtofetch) {
1261  $thirdparty = new Societe($this->db);
1262  $result = $thirdparty->fetch($idtofetch);
1263  $this->thirdparty = $thirdparty;
1264 
1265  // Use first price level if level not defined for third party
1266  if (!empty($conf->global->PRODUIT_MULTIPRICES) && empty($this->thirdparty->price_level)) {
1267  $this->thirdparty->price_level = 1;
1268  }
1269 
1270  return $result;
1271  } else
1272  return -1;
1273  }
1274 
1275 
1283  public function fetchOneLike($ref)
1284  {
1285  if (!$this->table_ref_field) {
1286  return 0;
1287  }
1288 
1289  $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element.' WHERE '.$this->table_ref_field.' LIKE "'.$this->db->escape($ref).'" LIMIT 1';
1290 
1291  $query = $this->db->query($sql);
1292 
1293  if (!$this->db->num_rows($query)) {
1294  return 0;
1295  }
1296 
1297  $result = $this->db->fetch_object($query);
1298 
1299  return $this->fetch($result->rowid);
1300  }
1301 
1302  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1310  function fetch_barcode()
1311  {
1312  // phpcs:enable
1313  global $conf;
1314 
1315  dol_syslog(get_class($this).'::fetch_barcode this->element='.$this->element.' this->barcode_type='.$this->barcode_type);
1316 
1317  $idtype=$this->barcode_type;
1318  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
1319  {
1320  if ($this->element == 'product') $idtype = $conf->global->PRODUIT_DEFAULT_BARCODE_TYPE;
1321  else if ($this->element == 'societe') $idtype = $conf->global->GENBARCODE_BARCODETYPE_THIRDPARTY;
1322  else dol_syslog('Call fetch_barcode with barcode_type not defined and cant be guessed', LOG_WARNING);
1323  }
1324 
1325  if ($idtype > 0)
1326  {
1327  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
1328  {
1329  $sql = "SELECT rowid, code, libelle as label, coder";
1330  $sql.= " FROM ".MAIN_DB_PREFIX."c_barcode_type";
1331  $sql.= " WHERE rowid = ".$idtype;
1332  dol_syslog(get_class($this).'::fetch_barcode', LOG_DEBUG);
1333  $resql = $this->db->query($sql);
1334  if ($resql)
1335  {
1336  $obj = $this->db->fetch_object($resql);
1337  $this->barcode_type = $obj->rowid;
1338  $this->barcode_type_code = $obj->code;
1339  $this->barcode_type_label = $obj->label;
1340  $this->barcode_type_coder = $obj->coder;
1341  return 1;
1342  }
1343  else
1344  {
1345  dol_print_error($this->db);
1346  return -1;
1347  }
1348  }
1349  }
1350  return 0;
1351  }
1352 
1353  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1359  function fetch_projet()
1360  {
1361  // phpcs:enable
1362  include_once DOL_DOCUMENT_ROOT.'/projet/class/project.class.php';
1363 
1364  if (empty($this->fk_project) && ! empty($this->fk_projet)) $this->fk_project = $this->fk_projet; // For backward compatibility
1365  if (empty($this->fk_project)) return 0;
1366 
1367  $project = new Project($this->db);
1368  $result = $project->fetch($this->fk_project);
1369 
1370  $this->projet = $project; // deprecated
1371  $this->project = $project;
1372  return $result;
1373  }
1374 
1375  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1381  function fetch_product()
1382  {
1383  // phpcs:enable
1384  include_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
1385 
1386  if (empty($this->fk_product)) return 0;
1387 
1388  $product = new Product($this->db);
1389  $result = $product->fetch($this->fk_product);
1390 
1391  $this->product = $product;
1392  return $result;
1393  }
1394 
1395  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1402  function fetch_user($userid)
1403  {
1404  // phpcs:enable
1405  $user = new User($this->db);
1406  $result=$user->fetch($userid);
1407  $this->user = $user;
1408  return $result;
1409  }
1410 
1411  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1417  function fetch_origin()
1418  {
1419  // phpcs:enable
1420  if ($this->origin == 'shipping') $this->origin = 'expedition';
1421  if ($this->origin == 'delivery') $this->origin = 'livraison';
1422 
1423  $origin = $this->origin;
1424 
1425  $classname = ucfirst($origin);
1426  $this->$origin = new $classname($this->db);
1427  $this->$origin->fetch($this->origin_id);
1428  }
1429 
1439  function fetchObjectFrom($table, $field, $key, $element = null)
1440  {
1441  global $conf;
1442 
1443  $result=false;
1444 
1445  $sql = "SELECT rowid FROM ".MAIN_DB_PREFIX.$table;
1446  $sql.= " WHERE ".$field." = '".$key."'";
1447  if (! empty($element)) {
1448  $sql.= " AND entity IN (".getEntity($element).")";
1449  } else {
1450  $sql.= " AND entity = ".$conf->entity;
1451  }
1452 
1453  dol_syslog(get_class($this).'::fetchObjectFrom', LOG_DEBUG);
1454  $resql = $this->db->query($sql);
1455  if ($resql)
1456  {
1457  $row = $this->db->fetch_row($resql);
1458  // Test for avoid error -1
1459  if ($row[0] > 0) {
1460  $result = $this->fetch($row[0]);
1461  }
1462  }
1463 
1464  return $result;
1465  }
1466 
1475  function getValueFrom($table, $id, $field)
1476  {
1477  $result=false;
1478  if (!empty($id) && !empty($field) && !empty($table)) {
1479  $sql = "SELECT ".$field." FROM ".MAIN_DB_PREFIX.$table;
1480  $sql.= " WHERE rowid = ".$id;
1481 
1482  dol_syslog(get_class($this).'::getValueFrom', LOG_DEBUG);
1483  $resql = $this->db->query($sql);
1484  if ($resql)
1485  {
1486  $row = $this->db->fetch_row($resql);
1487  $result = $row[0];
1488  }
1489  }
1490  return $result;
1491  }
1492 
1509  function setValueFrom($field, $value, $table='', $id=null, $format='', $id_field='', $fuser=null, $trigkey='', $fk_user_field='fk_user_modif')
1510  {
1511  global $user,$langs,$conf;
1512 
1513  if (empty($table)) $table=$this->table_element;
1514  if (empty($id)) $id=$this->id;
1515  if (empty($format)) $format='text';
1516  if (empty($id_field)) $id_field='rowid';
1517 
1518  $error=0;
1519 
1520  $this->db->begin();
1521 
1522  // Special case
1523  if ($table == 'product' && $field == 'note_private') $field='note';
1524  if (in_array($table, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))) $fk_user_field = 'fk_user_mod';
1525 
1526  $sql = "UPDATE ".MAIN_DB_PREFIX.$table." SET ";
1527 
1528  if ($format == 'text') $sql.= $field." = '".$this->db->escape($value)."'";
1529  else if ($format == 'int') $sql.= $field." = ".$this->db->escape($value);
1530  else if ($format == 'date') $sql.= $field." = ".($value ? "'".$this->db->idate($value)."'" : "null");
1531 
1532  if ($fk_user_field)
1533  {
1534  if (! empty($fuser) && is_object($fuser)) $sql.=", ".$fk_user_field." = ".$fuser->id;
1535  elseif (empty($fuser) || $fuser != 'none') $sql.=", ".$fk_user_field." = ".$user->id;
1536  }
1537 
1538  $sql.= " WHERE ".$id_field." = ".$id;
1539 
1540  dol_syslog(get_class($this)."::".__FUNCTION__."", LOG_DEBUG);
1541  $resql = $this->db->query($sql);
1542  if ($resql)
1543  {
1544  if ($trigkey)
1545  {
1546  // call trigger with updated object values
1547  if (empty($this->fields) && method_exists($this, 'fetch'))
1548  {
1549  $result = $this->fetch($id);
1550  }
1551  else
1552  {
1553  $result = $this->fetchCommon($id);
1554  }
1555  if ($result >= 0) $result=$this->call_trigger($trigkey, (! empty($fuser) && is_object($fuser)) ? $fuser : $user); // This may set this->errors
1556  if ($result < 0) $error++;
1557  }
1558 
1559  if (! $error)
1560  {
1561  if (property_exists($this, $field)) $this->$field = $value;
1562  $this->db->commit();
1563  return 1;
1564  }
1565  else
1566  {
1567  $this->db->rollback();
1568  return -2;
1569  }
1570  }
1571  else
1572  {
1573  $this->error=$this->db->lasterror();
1574  $this->db->rollback();
1575  return -1;
1576  }
1577  }
1578 
1579  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
1588  function load_previous_next_ref($filter, $fieldid, $nodbprefix=0)
1589  {
1590  // phpcs:enable
1591  global $conf, $user;
1592 
1593  if (! $this->table_element)
1594  {
1595  dol_print_error('',get_class($this)."::load_previous_next_ref was called on objet with property table_element not defined");
1596  return -1;
1597  }
1598  if ($fieldid == 'none') return 1;
1599 
1600  // Security on socid
1601  $socid = 0;
1602  if ($user->societe_id > 0) $socid = $user->societe_id;
1603 
1604  // this->ismultientitymanaged contains
1605  // 0=No test on entity, 1=Test with field entity, 2=Test with link by societe
1606  $alias = 's';
1607  if ($this->element == 'societe') $alias = 'te';
1608 
1609  $sql = "SELECT MAX(te.".$fieldid.")";
1610  $sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
1611  if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1612  $sql.= ",".MAIN_DB_PREFIX."usergroup_user as ug";
1613  }
1614  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ", ".MAIN_DB_PREFIX."societe as s"; // If we need to link to societe to limit select to entity
1615  else if ($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
1616  else if ($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
1617  if ($this->restrictiononfksoc && !$user->rights->societe->client->voir && !$socid) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$alias.".rowid = sc.fk_soc";
1618  $sql.= " WHERE te.".$fieldid." < '".$this->db->escape($this->ref)."'"; // ->ref must always be defined (set to id if field does not exists)
1619  if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND sc.fk_user = " .$user->id;
1620  if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND (sc.fk_user = " .$user->id.' OR te.fk_soc IS NULL)';
1621  if (! empty($filter))
1622  {
1623  if (! preg_match('/^\s*AND/i', $filter)) $sql.=" AND "; // For backward compatibility
1624  $sql.=$filter;
1625  }
1626  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to entity
1627  else if ($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
1628  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
1629  if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1630  if (! empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1631  $sql.= " AND te.entity IS NOT NULL"; // Show all users
1632  } else {
1633  $sql.= " AND ug.fk_user = te.rowid";
1634  $sql.= " AND ug.entity IN (".getEntity($this->element).")";
1635  }
1636  } else {
1637  $sql.= ' AND te.entity IN ('.getEntity($this->element).')';
1638  }
1639  }
1640  if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql.= ' AND te.fk_soc = ' . $socid;
1641  if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql.= ' AND (te.fk_soc = ' . $socid.' OR te.fk_soc IS NULL)';
1642  if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql.= ' AND te.rowid = ' . $socid;
1643  //print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
1644 
1645  $result = $this->db->query($sql);
1646  if (! $result)
1647  {
1648  $this->error=$this->db->lasterror();
1649  return -1;
1650  }
1651  $row = $this->db->fetch_row($result);
1652  $this->ref_previous = $row[0];
1653 
1654 
1655  $sql = "SELECT MIN(te.".$fieldid.")";
1656  $sql.= " FROM ".(empty($nodbprefix)?MAIN_DB_PREFIX:'').$this->table_element." as te";
1657  if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1658  $sql.= ",".MAIN_DB_PREFIX."usergroup_user as ug";
1659  }
1660  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ", ".MAIN_DB_PREFIX."societe as s"; // If we need to link to societe to limit select to entity
1661  else if ($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
1662  else if ($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
1663  if ($this->restrictiononfksoc && !$user->rights->societe->client->voir && !$socid) $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON ".$alias.".rowid = sc.fk_soc";
1664  $sql.= " WHERE te.".$fieldid." > '".$this->db->escape($this->ref)."'"; // ->ref must always be defined (set to id if field does not exists)
1665  if ($this->restrictiononfksoc == 1 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND sc.fk_user = " .$user->id;
1666  if ($this->restrictiononfksoc == 2 && !$user->rights->societe->client->voir && !$socid) $sql.= " AND (sc.fk_user = " .$user->id.' OR te.fk_soc IS NULL)';
1667  if (! empty($filter))
1668  {
1669  if (! preg_match('/^\s*AND/i', $filter)) $sql.=" AND "; // For backward compatibility
1670  $sql.=$filter;
1671  }
1672  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 2) $sql.= ' AND te.fk_soc = s.rowid'; // If we need to link to societe to limit select to entity
1673  else if ($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
1674  if (isset($this->ismultientitymanaged) && $this->ismultientitymanaged == 1) {
1675  if ($this->element == 'user' && ! empty($conf->global->MULTICOMPANY_TRANSVERSE_MODE)) {
1676  if (! empty($user->admin) && empty($user->entity) && $conf->entity == 1) {
1677  $sql.= " AND te.entity IS NOT NULL"; // Show all users
1678  } else {
1679  $sql.= " AND ug.fk_user = te.rowid";
1680  $sql.= " AND ug.entity IN (".getEntity($this->element).")";
1681  }
1682  } else {
1683  $sql.= ' AND te.entity IN ('.getEntity($this->element).')';
1684  }
1685  }
1686  if ($this->restrictiononfksoc == 1 && $socid && $this->element != 'societe') $sql.= ' AND te.fk_soc = ' . $socid;
1687  if ($this->restrictiononfksoc == 2 && $socid && $this->element != 'societe') $sql.= ' AND (te.fk_soc = ' . $socid.' OR te.fk_soc IS NULL)';
1688  if ($this->restrictiononfksoc && $socid && $this->element == 'societe') $sql.= ' AND te.rowid = ' . $socid;
1689  //print 'socid='.$socid.' restrictiononfksoc='.$this->restrictiononfksoc.' ismultientitymanaged = '.$this->ismultientitymanaged.' filter = '.$filter.' -> '.$sql."<br>";
1690  // 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
1691 
1692  $result = $this->db->query($sql);
1693  if (! $result)
1694  {
1695  $this->error=$this->db->lasterror();
1696  return -2;
1697  }
1698  $row = $this->db->fetch_row($result);
1699  $this->ref_next = $row[0];
1700 
1701  return 1;
1702  }
1703 
1704 
1712  function getListContactId($source='external')
1713  {
1714  $contactAlreadySelected = array();
1715  $tab = $this->liste_contact(-1,$source);
1716  $num=count($tab);
1717  $i = 0;
1718  while ($i < $num)
1719  {
1720  if ($source == 'thirdparty') $contactAlreadySelected[$i] = $tab[$i]['socid'];
1721  else $contactAlreadySelected[$i] = $tab[$i]['id'];
1722  $i++;
1723  }
1724  return $contactAlreadySelected;
1725  }
1726 
1727 
1734  function setProject($projectid)
1735  {
1736  if (! $this->table_element)
1737  {
1738  dol_syslog(get_class($this)."::setProject was called on objet with property table_element not defined",LOG_ERR);
1739  return -1;
1740  }
1741 
1742  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1743  if ($this->table_element == 'actioncomm')
1744  {
1745  if ($projectid) $sql.= ' SET fk_project = '.$projectid;
1746  else $sql.= ' SET fk_project = NULL';
1747  $sql.= ' WHERE id = '.$this->id;
1748  }
1749  else
1750  {
1751  if ($projectid) $sql.= ' SET fk_projet = '.$projectid;
1752  else $sql.= ' SET fk_projet = NULL';
1753  $sql.= ' WHERE rowid = '.$this->id;
1754  }
1755 
1756  dol_syslog(get_class($this)."::setProject", LOG_DEBUG);
1757  if ($this->db->query($sql))
1758  {
1759  $this->fk_project = $projectid;
1760  return 1;
1761  }
1762  else
1763  {
1764  dol_print_error($this->db);
1765  return -1;
1766  }
1767  }
1768 
1775  function setPaymentMethods($id)
1776  {
1777  dol_syslog(get_class($this).'::setPaymentMethods('.$id.')');
1778  if ($this->statut >= 0 || $this->element == 'societe')
1779  {
1780  // TODO uniformize field name
1781  $fieldname = 'fk_mode_reglement';
1782  if ($this->element == 'societe') $fieldname = 'mode_reglement';
1783  if (get_class($this) == 'Fournisseur') $fieldname = 'mode_reglement_supplier';
1784 
1785  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1786  $sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
1787  $sql .= ' WHERE rowid='.$this->id;
1788 
1789  if ($this->db->query($sql))
1790  {
1791  $this->mode_reglement_id = $id;
1792  // for supplier
1793  if (get_class($this) == 'Fournisseur') $this->mode_reglement_supplier_id = $id;
1794  return 1;
1795  }
1796  else
1797  {
1798  dol_syslog(get_class($this).'::setPaymentMethods Erreur '.$sql.' - '.$this->db->error());
1799  $this->error=$this->db->error();
1800  return -1;
1801  }
1802  }
1803  else
1804  {
1805  dol_syslog(get_class($this).'::setPaymentMethods, status of the object is incompatible');
1806  $this->error='Status of the object is incompatible '.$this->statut;
1807  return -2;
1808  }
1809  }
1810 
1817  function setMulticurrencyCode($code)
1818  {
1819  dol_syslog(get_class($this).'::setMulticurrencyCode('.$id.')');
1820  if ($this->statut >= 0 || $this->element == 'societe')
1821  {
1822  $fieldname = 'multicurrency_code';
1823 
1824  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1825  $sql .= ' SET '.$fieldname." = '".$this->db->escape($code)."'";
1826  $sql .= ' WHERE rowid='.$this->id;
1827 
1828  if ($this->db->query($sql))
1829  {
1830  $this->multicurrency_code = $code;
1831 
1832  list($fk_multicurrency, $rate) = MultiCurrency::getIdAndTxFromCode($this->db, $code);
1833  if ($rate) $this->setMulticurrencyRate($rate,2);
1834 
1835  return 1;
1836  }
1837  else
1838  {
1839  dol_syslog(get_class($this).'::setMulticurrencyCode Erreur '.$sql.' - '.$this->db->error());
1840  $this->error=$this->db->error();
1841  return -1;
1842  }
1843  }
1844  else
1845  {
1846  dol_syslog(get_class($this).'::setMulticurrencyCode, status of the object is incompatible');
1847  $this->error='Status of the object is incompatible '.$this->statut;
1848  return -2;
1849  }
1850  }
1851 
1859  function setMulticurrencyRate($rate, $mode=1)
1860  {
1861  dol_syslog(get_class($this).'::setMulticurrencyRate('.$id.')');
1862  if ($this->statut >= 0 || $this->element == 'societe')
1863  {
1864  $fieldname = 'multicurrency_tx';
1865 
1866  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1867  $sql .= ' SET '.$fieldname.' = '.$rate;
1868  $sql .= ' WHERE rowid='.$this->id;
1869 
1870  if ($this->db->query($sql))
1871  {
1872  $this->multicurrency_tx = $rate;
1873 
1874  // Update line price
1875  if (!empty($this->lines))
1876  {
1877  foreach ($this->lines as &$line)
1878  {
1879  if($mode == 1) {
1880  $line->subprice = 0;
1881  }
1882 
1883  switch ($this->element) {
1884  case 'propal':
1885  $this->updateline(
1886  $line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
1887  ($line->description?$line->description:$line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
1888  $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->date_start,
1889  $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1890  );
1891  break;
1892  case 'commande':
1893  $this->updateline(
1894  $line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1895  $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->date_start, $line->date_end,
1896  $line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
1897  $line->special_code, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1898  );
1899  break;
1900  case 'facture':
1901  $this->updateline(
1902  $line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1903  $line->date_start, $line->date_end, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits,
1904  $line->product_type, $line->fk_parent_line, $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label,
1905  $line->special_code, $line->array_options, $line->situation_percent, $line->fk_unit, $line->multicurrency_subprice
1906  );
1907  break;
1908  case 'supplier_proposal':
1909  $this->updateline(
1910  $line->id, $line->subprice, $line->qty, $line->remise_percent, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx,
1911  ($line->description?$line->description:$line->desc), 'HT', $line->info_bits, $line->special_code, $line->fk_parent_line,
1912  $line->skip_update_total, $line->fk_fournprice, $line->pa_ht, $line->label, $line->product_type, $line->array_options,
1913  $line->ref_fourn, $line->multicurrency_subprice
1914  );
1915  break;
1916  case 'order_supplier':
1917  $this->updateline(
1918  $line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->qty, $line->remise_percent,
1919  $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, 'HT', $line->info_bits, $line->product_type, false,
1920  $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1921  );
1922  break;
1923  case 'invoice_supplier':
1924  $this->updateline(
1925  $line->id, ($line->description?$line->description:$line->desc), $line->subprice, $line->tva_tx, $line->localtax1_tx,
1926  $line->localtax2_tx, $line->qty, 0, 'HT', $line->info_bits, $line->product_type, $line->remise_percent, false,
1927  $line->date_start, $line->date_end, $line->array_options, $line->fk_unit, $line->multicurrency_subprice
1928  );
1929  break;
1930  default:
1931  dol_syslog(get_class($this).'::setMulticurrencyRate no updateline defined', LOG_DEBUG);
1932  break;
1933  }
1934  }
1935  }
1936 
1937  return 1;
1938  }
1939  else
1940  {
1941  dol_syslog(get_class($this).'::setMulticurrencyRate Erreur '.$sql.' - '.$this->db->error());
1942  $this->error=$this->db->error();
1943  return -1;
1944  }
1945  }
1946  else
1947  {
1948  dol_syslog(get_class($this).'::setMulticurrencyRate, status of the object is incompatible');
1949  $this->error='Status of the object is incompatible '.$this->statut;
1950  return -2;
1951  }
1952  }
1953 
1960  function setPaymentTerms($id)
1961  {
1962  dol_syslog(get_class($this).'::setPaymentTerms('.$id.')');
1963  if ($this->statut >= 0 || $this->element == 'societe')
1964  {
1965  // TODO uniformize field name
1966  $fieldname = 'fk_cond_reglement';
1967  if ($this->element == 'societe') $fieldname = 'cond_reglement';
1968  if (get_class($this) == 'Fournisseur') $fieldname = 'cond_reglement_supplier';
1969 
1970  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
1971  $sql .= ' SET '.$fieldname.' = '.(($id > 0 || $id == '0') ? $id : 'NULL');
1972  $sql .= ' WHERE rowid='.$this->id;
1973 
1974  if ($this->db->query($sql))
1975  {
1976  $this->cond_reglement_id = $id;
1977  // for supplier
1978  if (get_class($this) == 'Fournisseur') $this->cond_reglement_supplier_id = $id;
1979  $this->cond_reglement = $id; // for compatibility
1980  return 1;
1981  }
1982  else
1983  {
1984  dol_syslog(get_class($this).'::setPaymentTerms Erreur '.$sql.' - '.$this->db->error());
1985  $this->error=$this->db->error();
1986  return -1;
1987  }
1988  }
1989  else
1990  {
1991  dol_syslog(get_class($this).'::setPaymentTerms, status of the object is incompatible');
1992  $this->error='Status of the object is incompatible '.$this->statut;
1993  return -2;
1994  }
1995  }
1996 
2004  function setDeliveryAddress($id)
2005  {
2006  $fieldname = 'fk_delivery_address';
2007  if ($this->element == 'delivery' || $this->element == 'shipping') $fieldname = 'fk_address';
2008 
2009  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET ".$fieldname." = ".$id;
2010  $sql.= " WHERE rowid = ".$this->id." AND fk_statut = 0";
2011 
2012  if ($this->db->query($sql))
2013  {
2014  $this->fk_delivery_address = $id;
2015  return 1;
2016  }
2017  else
2018  {
2019  $this->error=$this->db->error();
2020  dol_syslog(get_class($this).'::setDeliveryAddress Erreur '.$sql.' - '.$this->error);
2021  return -1;
2022  }
2023  }
2024 
2025 
2035  function setShippingMethod($shipping_method_id, $notrigger=false, $userused=null)
2036  {
2037  global $user;
2038 
2039  if (empty($userused)) $userused=$user;
2040 
2041  $error = 0;
2042 
2043  if (! $this->table_element) {
2044  dol_syslog(get_class($this)."::setShippingMethod was called on objet with property table_element not defined",LOG_ERR);
2045  return -1;
2046  }
2047 
2048  $this->db->begin();
2049 
2050  if ($shipping_method_id<0) $shipping_method_id='NULL';
2051  dol_syslog(get_class($this).'::setShippingMethod('.$shipping_method_id.')');
2052 
2053  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2054  $sql.= " SET fk_shipping_method = ".$shipping_method_id;
2055  $sql.= " WHERE rowid=".$this->id;
2056  $resql = $this->db->query($sql);
2057  if (! $resql) {
2058  dol_syslog(get_class($this).'::setShippingMethod Error ', LOG_DEBUG);
2059  $this->error = $this->db->lasterror();
2060  $error++;
2061  } else {
2062  if (!$notrigger)
2063  {
2064  // Call trigger
2065  $this->context=array('shippingmethodupdate'=>1);
2066  $result = $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $userused);
2067  if ($result < 0) $error++;
2068  // End call trigger
2069  }
2070  }
2071  if ($error)
2072  {
2073  $this->db->rollback();
2074  return -1;
2075  } else {
2076  $this->shipping_method_id = ($shipping_method_id=='NULL')?null:$shipping_method_id;
2077  $this->db->commit();
2078  return 1;
2079  }
2080  }
2081 
2082 
2089  function setWarehouse($warehouse_id)
2090  {
2091  if (! $this->table_element) {
2092  dol_syslog(get_class($this)."::setWarehouse was called on objet with property table_element not defined",LOG_ERR);
2093  return -1;
2094  }
2095  if ($warehouse_id<0) $warehouse_id='NULL';
2096  dol_syslog(get_class($this).'::setWarehouse('.$warehouse_id.')');
2097 
2098  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2099  $sql.= " SET fk_warehouse = ".$warehouse_id;
2100  $sql.= " WHERE rowid=".$this->id;
2101 
2102  if ($this->db->query($sql)) {
2103  $this->warehouse_id = ($warehouse_id=='NULL')?null:$warehouse_id;
2104  return 1;
2105  } else {
2106  dol_syslog(get_class($this).'::setWarehouse Error ', LOG_DEBUG);
2107  $this->error=$this->db->error();
2108  return 0;
2109  }
2110  }
2111 
2112 
2120  function setDocModel($user, $modelpdf)
2121  {
2122  if (! $this->table_element)
2123  {
2124  dol_syslog(get_class($this)."::setDocModel was called on objet with property table_element not defined",LOG_ERR);
2125  return -1;
2126  }
2127 
2128  $newmodelpdf=dol_trunc($modelpdf,255);
2129 
2130  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2131  $sql.= " SET model_pdf = '".$this->db->escape($newmodelpdf)."'";
2132  $sql.= " WHERE rowid = ".$this->id;
2133  // if ($this->element == 'facture') $sql.= " AND fk_statut < 2";
2134  // if ($this->element == 'propal') $sql.= " AND fk_statut = 0";
2135 
2136  dol_syslog(get_class($this)."::setDocModel", LOG_DEBUG);
2137  $resql=$this->db->query($sql);
2138  if ($resql)
2139  {
2140  $this->modelpdf=$modelpdf;
2141  return 1;
2142  }
2143  else
2144  {
2145  dol_print_error($this->db);
2146  return 0;
2147  }
2148  }
2149 
2150 
2159  function setBankAccount($fk_account, $notrigger=false, $userused=null)
2160  {
2161  global $user;
2162 
2163  if (empty($userused)) $userused=$user;
2164 
2165  $error = 0;
2166 
2167  if (! $this->table_element) {
2168  dol_syslog(get_class($this)."::setBankAccount was called on objet with property table_element not defined",LOG_ERR);
2169  return -1;
2170  }
2171  $this->db->begin();
2172 
2173  if ($fk_account<0) $fk_account='NULL';
2174  dol_syslog(get_class($this).'::setBankAccount('.$fk_account.')');
2175 
2176  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
2177  $sql.= " SET fk_account = ".$fk_account;
2178  $sql.= " WHERE rowid=".$this->id;
2179 
2180  $resql = $this->db->query($sql);
2181  if (! $resql)
2182  {
2183  dol_syslog(get_class($this).'::setBankAccount Error '.$sql.' - '.$this->db->error());
2184  $this->error = $this->db->lasterror();
2185  $error++;
2186  }
2187  else
2188  {
2189  if (!$notrigger)
2190  {
2191  // Call trigger
2192  $this->context=array('bankaccountupdate'=>1);
2193  $result = $this->call_trigger(strtoupper(get_class($this)) . '_MODIFY', $userused);
2194  if ($result < 0) $error++;
2195  // End call trigger
2196  }
2197  }
2198  if ($error)
2199  {
2200  $this->db->rollback();
2201  return -1;
2202  }
2203  else
2204  {
2205  $this->fk_account = ($fk_account=='NULL')?null:$fk_account;
2206  $this->db->commit();
2207  return 1;
2208  }
2209  }
2210 
2211 
2212  // TODO: Move line related operations to CommonObjectLine?
2213 
2214  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2224  function line_order($renum=false, $rowidorder='ASC', $fk_parent_line=true)
2225  {
2226  // phpcs:enable
2227  if (! $this->table_element_line)
2228  {
2229  dol_syslog(get_class($this)."::line_order was called on objet with property table_element_line not defined",LOG_ERR);
2230  return -1;
2231  }
2232  if (! $this->fk_element)
2233  {
2234  dol_syslog(get_class($this)."::line_order was called on objet with property fk_element not defined",LOG_ERR);
2235  return -1;
2236  }
2237 
2238  // Count number of lines to reorder (according to choice $renum)
2239  $nl=0;
2240  $sql = 'SELECT count(rowid) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2241  $sql.= ' WHERE '.$this->fk_element.'='.$this->id;
2242  if (! $renum) $sql.= ' AND rang = 0';
2243  if ($renum) $sql.= ' AND rang <> 0';
2244 
2245  dol_syslog(get_class($this)."::line_order", LOG_DEBUG);
2246  $resql = $this->db->query($sql);
2247  if ($resql)
2248  {
2249  $row = $this->db->fetch_row($resql);
2250  $nl = $row[0];
2251  }
2252  else dol_print_error($this->db);
2253  if ($nl > 0)
2254  {
2255  // The goal of this part is to reorder all lines, with all children lines sharing the same
2256  // counter that parents.
2257  $rows=array();
2258 
2259  // We first search all lines that are parent lines (for multilevel details lines)
2260  $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2261  $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2262  if ($fk_parent_line) $sql.= ' AND fk_parent_line IS NULL';
2263  $sql.= ' ORDER BY rang ASC, rowid '.$rowidorder;
2264 
2265  dol_syslog(get_class($this)."::line_order search all parent lines", LOG_DEBUG);
2266  $resql = $this->db->query($sql);
2267  if ($resql)
2268  {
2269  $i=0;
2270  $num = $this->db->num_rows($resql);
2271  while ($i < $num)
2272  {
2273  $row = $this->db->fetch_row($resql);
2274  $rows[] = $row[0]; // Add parent line into array rows
2275  $childrens = $this->getChildrenOfLine($row[0]);
2276  if (! empty($childrens))
2277  {
2278  foreach($childrens as $child)
2279  {
2280  array_push($rows, $child);
2281  }
2282  }
2283  $i++;
2284  }
2285 
2286  // Now we set a new number for each lines (parent and children with children included into parent tree)
2287  if (! empty($rows))
2288  {
2289  foreach($rows as $key => $row)
2290  {
2291  $this->updateRangOfLine($row, ($key+1));
2292  }
2293  }
2294  }
2295  else
2296  {
2297  dol_print_error($this->db);
2298  }
2299  }
2300  return 1;
2301  }
2302 
2309  function getChildrenOfLine($id)
2310  {
2311  $rows=array();
2312 
2313  $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2314  $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2315  $sql.= ' AND fk_parent_line = '.$id;
2316  $sql.= ' ORDER BY rang ASC';
2317 
2318  dol_syslog(get_class($this)."::getChildrenOfLine search children lines for line ".$id."", LOG_DEBUG);
2319  $resql = $this->db->query($sql);
2320  if ($resql)
2321  {
2322  $i=0;
2323  $num = $this->db->num_rows($resql);
2324  while ($i < $num)
2325  {
2326  $row = $this->db->fetch_row($resql);
2327  $rows[$i] = $row[0];
2328  $i++;
2329  }
2330  }
2331 
2332  return $rows;
2333  }
2334 
2335  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2343  function line_up($rowid, $fk_parent_line=true)
2344  {
2345  // phpcs:enable
2346  $this->line_order(false, 'ASC', $fk_parent_line);
2347 
2348  // Get rang of line
2349  $rang = $this->getRangOfLine($rowid);
2350 
2351  // Update position of line
2352  $this->updateLineUp($rowid, $rang);
2353  }
2354 
2355  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2363  function line_down($rowid, $fk_parent_line=true)
2364  {
2365  // phpcs:enable
2366  $this->line_order(false, 'ASC', $fk_parent_line);
2367 
2368  // Get rang of line
2369  $rang = $this->getRangOfLine($rowid);
2370 
2371  // Get max value for rang
2372  $max = $this->line_max();
2373 
2374  // Update position of line
2375  $this->updateLineDown($rowid, $rang, $max);
2376  }
2377 
2385  function updateRangOfLine($rowid,$rang)
2386  {
2387  $fieldposition = 'rang';
2388  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2389 
2390  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2391  $sql.= ' WHERE rowid = '.$rowid;
2392 
2393  dol_syslog(get_class($this)."::updateRangOfLine", LOG_DEBUG);
2394  if (! $this->db->query($sql))
2395  {
2396  dol_print_error($this->db);
2397  }
2398  }
2399 
2400  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2407  function line_ajaxorder($rows)
2408  {
2409  // phpcs:enable
2410  $num = count($rows);
2411  for ($i = 0 ; $i < $num ; $i++)
2412  {
2413  $this->updateRangOfLine($rows[$i], ($i+1));
2414  }
2415  }
2416 
2424  function updateLineUp($rowid,$rang)
2425  {
2426  if ($rang > 1)
2427  {
2428  $fieldposition = 'rang';
2429  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2430 
2431  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang ;
2432  $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2433  $sql.= ' AND rang = '.($rang - 1);
2434  if ($this->db->query($sql) )
2435  {
2436  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang - 1);
2437  $sql.= ' WHERE rowid = '.$rowid;
2438  if (! $this->db->query($sql) )
2439  {
2440  dol_print_error($this->db);
2441  }
2442  }
2443  else
2444  {
2445  dol_print_error($this->db);
2446  }
2447  }
2448  }
2449 
2458  function updateLineDown($rowid,$rang,$max)
2459  {
2460  if ($rang < $max)
2461  {
2462  $fieldposition = 'rang';
2463  if (in_array($this->table_element_line, array('ecm_files', 'emailcollector_emailcollectoraction'))) $fieldposition = 'position';
2464 
2465  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.$rang;
2466  $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2467  $sql.= ' AND rang = '.($rang+1);
2468  if ($this->db->query($sql) )
2469  {
2470  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element_line.' SET '.$fieldposition.' = '.($rang+1);
2471  $sql.= ' WHERE rowid = '.$rowid;
2472  if (! $this->db->query($sql) )
2473  {
2474  dol_print_error($this->db);
2475  }
2476  }
2477  else
2478  {
2479  dol_print_error($this->db);
2480  }
2481  }
2482  }
2483 
2490  function getRangOfLine($rowid)
2491  {
2492  $sql = 'SELECT rang FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2493  $sql.= ' WHERE rowid ='.$rowid;
2494 
2495  dol_syslog(get_class($this)."::getRangOfLine", LOG_DEBUG);
2496  $resql = $this->db->query($sql);
2497  if ($resql)
2498  {
2499  $row = $this->db->fetch_row($resql);
2500  return $row[0];
2501  }
2502  }
2503 
2510  function getIdOfLine($rang)
2511  {
2512  $sql = 'SELECT rowid FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2513  $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2514  $sql.= ' AND rang = '.$rang;
2515  $resql = $this->db->query($sql);
2516  if ($resql)
2517  {
2518  $row = $this->db->fetch_row($resql);
2519  return $row[0];
2520  }
2521  }
2522 
2523  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2530  function line_max($fk_parent_line=0)
2531  {
2532  // phpcs:enable
2533  // Search the last rang with fk_parent_line
2534  if ($fk_parent_line)
2535  {
2536  $sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2537  $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2538  $sql.= ' AND fk_parent_line = '.$fk_parent_line;
2539 
2540  dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2541  $resql = $this->db->query($sql);
2542  if ($resql)
2543  {
2544  $row = $this->db->fetch_row($resql);
2545  if (! empty($row[0]))
2546  {
2547  return $row[0];
2548  }
2549  else
2550  {
2551  return $this->getRangOfLine($fk_parent_line);
2552  }
2553  }
2554  }
2555  // If not, search the last rang of element
2556  else
2557  {
2558  $sql = 'SELECT max(rang) FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2559  $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2560 
2561  dol_syslog(get_class($this)."::line_max", LOG_DEBUG);
2562  $resql = $this->db->query($sql);
2563  if ($resql)
2564  {
2565  $row = $this->db->fetch_row($resql);
2566  return $row[0];
2567  }
2568  }
2569  }
2570 
2571  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2578  function update_ref_ext($ref_ext)
2579  {
2580  // phpcs:enable
2581  if (! $this->table_element)
2582  {
2583  dol_syslog(get_class($this)."::update_ref_ext was called on objet with property table_element not defined", LOG_ERR);
2584  return -1;
2585  }
2586 
2587  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2588  $sql.= " SET ref_ext = '".$this->db->escape($ref_ext)."'";
2589  $sql.= " WHERE ".(isset($this->table_rowid)?$this->table_rowid:'rowid')." = ". $this->id;
2590 
2591  dol_syslog(get_class($this)."::update_ref_ext", LOG_DEBUG);
2592  if ($this->db->query($sql))
2593  {
2594  $this->ref_ext = $ref_ext;
2595  return 1;
2596  }
2597  else
2598  {
2599  $this->error=$this->db->error();
2600  return -1;
2601  }
2602  }
2603 
2604  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2612  function update_note($note, $suffix='')
2613  {
2614  // phpcs:enable
2615  global $user;
2616 
2617  if (! $this->table_element)
2618  {
2619  $this->error='update_note was called on objet with property table_element not defined';
2620  dol_syslog(get_class($this)."::update_note was called on objet with property table_element not defined", LOG_ERR);
2621  return -1;
2622  }
2623  if (! in_array($suffix,array('','_public','_private')))
2624  {
2625  $this->error='update_note Parameter suffix must be empty, \'_private\' or \'_public\'';
2626  dol_syslog(get_class($this)."::update_note Parameter suffix must be empty, '_private' or '_public'", LOG_ERR);
2627  return -2;
2628  }
2629  // Special cas
2630  //var_dump($this->table_element);exit;
2631  if ($this->table_element == 'product') $suffix='';
2632 
2633  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element;
2634  $sql.= " SET note".$suffix." = ".(!empty($note)?("'".$this->db->escape($note)."'"):"NULL");
2635  $sql.= " ,".(in_array($this->table_element, array('actioncomm', 'adherent', 'advtargetemailing', 'cronjob', 'establishment'))?"fk_user_mod":"fk_user_modif")." = ".$user->id;
2636  $sql.= " WHERE rowid =". $this->id;
2637 
2638  dol_syslog(get_class($this)."::update_note", LOG_DEBUG);
2639  if ($this->db->query($sql))
2640  {
2641  if ($suffix == '_public') $this->note_public = $note;
2642  else if ($suffix == '_private') $this->note_private = $note;
2643  else
2644  {
2645  $this->note = $note; // deprecated
2646  $this->note_private = $note;
2647  }
2648  return 1;
2649  }
2650  else
2651  {
2652  $this->error=$this->db->lasterror();
2653  return -1;
2654  }
2655  }
2656 
2657  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2666  function update_note_public($note)
2667  {
2668  // phpcs:enable
2669  return $this->update_note($note,'_public');
2670  }
2671 
2672  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2683  function update_price($exclspec=0,$roundingadjust='none',$nodatabaseupdate=0,$seller=null)
2684  {
2685  // phpcs:enable
2686  global $conf, $hookmanager, $action;
2687 
2688  // Some external module want no update price after a trigger because they have another method to calculate the total (ex: with an extrafield)
2689  $MODULE = "";
2690  if ($this->element == 'propal')
2691  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_PROPOSAL";
2692  elseif ($this->element == 'order')
2693  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_ORDER";
2694  elseif ($this->element == 'facture')
2695  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_INVOICE";
2696  elseif ($this->element == 'facture_fourn')
2697  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_INVOICE";
2698  elseif ($this->element == 'order_supplier')
2699  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_ORDER";
2700  elseif ($this->element == 'supplier_proposal')
2701  $MODULE = "MODULE_DISALLOW_UPDATE_PRICE_SUPPLIER_PROPOSAL";
2702 
2703  if (! empty($MODULE)) {
2704  if (! empty($conf->global->$MODULE)) {
2705  $modsactivated = explode(',', $conf->global->$MODULE);
2706  foreach ($modsactivated as $mod) {
2707  if ($conf->$mod->enabled)
2708  return 1; // update was disabled by specific setup
2709  }
2710  }
2711  }
2712 
2713  include_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
2714 
2715  if ($roundingadjust == '-1') $roundingadjust='auto'; // For backward compatibility
2716 
2717  $forcedroundingmode=$roundingadjust;
2718  if ($forcedroundingmode == 'auto' && isset($conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND)) $forcedroundingmode=$conf->global->MAIN_ROUNDOFTOTAL_NOT_TOTALOFROUND;
2719  elseif ($forcedroundingmode == 'auto') $forcedroundingmode='0';
2720 
2721  $error=0;
2722 
2723  $multicurrency_tx = !empty($this->multicurrency_tx) ? $this->multicurrency_tx : 1;
2724 
2725  // Define constants to find lines to sum
2726  $fieldtva='total_tva';
2727  $fieldlocaltax1='total_localtax1';
2728  $fieldlocaltax2='total_localtax2';
2729  $fieldup='subprice';
2730  if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier')
2731  {
2732  $fieldtva='tva';
2733  $fieldup='pu_ht';
2734  }
2735  if ($this->element == 'expensereport')
2736  {
2737  $fieldup='value_unit';
2738  }
2739 
2740  $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,';
2741  $sql.= ' tva_tx as vatrate, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, info_bits, product_type';
2742  if ($this->table_element_line == 'facturedet') $sql.= ', situation_percent';
2743  $sql.= ', multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc';
2744  $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element_line;
2745  $sql.= ' WHERE '.$this->fk_element.' = '.$this->id;
2746  if ($exclspec)
2747  {
2748  $product_field='product_type';
2749  if ($this->table_element_line == 'contratdet') $product_field=''; // contratdet table has no product_type field
2750  if ($product_field) $sql.= ' AND '.$product_field.' <> 9';
2751  }
2752  $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
2753 
2754  dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2755  $resql = $this->db->query($sql);
2756  if ($resql)
2757  {
2758  $this->total_ht = 0;
2759  $this->total_tva = 0;
2760  $this->total_localtax1 = 0;
2761  $this->total_localtax2 = 0;
2762  $this->total_ttc = 0;
2763  $total_ht_by_vats = array();
2764  $total_tva_by_vats = array();
2765  $total_ttc_by_vats = array();
2766  $this->multicurrency_total_ht = 0;
2767  $this->multicurrency_total_tva = 0;
2768  $this->multicurrency_total_ttc = 0;
2769 
2770  $num = $this->db->num_rows($resql);
2771  $i = 0;
2772  while ($i < $num)
2773  {
2774  $obj = $this->db->fetch_object($resql);
2775 
2776  // Note: There is no check on detail line and no check on total, if $forcedroundingmode = 'none'
2777  $parameters=array('fk_element' => $obj->rowid);
2778  $reshook = $hookmanager->executeHooks('changeRoundingMode', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2779 
2780  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'
2781  {
2782  $localtax_array=array($obj->localtax1_type,$obj->localtax1_tx,$obj->localtax2_type,$obj->localtax2_tx);
2783  $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);
2784  $diff=price2num($tmpcal[1] - $obj->total_tva, 'MT', 1);
2785  if ($diff)
2786  {
2787  $sqlfix="UPDATE ".MAIN_DB_PREFIX.$this->table_element_line." SET ".$fieldtva." = ".$tmpcal[1].", total_ttc = ".$tmpcal[2]." WHERE rowid = ".$obj->rowid;
2788  dol_syslog('We found unconsistent data into detailed line (difference of '.$diff.') 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);
2789  $resqlfix=$this->db->query($sqlfix);
2790  if (! $resqlfix) dol_print_error($this->db,'Failed to update line');
2791  $obj->total_tva = $tmpcal[1];
2792  $obj->total_ttc = $tmpcal[2];
2793  //
2794  }
2795  }
2796 
2797  $this->total_ht += $obj->total_ht; // The field visible at end of line detail
2798  $this->total_tva += $obj->total_tva;
2799  $this->total_localtax1 += $obj->total_localtax1;
2800  $this->total_localtax2 += $obj->total_localtax2;
2801  $this->total_ttc += $obj->total_ttc;
2802  $this->multicurrency_total_ht += $obj->multicurrency_total_ht; // The field visible at end of line detail
2803  $this->multicurrency_total_tva += $obj->multicurrency_total_tva;
2804  $this->multicurrency_total_ttc += $obj->multicurrency_total_ttc;
2805 
2806  if (! isset($total_ht_by_vats[$obj->vatrate])) $total_ht_by_vats[$obj->vatrate]=0;
2807  if (! isset($total_tva_by_vats[$obj->vatrate])) $total_tva_by_vats[$obj->vatrate]=0;
2808  if (! isset($total_ttc_by_vats[$obj->vatrate])) $total_ttc_by_vats[$obj->vatrate]=0;
2809  $total_ht_by_vats[$obj->vatrate] += $obj->total_ht;
2810  $total_tva_by_vats[$obj->vatrate] += $obj->total_tva;
2811  $total_ttc_by_vats[$obj->vatrate] += $obj->total_ttc;
2812 
2813  if ($forcedroundingmode == '1') // Check if we need adjustement onto line for vat. TODO This works on the company currency but not on multicurrency
2814  {
2815  $tmpvat=price2num($total_ht_by_vats[$obj->vatrate] * $obj->vatrate / 100, 'MT', 1);
2816  $diff=price2num($total_tva_by_vats[$obj->vatrate]-$tmpvat, 'MT', 1);
2817  //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";
2818  if ($diff)
2819  {
2820  if (abs($diff) > 0.1) { dol_syslog('A rounding difference was detected into TOTAL but is too high to be corrected', LOG_WARNING); exit; }
2821  $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;
2822  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);
2823  $resqlfix=$this->db->query($sqlfix);
2824  if (! $resqlfix) dol_print_error($this->db,'Failed to update line');
2825  $this->total_tva -= $diff;
2826  $this->total_ttc -= $diff;
2827  $total_tva_by_vats[$obj->vatrate] -= $diff;
2828  $total_ttc_by_vats[$obj->vatrate] -= $diff;
2829  }
2830  }
2831 
2832  $i++;
2833  }
2834 
2835  // Add revenue stamp to total
2836  $this->total_ttc += isset($this->revenuestamp)?$this->revenuestamp:0;
2837  $this->multicurrency_total_ttc += isset($this->revenuestamp)?($this->revenuestamp * $multicurrency_tx):0;
2838 
2839  // Situations totals
2840  if ($this->situation_cycle_ref && $this->situation_counter > 1 && method_exists($this, 'get_prev_sits') && $this->type != $this::TYPE_CREDIT_NOTE )
2841  {
2842  $prev_sits = $this->get_prev_sits();
2843 
2844  foreach ($prev_sits as $sit) { // $sit is an object Facture loaded with a fetch.
2845  $this->total_ht -= $sit->total_ht;
2846  $this->total_tva -= $sit->total_tva;
2847  $this->total_localtax1 -= $sit->total_localtax1;
2848  $this->total_localtax2 -= $sit->total_localtax2;
2849  $this->total_ttc -= $sit->total_ttc;
2850  $this->multicurrency_total_ht -= $sit->multicurrency_total_ht;
2851  $this->multicurrency_total_tva -= $sit->multicurrency_total_tva;
2852  $this->multicurrency_total_ttc -= $sit->multicurrency_total_ttc;
2853  }
2854  }
2855 
2856  $this->db->free($resql);
2857 
2858  // Now update global field total_ht, total_ttc and tva
2859  $fieldht='total_ht';
2860  $fieldtva='tva';
2861  $fieldlocaltax1='localtax1';
2862  $fieldlocaltax2='localtax2';
2863  $fieldttc='total_ttc';
2864  // Specific code for backward compatibility with old field names
2865  if ($this->element == 'facture' || $this->element == 'facturerec') $fieldht='total';
2866  if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') $fieldtva='total_tva';
2867  if ($this->element == 'propal') $fieldttc='total';
2868  if ($this->element == 'expensereport') $fieldtva='total_tva';
2869  if ($this->element == 'supplier_proposal') $fieldttc='total';
2870 
2871  if (empty($nodatabaseupdate))
2872  {
2873  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET';
2874  $sql .= " ".$fieldht."='".price2num($this->total_ht)."',";
2875  $sql .= " ".$fieldtva."='".price2num($this->total_tva)."',";
2876  $sql .= " ".$fieldlocaltax1."='".price2num($this->total_localtax1)."',";
2877  $sql .= " ".$fieldlocaltax2."='".price2num($this->total_localtax2)."',";
2878  $sql .= " ".$fieldttc."='".price2num($this->total_ttc)."'";
2879  $sql .= ", multicurrency_total_ht='".price2num($this->multicurrency_total_ht, 'MT', 1)."'";
2880  $sql .= ", multicurrency_total_tva='".price2num($this->multicurrency_total_tva, 'MT', 1)."'";
2881  $sql .= ", multicurrency_total_ttc='".price2num($this->multicurrency_total_ttc, 'MT', 1)."'";
2882  $sql .= ' WHERE rowid = '.$this->id;
2883 
2884 
2885  dol_syslog(get_class($this)."::update_price", LOG_DEBUG);
2886  $resql=$this->db->query($sql);
2887  if (! $resql)
2888  {
2889  $error++;
2890  $this->error=$this->db->lasterror();
2891  $this->errors[]=$this->db->lasterror();
2892  }
2893  }
2894 
2895  if (! $error)
2896  {
2897  return 1;
2898  }
2899  else
2900  {
2901  return -1;
2902  }
2903  }
2904  else
2905  {
2906  dol_print_error($this->db,'Bad request in update_price');
2907  return -1;
2908  }
2909  }
2910 
2911  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
2920  function add_object_linked($origin=null, $origin_id=null)
2921  {
2922  // phpcs:enable
2923  $origin = (! empty($origin) ? $origin : $this->origin);
2924  $origin_id = (! empty($origin_id) ? $origin_id : $this->origin_id);
2925 
2926  // Special case
2927  if ($origin == 'order') $origin='commande';
2928  if ($origin == 'invoice') $origin='facture';
2929  if ($origin == 'invoice_template') $origin='facturerec';
2930 
2931  $this->db->begin();
2932 
2933  $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_element (";
2934  $sql.= "fk_source";
2935  $sql.= ", sourcetype";
2936  $sql.= ", fk_target";
2937  $sql.= ", targettype";
2938  $sql.= ") VALUES (";
2939  $sql.= $origin_id;
2940  $sql.= ", '".$this->db->escape($origin)."'";
2941  $sql.= ", ".$this->id;
2942  $sql.= ", '".$this->db->escape($this->element)."'";
2943  $sql.= ")";
2944 
2945  dol_syslog(get_class($this)."::add_object_linked", LOG_DEBUG);
2946  if ($this->db->query($sql))
2947  {
2948  $this->db->commit();
2949  return 1;
2950  }
2951  else
2952  {
2953  $this->error=$this->db->lasterror();
2954  $this->db->rollback();
2955  return 0;
2956  }
2957  }
2958 
2981  function fetchObjectLinked($sourceid=null,$sourcetype='',$targetid=null,$targettype='',$clause='OR',$alsosametype=1,$orderby='sourcetype',$loadalsoobjects=1)
2982  {
2983  global $conf;
2984 
2985  $this->linkedObjectsIds=array();
2986  $this->linkedObjects=array();
2987 
2988  $justsource=false;
2989  $justtarget=false;
2990  $withtargettype=false;
2991  $withsourcetype=false;
2992 
2993  if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid))
2994  {
2995  $justsource=true; // the source (id and type) is a search criteria
2996  if (! empty($targettype)) $withtargettype=true;
2997  }
2998  if (! empty($targetid) && ! empty($targettype) && empty($sourceid))
2999  {
3000  $justtarget=true; // the target (id and type) is a search criteria
3001  if (! empty($sourcetype)) $withsourcetype=true;
3002  }
3003 
3004  $sourceid = (! empty($sourceid) ? $sourceid : $this->id);
3005  $targetid = (! empty($targetid) ? $targetid : $this->id);
3006  $sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
3007  $targettype = (! empty($targettype) ? $targettype : $this->element);
3008 
3009  /*if (empty($sourceid) && empty($targetid))
3010  {
3011  dol_syslog('Bad usage of function. No source nor target id defined (nor as parameter nor as object id)', LOG_ERR);
3012  return -1;
3013  }*/
3014 
3015  // Links between objects are stored in table element_element
3016  $sql = 'SELECT rowid, fk_source, sourcetype, fk_target, targettype';
3017  $sql.= ' FROM '.MAIN_DB_PREFIX.'element_element';
3018  $sql.= " WHERE ";
3019  if ($justsource || $justtarget)
3020  {
3021  if ($justsource)
3022  {
3023  $sql.= "fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."'";
3024  if ($withtargettype) $sql.= " AND targettype = '".$targettype."'";
3025  }
3026  else if ($justtarget)
3027  {
3028  $sql.= "fk_target = ".$targetid." AND targettype = '".$targettype."'";
3029  if ($withsourcetype) $sql.= " AND sourcetype = '".$sourcetype."'";
3030  }
3031  }
3032  else
3033  {
3034  $sql.= "(fk_source = ".$sourceid." AND sourcetype = '".$sourcetype."')";
3035  $sql.= " ".$clause." (fk_target = ".$targetid." AND targettype = '".$targettype."')";
3036  }
3037  $sql .= ' ORDER BY '.$orderby;
3038 
3039  dol_syslog(get_class($this)."::fetchObjectLink", LOG_DEBUG);
3040  $resql = $this->db->query($sql);
3041  if ($resql)
3042  {
3043  $num = $this->db->num_rows($resql);
3044  $i = 0;
3045  while ($i < $num)
3046  {
3047  $obj = $this->db->fetch_object($resql);
3048  if ($justsource || $justtarget)
3049  {
3050  if ($justsource)
3051  {
3052  $this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
3053  }
3054  else if ($justtarget)
3055  {
3056  $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
3057  }
3058  }
3059  else
3060  {
3061  if ($obj->fk_source == $sourceid && $obj->sourcetype == $sourcetype)
3062  {
3063  $this->linkedObjectsIds[$obj->targettype][$obj->rowid]=$obj->fk_target;
3064  }
3065  if ($obj->fk_target == $targetid && $obj->targettype == $targettype)
3066  {
3067  $this->linkedObjectsIds[$obj->sourcetype][$obj->rowid]=$obj->fk_source;
3068  }
3069  }
3070  $i++;
3071  }
3072 
3073  if (! empty($this->linkedObjectsIds))
3074  {
3075  $tmparray = $this->linkedObjectsIds;
3076  foreach($tmparray as $objecttype => $objectids) // $objecttype is a module name ('facture', 'mymodule', ...) or a module name with a suffix ('project_task', 'mymodule_myobj', ...)
3077  {
3078  // Parse element/subelement (ex: project_task, cabinetmed_consultation, ...)
3079  $module = $element = $subelement = $objecttype;
3080  if ($objecttype != 'supplier_proposal' && $objecttype != 'order_supplier' && $objecttype != 'invoice_supplier'
3081  && preg_match('/^([^_]+)_([^_]+)/i',$objecttype,$regs))
3082  {
3083  $module = $element = $regs[1];
3084  $subelement = $regs[2];
3085  }
3086 
3087  $classpath = $element.'/class';
3088  // To work with non standard classpath or module name
3089  if ($objecttype == 'facture') {
3090  $classpath = 'compta/facture/class';
3091  }
3092  else if ($objecttype == 'facturerec') {
3093  $classpath = 'compta/facture/class'; $module = 'facture';
3094  }
3095  else if ($objecttype == 'propal') {
3096  $classpath = 'comm/propal/class';
3097  }
3098  else if ($objecttype == 'supplier_proposal') {
3099  $classpath = 'supplier_proposal/class';
3100  }
3101  else if ($objecttype == 'shipping') {
3102  $classpath = 'expedition/class'; $subelement = 'expedition'; $module = 'expedition_bon';
3103  }
3104  else if ($objecttype == 'delivery') {
3105  $classpath = 'livraison/class'; $subelement = 'livraison'; $module = 'livraison_bon';
3106  }
3107  else if ($objecttype == 'invoice_supplier' || $objecttype == 'order_supplier') {
3108  $classpath = 'fourn/class'; $module = 'fournisseur';
3109  }
3110  else if ($objecttype == 'fichinter') {
3111  $classpath = 'fichinter/class'; $subelement = 'fichinter'; $module = 'ficheinter';
3112  }
3113  else if ($objecttype == 'subscription') {
3114  $classpath = 'adherents/class'; $module = 'adherent';
3115  }
3116 
3117  // Set classfile
3118  $classfile = strtolower($subelement); $classname = ucfirst($subelement);
3119 
3120  if ($objecttype == 'order') {
3121  $classfile = 'commande'; $classname = 'Commande';
3122  }
3123  else if ($objecttype == 'invoice_supplier') {
3124  $classfile = 'fournisseur.facture'; $classname = 'FactureFournisseur';
3125  }
3126  else if ($objecttype == 'order_supplier') {
3127  $classfile = 'fournisseur.commande'; $classname = 'CommandeFournisseur';
3128  }
3129  else if ($objecttype == 'supplier_proposal') {
3130  $classfile = 'supplier_proposal'; $classname = 'SupplierProposal';
3131  }
3132  else if ($objecttype == 'facturerec') {
3133  $classfile = 'facture-rec'; $classname = 'FactureRec';
3134  }
3135  else if ($objecttype == 'subscription') {
3136  $classfile = 'subscription'; $classname = 'Subscription';
3137  }
3138 
3139  // Here $module, $classfile and $classname are set
3140  if ($conf->$module->enabled && (($element != $this->element) || $alsosametype))
3141  {
3142  if ($loadalsoobjects)
3143  {
3144  dol_include_once('/'.$classpath.'/'.$classfile.'.class.php');
3145  //print '/'.$classpath.'/'.$classfile.'.class.php '.class_exists($classname);
3146  if (class_exists($classname))
3147  {
3148  foreach($objectids as $i => $objectid) // $i is rowid into llx_element_element
3149  {
3150  $object = new $classname($this->db);
3151  $ret = $object->fetch($objectid);
3152  if ($ret >= 0)
3153  {
3154  $this->linkedObjects[$objecttype][$i] = $object;
3155  }
3156  }
3157  }
3158  }
3159  }
3160  else
3161  {
3162  unset($this->linkedObjectsIds[$objecttype]);
3163  }
3164  }
3165  }
3166  return 1;
3167  }
3168  else
3169  {
3170  dol_print_error($this->db);
3171  return -1;
3172  }
3173  }
3174 
3185  function updateObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='')
3186  {
3187  $updatesource=false;
3188  $updatetarget=false;
3189 
3190  if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $updatesource=true;
3191  else if (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $updatetarget=true;
3192 
3193  $sql = "UPDATE ".MAIN_DB_PREFIX."element_element SET ";
3194  if ($updatesource)
3195  {
3196  $sql.= "fk_source = ".$sourceid;
3197  $sql.= ", sourcetype = '".$this->db->escape($sourcetype)."'";
3198  $sql.= " WHERE fk_target = ".$this->id;
3199  $sql.= " AND targettype = '".$this->db->escape($this->element)."'";
3200  }
3201  else if ($updatetarget)
3202  {
3203  $sql.= "fk_target = ".$targetid;
3204  $sql.= ", targettype = '".$this->db->escape($targettype)."'";
3205  $sql.= " WHERE fk_source = ".$this->id;
3206  $sql.= " AND sourcetype = '".$this->db->escape($this->element)."'";
3207  }
3208 
3209  dol_syslog(get_class($this)."::updateObjectLinked", LOG_DEBUG);
3210  if ($this->db->query($sql))
3211  {
3212  return 1;
3213  }
3214  else
3215  {
3216  $this->error=$this->db->lasterror();
3217  return -1;
3218  }
3219  }
3220 
3232  function deleteObjectLinked($sourceid=null, $sourcetype='', $targetid=null, $targettype='', $rowid='')
3233  {
3234  $deletesource=false;
3235  $deletetarget=false;
3236 
3237  if (! empty($sourceid) && ! empty($sourcetype) && empty($targetid) && empty($targettype)) $deletesource=true;
3238  else if (empty($sourceid) && empty($sourcetype) && ! empty($targetid) && ! empty($targettype)) $deletetarget=true;
3239 
3240  $sourceid = (! empty($sourceid) ? $sourceid : $this->id);
3241  $sourcetype = (! empty($sourcetype) ? $sourcetype : $this->element);
3242  $targetid = (! empty($targetid) ? $targetid : $this->id);
3243  $targettype = (! empty($targettype) ? $targettype : $this->element);
3244 
3245  $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_element";
3246  $sql.= " WHERE";
3247  if ($rowid > 0)
3248  {
3249  $sql.=" rowid = ".$rowid;
3250  }
3251  else
3252  {
3253  if ($deletesource)
3254  {
3255  $sql.= " fk_source = ".$sourceid." AND sourcetype = '".$this->db->escape($sourcetype)."'";
3256  $sql.= " AND fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."'";
3257  }
3258  else if ($deletetarget)
3259  {
3260  $sql.= " fk_target = ".$targetid." AND targettype = '".$this->db->escape($targettype)."'";
3261  $sql.= " AND fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."'";
3262  }
3263  else
3264  {
3265  $sql.= " (fk_source = ".$this->id." AND sourcetype = '".$this->db->escape($this->element)."')";
3266  $sql.= " OR";
3267  $sql.= " (fk_target = ".$this->id." AND targettype = '".$this->db->escape($this->element)."')";
3268  }
3269  }
3270 
3271  dol_syslog(get_class($this)."::deleteObjectLinked", LOG_DEBUG);
3272  if ($this->db->query($sql))
3273  {
3274  return 1;
3275  }
3276  else
3277  {
3278  $this->error=$this->db->lasterror();
3279  $this->errors[]=$this->error;
3280  return -1;
3281  }
3282  }
3283 
3293  function setStatut($status, $elementId=null, $elementType='', $trigkey='')
3294  {
3295  global $user,$langs,$conf;
3296 
3297  $savElementId=$elementId; // To be used later to know if we were using the method using the id of this or not.
3298 
3299  $elementId = (!empty($elementId)?$elementId:$this->id);
3300  $elementTable = (!empty($elementType)?$elementType:$this->table_element);
3301 
3302  $this->db->begin();
3303 
3304  $fieldstatus="fk_statut";
3305  if ($elementTable == 'facture_rec') $fieldstatus="suspended";
3306  if ($elementTable == 'mailing') $fieldstatus="statut";
3307  if ($elementTable == 'cronjob') $fieldstatus="status";
3308  if ($elementTable == 'user') $fieldstatus="statut";
3309  if ($elementTable == 'expensereport') $fieldstatus="fk_statut";
3310  if ($elementTable == 'commande_fournisseur_dispatch') $fieldstatus="status";
3311 
3312  $sql = "UPDATE ".MAIN_DB_PREFIX.$elementTable;
3313  $sql.= " SET ".$fieldstatus." = ".$status;
3314  // If status = 1 = validated, update also fk_user_valid
3315  if ($status == 1 && $elementTable == 'expensereport') $sql.=", fk_user_valid = ".$user->id;
3316  $sql.= " WHERE rowid=".$elementId;
3317 
3318  dol_syslog(get_class($this)."::setStatut", LOG_DEBUG);
3319  if ($this->db->query($sql))
3320  {
3321  $error = 0;
3322 
3323  // Try autoset of trigkey
3324  if (empty($trigkey))
3325  {
3326  if ($this->element == 'supplier_proposal' && $status == 2) $trigkey='SUPPLIER_PROPOSAL_SIGN'; // 2 = SupplierProposal::STATUS_SIGNED. Can't use constant into this generic class
3327  if ($this->element == 'supplier_proposal' && $status == 3) $trigkey='SUPPLIER_PROPOSAL_REFUSE'; // 3 = SupplierProposal::STATUS_REFUSED. Can't use constant into this generic class
3328  if ($this->element == 'supplier_proposal' && $status == 4) $trigkey='SUPPLIER_PROPOSAL_CLOSE'; // 4 = SupplierProposal::STATUS_CLOSED. Can't use constant into this generic class
3329  if ($this->element == 'fichinter' && $status == 3) $trigkey='FICHINTER_CLASSIFY_DONE';
3330  if ($this->element == 'fichinter' && $status == 2) $trigkey='FICHINTER_CLASSIFY_BILLED';
3331  if ($this->element == 'fichinter' && $status == 1) $trigkey='FICHINTER_CLASSIFY_UNBILLED';
3332  }
3333 
3334  if ($trigkey)
3335  {
3336  // Appel des triggers
3337  include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
3338  $interface=new Interfaces($this->db);
3339  $result=$interface->run_triggers($trigkey,$this,$user,$langs,$conf);
3340  if ($result < 0) {
3341  $error++; $this->errors=$interface->errors;
3342  }
3343  // Fin appel triggers
3344  }
3345 
3346  if (! $error)
3347  {
3348  $this->db->commit();
3349 
3350  if (empty($savElementId)) // If the element we update was $this (so $elementId is null)
3351  {
3352  $this->statut = $status;
3353  $this->status = $status;
3354  }
3355 
3356  return 1;
3357  }
3358  else
3359  {
3360  $this->db->rollback();
3361  dol_syslog(get_class($this)."::setStatus ".$this->error,LOG_ERR);
3362  return -1;
3363  }
3364  }
3365  else
3366  {
3367  $this->error=$this->db->lasterror();
3368  $this->db->rollback();
3369  return -1;
3370  }
3371  }
3372 
3373 
3381  function getCanvas($id=0,$ref='')
3382  {
3383  global $conf;
3384 
3385  if (empty($id) && empty($ref)) return 0;
3386  if (! empty($conf->global->MAIN_DISABLE_CANVAS)) return 0; // To increase speed. Not enabled by default.
3387 
3388  // Clean parameters
3389  $ref = trim($ref);
3390 
3391  $sql = "SELECT rowid, canvas";
3392  $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element;
3393  $sql.= " WHERE entity IN (".getEntity($this->element).")";
3394  if (! empty($id)) $sql.= " AND rowid = ".$id;
3395  if (! empty($ref)) $sql.= " AND ref = '".$this->db->escape($ref)."'";
3396 
3397  $resql = $this->db->query($sql);
3398  if ($resql)
3399  {
3400  $obj = $this->db->fetch_object($resql);
3401  if ($obj)
3402  {
3403  $this->canvas = $obj->canvas;
3404  return 1;
3405  }
3406  else return 0;
3407  }
3408  else
3409  {
3410  dol_print_error($this->db);
3411  return -1;
3412  }
3413  }
3414 
3415 
3422  function getSpecialCode($lineid)
3423  {
3424  $sql = 'SELECT special_code FROM '.MAIN_DB_PREFIX.$this->table_element_line;
3425  $sql.= ' WHERE rowid = '.$lineid;
3426  $resql = $this->db->query($sql);
3427  if ($resql)
3428  {
3429  $row = $this->db->fetch_row($resql);
3430  return $row[0];
3431  }
3432  }
3433 
3441  function isObjectUsed($id=0)
3442  {
3443  global $langs;
3444 
3445  if (empty($id)) $id=$this->id;
3446 
3447  // Check parameters
3448  if (! isset($this->childtables) || ! is_array($this->childtables) || count($this->childtables) == 0)
3449  {
3450  dol_print_error('Called isObjectUsed on a class with property this->childtables not defined');
3451  return -1;
3452  }
3453 
3454  $arraytoscan = $this->childtables;
3455  // For backward compatibility, we check if array is old format array('table1', 'table2', ...)
3456  $tmparray=array_keys($this->childtables);
3457  if (is_numeric($tmparray[0]))
3458  {
3459  $arraytoscan = array_flip($this->childtables);
3460  }
3461 
3462  // Test if child exists
3463  $haschild=0;
3464  foreach($arraytoscan as $table => $elementname)
3465  {
3466  //print $id.'-'.$table.'-'.$elementname.'<br>';
3467  // Check if third party can be deleted
3468  $sql = "SELECT COUNT(*) as nb from ".MAIN_DB_PREFIX.$table;
3469  $sql.= " WHERE ".$this->fk_element." = ".$id;
3470  $resql=$this->db->query($sql);
3471  if ($resql)
3472  {
3473  $obj=$this->db->fetch_object($resql);
3474  if ($obj->nb > 0)
3475  {
3476  $langs->load("errors");
3477  //print 'Found into table '.$table.', type '.$langs->transnoentitiesnoconv($elementname).', haschild='.$haschild;
3478  $haschild += $obj->nb;
3479  if (is_numeric($elementname)) // old usage
3480  {
3481  $this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $table);
3482  }
3483  else // new usage: $elementname=Translation key
3484  {
3485  $this->errors[]=$langs->trans("ErrorRecordHasAtLeastOneChildOfType", $langs->transnoentitiesnoconv($elementname));
3486  }
3487  break; // We found at least one, we stop here
3488  }
3489  }
3490  else
3491  {
3492  $this->errors[]=$this->db->lasterror();
3493  return -1;
3494  }
3495  }
3496  if ($haschild > 0)
3497  {
3498  $this->errors[]="ErrorRecordHasChildren";
3499  return $haschild;
3500  }
3501  else return 0;
3502  }
3503 
3510  function hasProductsOrServices($predefined=-1)
3511  {
3512  $nb=0;
3513 
3514  foreach($this->lines as $key => $val)
3515  {
3516  $qualified=0;
3517  if ($predefined == -1) $qualified=1;
3518  if ($predefined == 1 && $val->fk_product > 0) $qualified=1;
3519  if ($predefined == 0 && $val->fk_product <= 0) $qualified=1;
3520  if ($predefined == 2 && $val->fk_product > 0 && $val->product_type==0) $qualified=1;
3521  if ($predefined == 3 && $val->fk_product > 0 && $val->product_type==1) $qualified=1;
3522  if ($qualified) $nb++;
3523  }
3524  dol_syslog(get_class($this).'::hasProductsOrServices we found '.$nb.' qualified lines of products/servcies');
3525  return $nb;
3526  }
3527 
3533  function getTotalDiscount()
3534  {
3535  $total_discount=0.00;
3536 
3537  $sql = "SELECT subprice as pu_ht, qty, remise_percent, total_ht";
3538  $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element."det";
3539  $sql.= " WHERE ".$this->fk_element." = ".$this->id;
3540 
3541  dol_syslog(get_class($this).'::getTotalDiscount', LOG_DEBUG);
3542  $resql = $this->db->query($sql);
3543  if ($resql)
3544  {
3545  $num=$this->db->num_rows($resql);
3546  $i=0;
3547  while ($i < $num)
3548  {
3549  $obj = $this->db->fetch_object($resql);
3550 
3551  $pu_ht = $obj->pu_ht;
3552  $qty= $obj->qty;
3553  $total_ht = $obj->total_ht;
3554 
3555  $total_discount_line = floatval(price2num(($pu_ht * $qty) - $total_ht, 'MT'));
3556  $total_discount += $total_discount_line;
3557 
3558  $i++;
3559  }
3560  }
3561 
3562  //print $total_discount; exit;
3563  return price2num($total_discount);
3564  }
3565 
3566 
3573  function getTotalWeightVolume()
3574  {
3575  $totalWeight = 0;
3576  $totalVolume = 0;
3577  // defined for shipment only
3578  $totalOrdered = '';
3579  // defined for shipment only
3580  $totalToShip = '';
3581 
3582  foreach ($this->lines as $line)
3583  {
3584  if (isset($line->qty_asked))
3585  {
3586  if (empty($totalOrdered)) $totalOrdered=0; // Avoid warning because $totalOrdered is ''
3587  $totalOrdered+=$line->qty_asked; // defined for shipment only
3588  }
3589  if (isset($line->qty_shipped))
3590  {
3591  if (empty($totalToShip)) $totalToShip=0; // Avoid warning because $totalToShip is ''
3592  $totalToShip+=$line->qty_shipped; // defined for shipment only
3593  }
3594 
3595  // Define qty, weight, volume, weight_units, volume_units
3596  if ($this->element == 'shipping') {
3597  // for shipments
3598  $qty = $line->qty_shipped ? $line->qty_shipped : 0;
3599  }
3600  else {
3601  $qty = $line->qty ? $line->qty : 0;
3602  }
3603 
3604  $weight = $line->weight ? $line->weight : 0;
3605  $volume = $line->volume ? $line->volume : 0;
3606 
3607  $weight_units=$line->weight_units;
3608  $volume_units=$line->volume_units;
3609 
3610  $weightUnit=0;
3611  $volumeUnit=0;
3612  if (! empty($weight_units)) $weightUnit = $weight_units;
3613  if (! empty($volume_units)) $volumeUnit = $volume_units;
3614 
3615  if (empty($totalWeight)) $totalWeight=0; // Avoid warning because $totalWeight is ''
3616  if (empty($totalVolume)) $totalVolume=0; // Avoid warning because $totalVolume is ''
3617 
3618  //var_dump($line->volume_units);
3619  if ($weight_units < 50) // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3620  {
3621  $trueWeightUnit=pow(10, $weightUnit);
3622  $totalWeight += $weight * $qty * $trueWeightUnit;
3623  }
3624  else {
3625  if ($weight_units == 99) {
3626  // conversion 1 Pound = 0.45359237 KG
3627  $trueWeightUnit = 0.45359237;
3628  $totalWeight += $weight * $qty * $trueWeightUnit;
3629  } elseif ($weight_units == 98) {
3630  // conversion 1 Ounce = 0.0283495 KG
3631  $trueWeightUnit = 0.0283495;
3632  $totalWeight += $weight * $qty * $trueWeightUnit;
3633  }
3634  else
3635  $totalWeight += $weight * $qty; // This may be wrong if we mix different units
3636  }
3637  if ($volume_units < 50) // >50 means a standard unit (power of 10 of official unit), > 50 means an exotic unit (like inch)
3638  {
3639  //print $line->volume."x".$line->volume_units."x".($line->volume_units < 50)."x".$volumeUnit;
3640  $trueVolumeUnit=pow(10, $volumeUnit);
3641  //print $line->volume;
3642  $totalVolume += $volume * $qty * $trueVolumeUnit;
3643  }
3644  else
3645  {
3646  $totalVolume += $volume * $qty; // This may be wrong if we mix different units
3647  }
3648  }
3649 
3650  return array('weight'=>$totalWeight, 'volume'=>$totalVolume, 'ordered'=>$totalOrdered, 'toship'=>$totalToShip);
3651  }
3652 
3653 
3659  function setExtraParameters()
3660  {
3661  $this->db->begin();
3662 
3663  $extraparams = (! empty($this->extraparams) ? json_encode($this->extraparams) : null);
3664 
3665  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3666  $sql.= " SET extraparams = ".(! empty($extraparams) ? "'".$this->db->escape($extraparams)."'" : "null");
3667  $sql.= " WHERE rowid = ".$this->id;
3668 
3669  dol_syslog(get_class($this)."::setExtraParameters", LOG_DEBUG);
3670  $resql = $this->db->query($sql);
3671  if (! $resql)
3672  {
3673  $this->error=$this->db->lasterror();
3674  $this->db->rollback();
3675  return -1;
3676  }
3677  else
3678  {
3679  $this->db->commit();
3680  return 1;
3681  }
3682  }
3683 
3684 
3685  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
3692  function display_incoterms()
3693  {
3694  // phpcs:enable
3695  $out = '';
3696  $this->libelle_incoterms = '';
3697  if (!empty($this->fk_incoterms))
3698  {
3699  $sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3700  $result = $this->db->query($sql);
3701  if ($result)
3702  {
3703  $res = $this->db->fetch_object($result);
3704  $out .= $res->code;
3705  }
3706  }
3707 
3708  $out .= (($res->code && $this->location_incoterms)?' - ':'').$this->location_incoterms;
3709 
3710  return $out;
3711  }
3712 
3718  function getIncotermsForPDF()
3719  {
3720  $sql = 'SELECT code FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3721  $resql = $this->db->query($sql);
3722  if ($resql)
3723  {
3724  $num = $this->db->num_rows($resql);
3725  if ($num > 0)
3726  {
3727  $res = $this->db->fetch_object($resql);
3728  return 'Incoterm : '.$res->code.' - '.$this->location_incoterms;
3729  }
3730  else
3731  {
3732  return '';
3733  }
3734  }
3735  else
3736  {
3737  $this->errors[] = $this->db->lasterror();
3738  return false;
3739  }
3740  }
3741 
3749  function setIncoterms($id_incoterm, $location)
3750  {
3751  if ($this->id && $this->table_element)
3752  {
3753  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element;
3754  $sql.= " SET fk_incoterms = ".($id_incoterm > 0 ? $id_incoterm : "null");
3755  $sql.= ", location_incoterms = ".($id_incoterm > 0 ? "'".$this->db->escape($location)."'" : "null");
3756  $sql.= " WHERE rowid = " . $this->id;
3757  dol_syslog(get_class($this).'::setIncoterms', LOG_DEBUG);
3758  $resql=$this->db->query($sql);
3759  if ($resql)
3760  {
3761  $this->fk_incoterms = $id_incoterm;
3762  $this->location_incoterms = $location;
3763 
3764  $sql = 'SELECT libelle FROM '.MAIN_DB_PREFIX.'c_incoterms WHERE rowid = '.(int) $this->fk_incoterms;
3765  $res = $this->db->query($sql);
3766  if ($res)
3767  {
3768  $obj = $this->db->fetch_object($res);
3769  $this->libelle_incoterms = $obj->libelle;
3770  }
3771  return 1;
3772  }
3773  else
3774  {
3775  $this->errors[] = $this->db->lasterror();
3776  return -1;
3777  }
3778  }
3779  else return -1;
3780  }
3781 
3782 
3783  // --------------------
3784  // TODO: All functions here must be redesigned and moved as they are not business functions but output functions
3785  // --------------------
3786 
3787  /* This is to show add lines */
3788 
3797  function formAddObjectLine($dateSelector, $seller, $buyer)
3798  {
3799  global $conf,$user,$langs,$object,$hookmanager;
3800  global $form,$bcnd,$var;
3801 
3802  // Line extrafield
3803  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3804  $extrafieldsline = new ExtraFields($this->db);
3805  $extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3806 
3807  // Output template part (modules that overwrite templates must declare this into descriptor)
3808  // Use global variables + $dateSelector + $seller and $buyer
3809  $dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
3810  foreach($dirtpls as $reldir)
3811  {
3812  $tpl = dol_buildpath($reldir.'/objectline_create.tpl.php');
3813  if (empty($conf->file->strict_mode)) {
3814  $res=@include $tpl;
3815  } else {
3816  $res=include $tpl; // for debug
3817  }
3818  if ($res) break;
3819  }
3820  }
3821 
3822 
3823 
3824  /* This is to show array of line of details */
3825 
3826 
3840  function printObjectLines($action, $seller, $buyer, $selected=0, $dateSelector=0)
3841  {
3842  global $conf, $hookmanager, $langs, $user;
3843  // TODO We should not use global var for this !
3844  global $inputalsopricewithtax, $usemargins, $disableedit, $disablemove, $disableremove, $outputalsopricetotalwithtax;
3845 
3846  // Define usemargins
3847  $usemargins=0;
3848  if (! empty($conf->margin->enabled) && ! empty($this->element) && in_array($this->element,array('facture','propal','commande'))) $usemargins=1;
3849 
3850  $num = count($this->lines);
3851 
3852  // Line extrafield
3853  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
3854  $extrafieldsline = new ExtraFields($this->db);
3855  $extralabelslines=$extrafieldsline->fetch_name_optionals_label($this->table_element_line);
3856 
3857  $parameters = array('num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
3858  $reshook = $hookmanager->executeHooks('printObjectLineTitle', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3859  if (empty($reshook))
3860  {
3861  // Title line
3862  print "<thead>\n";
3863 
3864  print '<tr class="liste_titre nodrag nodrop">';
3865 
3866  // Adds a line numbering column
3867  if (! empty($conf->global->MAIN_VIEW_LINE_NUMBER)) print '<td class="linecolnum" align="center" width="5">&nbsp;</td>';
3868 
3869  // Description
3870  print '<td class="linecoldescription">'.$langs->trans('Description').'</td>';
3871 
3872  if ($this->element == 'supplier_proposal' || $this->element == 'order_supplier' || $this->element == 'invoice_supplier')
3873  {
3874  print '<td class="linerefsupplier"><span id="title_fourn_ref">'.$langs->trans("SupplierRef").'</span></td>';
3875  }
3876 
3877  // VAT
3878  print '<td class="linecolvat" align="right" width="80">'.$langs->trans('VAT').'</td>';
3879 
3880  // Price HT
3881  print '<td class="linecoluht" align="right" width="80">'.$langs->trans('PriceUHT').'</td>';
3882 
3883  // Multicurrency
3884  if (!empty($conf->multicurrency->enabled) && $this->multicurrency_code != $conf->currency) print '<td class="linecoluht_currency" align="right" width="80">'.$langs->trans('PriceUHTCurrency', $this->multicurrency_code).'</td>';
3885 
3886  if ($inputalsopricewithtax) print '<td align="right" width="80">'.$langs->trans('PriceUTTC').'</td>';
3887 
3888  // Qty
3889  print '<td class="linecolqty" align="right">'.$langs->trans('Qty').'</td>';
3890 
3891  if($conf->global->PRODUCT_USE_UNITS)
3892  {
3893  print '<td class="linecoluseunit" align="left">'.$langs->trans('Unit').'</td>';
3894  }
3895 
3896  // Reduction short
3897  print '<td class="linecoldiscount" align="right">'.$langs->trans('ReductionShort').'</td>';
3898 
3899  if ($this->situation_cycle_ref) {
3900  print '<td class="linecolcycleref" align="right">' . $langs->trans('Progress') . '</td>';
3901  }
3902 
3903  if ($usemargins && ! empty($conf->margin->enabled) && empty($user->societe_id))
3904  {
3905  if (!empty($user->rights->margins->creer))
3906  {
3907  if ($conf->global->MARGIN_TYPE == "1")
3908  print '<td class="linecolmargin1 margininfos" align="right" width="80">'.$langs->trans('BuyingPrice').'</td>';
3909  else
3910  print '<td class="linecolmargin1 margininfos" align="right" width="80">'.$langs->trans('CostPrice').'</td>';
3911  }
3912 
3913  if (! empty($conf->global->DISPLAY_MARGIN_RATES) && $user->rights->margins->liretous)
3914  print '<td class="linecolmargin2 margininfos" align="right" width="50">'.$langs->trans('MarginRate').'</td>';
3915  if (! empty($conf->global->DISPLAY_MARK_RATES) && $user->rights->margins->liretous)
3916  print '<td class="linecolmargin2 margininfos" align="right" width="50">'.$langs->trans('MarkRate').'</td>';
3917  }
3918 
3919  // Total HT
3920  print '<td class="linecolht" align="right">'.$langs->trans('TotalHTShort').'</td>';
3921 
3922  // Multicurrency
3923  if (!empty($conf->multicurrency->enabled) && $this->multicurrency_code != $conf->currency) print '<td class="linecoltotalht_currency" align="right">'.$langs->trans('TotalHTShortCurrency', $this->multicurrency_code).'</td>';
3924 
3925  if ($outputalsopricetotalwithtax) print '<td align="right" width="80">'.$langs->trans('TotalTTCShort').'</td>';
3926 
3927  print '<td class="linecoledit"></td>'; // No width to allow autodim
3928 
3929  print '<td class="linecoldelete" width="10"></td>';
3930 
3931  print '<td class="linecolmove" width="10"></td>';
3932 
3933  if($action == 'selectlines')
3934  {
3935  print '<td class="linecolcheckall" align="center">';
3936  print '<input type="checkbox" class="linecheckboxtoggle" />';
3937  print '<script type="text/javascript">$(document).ready(function() {$(".linecheckboxtoggle").click(function() {var checkBoxes = $(".linecheckbox");checkBoxes.prop("checked", this.checked);})});</script>';
3938  print '</td>';
3939  }
3940 
3941  print "</tr>\n";
3942  print "</thead>\n";
3943  }
3944 
3945  $var = true;
3946  $i = 0;
3947 
3948  print "<tbody>\n";
3949  foreach ($this->lines as $line)
3950  {
3951  //Line extrafield
3952  $line->fetch_optionals();
3953 
3954  //if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
3955  if (is_object($hookmanager)) // Old code is commented on preceding line.
3956  {
3957  if (empty($line->fk_parent_line))
3958  {
3959  $parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline);
3960  $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3961  }
3962  else
3963  {
3964  $parameters = array('line'=>$line,'var'=>$var,'num'=>$num,'i'=>$i,'dateSelector'=>$dateSelector,'seller'=>$seller,'buyer'=>$buyer,'selected'=>$selected, 'extrafieldsline'=>$extrafieldsline, 'fk_parent_line'=>$line->fk_parent_line);
3965  $reshook = $hookmanager->executeHooks('printObjectSubLine', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
3966  }
3967  }
3968  if (empty($reshook))
3969  {
3970  $this->printObjectLine($action,$line,$var,$num,$i,$dateSelector,$seller,$buyer,$selected,$extrafieldsline);
3971  }
3972 
3973  $i++;
3974  }
3975  print "</tbody>\n";
3976  }
3977 
3994  function printObjectLine($action,$line,$var,$num,$i,$dateSelector,$seller,$buyer,$selected=0,$extrafieldsline=0)
3995  {
3996  global $conf,$langs,$user,$object,$hookmanager;
3997  global $form,$bc,$bcdd;
3998  global $object_rights, $disableedit, $disablemove, $disableremove; // TODO We should not use global var for this !
3999 
4000  $object_rights = $this->getRights();
4001 
4002  $element=$this->element;
4003 
4004  $text=''; $description=''; $type=0;
4005 
4006  // Show product and description
4007  $type=(! empty($line->product_type)?$line->product_type:$line->fk_product_type);
4008  // Try to enhance type detection using date_start and date_end for free lines where type was not saved.
4009  if (! empty($line->date_start)) $type=1; // deprecated
4010  if (! empty($line->date_end)) $type=1; // deprecated
4011 
4012  // Ligne en mode visu
4013  if ($action != 'editline' || $selected != $line->id)
4014  {
4015  // Product
4016  if ($line->fk_product > 0)
4017  {
4018  $product_static = new Product($this->db);
4019  $product_static->fetch($line->fk_product);
4020 
4021  $product_static->ref = $line->ref; //can change ref in hook
4022  $product_static->label = $line->label; //can change label in hook
4023  $text=$product_static->getNomUrl(1);
4024 
4025  // Define output language and label
4026  if (! empty($conf->global->MAIN_MULTILANGS))
4027  {
4028  if (! is_object($this->thirdparty))
4029  {
4030  dol_print_error('','Error: Method printObjectLine was called on an object and object->fetch_thirdparty was not done before');
4031  return;
4032  }
4033 
4034  $prod = new Product($this->db);
4035  $prod->fetch($line->fk_product);
4036 
4037  $outputlangs = $langs;
4038  $newlang='';
4039  if (empty($newlang) && GETPOST('lang_id','aZ09')) $newlang=GETPOST('lang_id','aZ09');
4040  if (! empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE) && empty($newlang)) $newlang=$this->thirdparty->default_lang; // For language to language of customer
4041  if (! empty($newlang))
4042  {
4043  $outputlangs = new Translate("",$conf);
4044  $outputlangs->setDefaultLang($newlang);
4045  }
4046 
4047  $label = (! empty($prod->multilangs[$outputlangs->defaultlang]["label"])) ? $prod->multilangs[$outputlangs->defaultlang]["label"] : $line->product_label;
4048  }
4049  else
4050  {
4051  $label = $line->product_label;
4052  }
4053 
4054  $text.= ' - '.(! empty($line->label)?$line->label:$label);
4055  $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.
4056  }
4057 
4058  $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
4059 
4060  // Output template part (modules that overwrite templates must declare this into descriptor)
4061  // Use global variables + $dateSelector + $seller and $buyer
4062  $dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
4063  foreach($dirtpls as $reldir)
4064  {
4065  $tpl = dol_buildpath($reldir.'/objectline_view.tpl.php');
4066  if (empty($conf->file->strict_mode)) {
4067  $res=@include $tpl;
4068  } else {
4069  $res=include $tpl; // for debug
4070  }
4071  if ($res) break;
4072  }
4073  }
4074 
4075  // Ligne en mode update
4076  if ($this->statut == 0 && $action == 'editline' && $selected == $line->id)
4077  {
4078  $label = (! empty($line->label) ? $line->label : (($line->fk_product > 0) ? $line->product_label : ''));
4079  $placeholder=' placeholder="'.$langs->trans("Label").'"';
4080 
4081  $line->pu_ttc = price2num($line->subprice * (1 + ($line->tva_tx/100)), 'MU');
4082 
4083  // Output template part (modules that overwrite templates must declare this into descriptor)
4084  // Use global variables + $dateSelector + $seller and $buyer
4085  $dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
4086  foreach($dirtpls as $reldir)
4087  {
4088  $tpl = dol_buildpath($reldir.'/objectline_edit.tpl.php');
4089  if (empty($conf->file->strict_mode)) {
4090  $res=@include $tpl;
4091  } else {
4092  $res=include $tpl; // for debug
4093  }
4094  if ($res) break;
4095  }
4096  }
4097  }
4098 
4099 
4100  /* This is to show array of line of details of source object */
4101 
4102 
4112  function printOriginLinesList($restrictlist='')
4113  {
4114  global $langs, $hookmanager, $conf;
4115 
4116  print '<tr class="liste_titre">';
4117  print '<td>'.$langs->trans('Ref').'</td>';
4118  print '<td>'.$langs->trans('Description').'</td>';
4119  print '<td align="right">'.$langs->trans('VATRate').'</td>';
4120  print '<td align="right">'.$langs->trans('PriceUHT').'</td>';
4121  if (!empty($conf->multicurrency->enabled)) print '<td align="right">'.$langs->trans('PriceUHTCurrency').'</td>';
4122  print '<td align="right">'.$langs->trans('Qty').'</td>';
4123  if($conf->global->PRODUCT_USE_UNITS)
4124  {
4125  print '<td align="left">'.$langs->trans('Unit').'</td>';
4126  }
4127  print '<td align="right">'.$langs->trans('ReductionShort').'</td></tr>';
4128 
4129  $var = true;
4130  $i = 0;
4131 
4132  if (! empty($this->lines))
4133  {
4134  foreach ($this->lines as $line)
4135  {
4136  if (is_object($hookmanager) && (($line->product_type == 9 && ! empty($line->special_code)) || ! empty($line->fk_parent_line)))
4137  {
4138  if (empty($line->fk_parent_line))
4139  {
4140  $parameters=array('line'=>$line,'var'=>$var,'i'=>$i);
4141  $action='';
4142  $hookmanager->executeHooks('printOriginObjectLine',$parameters,$this,$action); // Note that $action and $object may have been modified by some hooks
4143  }
4144  }
4145  else
4146  {
4147  $this->printOriginLine($line, $var, $restrictlist);
4148  }
4149 
4150  $i++;
4151  }
4152  }
4153  }
4154 
4166  function printOriginLine($line, $var, $restrictlist='')
4167  {
4168  global $langs, $conf;
4169 
4170  //var_dump($line);
4171  if (!empty($line->date_start))
4172  {
4173  $date_start=$line->date_start;
4174  }
4175  else
4176  {
4177  $date_start=$line->date_debut_prevue;
4178  if ($line->date_debut_reel) $date_start=$line->date_debut_reel;
4179  }
4180  if (!empty($line->date_end))
4181  {
4182  $date_end=$line->date_end;
4183  }
4184  else
4185  {
4186  $date_end=$line->date_fin_prevue;
4187  if ($line->date_fin_reel) $date_end=$line->date_fin_reel;
4188  }
4189 
4190  $this->tpl['label'] = '';
4191  if (! empty($line->fk_parent_line)) $this->tpl['label'].= img_picto('', 'rightarrow');
4192 
4193  if (($line->info_bits & 2) == 2) // TODO Not sure this is used for source object
4194  {
4195  $discount=new DiscountAbsolute($this->db);
4196  $discount->fk_soc = $this->socid;
4197  $this->tpl['label'].= $discount->getNomUrl(0,'discount');
4198  }
4199  else if (! empty($line->fk_product))
4200  {
4201  $productstatic = new Product($this->db);
4202  $productstatic->id = $line->fk_product;
4203  $productstatic->ref = $line->ref;
4204  $productstatic->type = $line->fk_product_type;
4205  $this->tpl['label'].= $productstatic->getNomUrl(1);
4206  $this->tpl['label'].= ' - '.(! empty($line->label)?$line->label:$line->product_label);
4207  // Dates
4208  if ($line->product_type == 1 && ($date_start || $date_end))
4209  {
4210  $this->tpl['label'].= get_date_range($date_start,$date_end);
4211  }
4212  }
4213  else
4214  {
4215  $this->tpl['label'].= ($line->product_type == -1 ? '&nbsp;' : ($line->product_type == 1 ? img_object($langs->trans(''),'service') : img_object($langs->trans(''),'product')));
4216  if (!empty($line->desc)) {
4217  $this->tpl['label'].=$line->desc;
4218  }else {
4219  $this->tpl['label'].= ($line->label ? '&nbsp;'.$line->label : '');
4220  }
4221  // Dates
4222  if ($line->product_type == 1 && ($date_start || $date_end))
4223  {
4224  $this->tpl['label'].= get_date_range($date_start,$date_end);
4225  }
4226  }
4227 
4228  if (! empty($line->desc))
4229  {
4230  if ($line->desc == '(CREDIT_NOTE)') // TODO Not sure this is used for source object
4231  {
4232  $discount=new DiscountAbsolute($this->db);
4233  $discount->fetch($line->fk_remise_except);
4234  $this->tpl['description'] = $langs->transnoentities("DiscountFromCreditNote",$discount->getNomUrl(0));
4235  }
4236  elseif ($line->desc == '(DEPOSIT)') // TODO Not sure this is used for source object
4237  {
4238  $discount=new DiscountAbsolute($this->db);
4239  $discount->fetch($line->fk_remise_except);
4240  $this->tpl['description'] = $langs->transnoentities("DiscountFromDeposit",$discount->getNomUrl(0));
4241  }
4242  elseif ($line->desc == '(EXCESS RECEIVED)')
4243  {
4244  $discount=new DiscountAbsolute($this->db);
4245  $discount->fetch($line->fk_remise_except);
4246  $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessReceived",$discount->getNomUrl(0));
4247  }
4248  elseif ($line->desc == '(EXCESS PAID)')
4249  {
4250  $discount=new DiscountAbsolute($this->db);
4251  $discount->fetch($line->fk_remise_except);
4252  $this->tpl['description'] = $langs->transnoentities("DiscountFromExcessPaid",$discount->getNomUrl(0));
4253  }
4254  else
4255  {
4256  $this->tpl['description'] = dol_trunc($line->desc,60);
4257  }
4258  }
4259  else
4260  {
4261  $this->tpl['description'] = '&nbsp;';
4262  }
4263 
4264  // VAT Rate
4265  $this->tpl['vat_rate'] = vatrate($line->tva_tx, true);
4266  $this->tpl['vat_rate'] .= (($line->info_bits & 1) == 1) ? '*' : '';
4267  if (! empty($line->vat_src_code) && ! preg_match('/\(/', $this->tpl['vat_rate'])) $this->tpl['vat_rate'].=' ('.$line->vat_src_code.')';
4268 
4269  $this->tpl['price'] = price($line->subprice);
4270  $this->tpl['multicurrency_price'] = price($line->multicurrency_subprice);
4271  $this->tpl['qty'] = (($line->info_bits & 2) != 2) ? $line->qty : '&nbsp;';
4272  if ($conf->global->PRODUCT_USE_UNITS) $this->tpl['unit'] = $langs->transnoentities($line->getLabelOfUnit('long'));
4273  $this->tpl['remise_percent'] = (($line->info_bits & 2) != 2) ? vatrate($line->remise_percent, true) : '&nbsp;';
4274 
4275  // Is the line strike or not
4276  $this->tpl['strike']=0;
4277  if ($restrictlist == 'services' && $line->product_type != Product::TYPE_SERVICE) $this->tpl['strike']=1;
4278 
4279  // Output template part (modules that overwrite templates must declare this into descriptor)
4280  // Use global variables + $dateSelector + $seller and $buyer
4281  $dirtpls=array_merge($conf->modules_parts['tpl'],array('/core/tpl'));
4282  foreach($dirtpls as $reldir)
4283  {
4284  $tpl = dol_buildpath($reldir.'/originproductline.tpl.php');
4285  if (empty($conf->file->strict_mode)) {
4286  $res=@include $tpl;
4287  } else {
4288  $res=include $tpl; // for debug
4289  }
4290  if ($res) break;
4291  }
4292  }
4293 
4294 
4295  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4306  function add_element_resource($resource_id, $resource_type, $busy=0, $mandatory=0)
4307  {
4308  // phpcs:enable
4309  $this->db->begin();
4310 
4311  $sql = "INSERT INTO ".MAIN_DB_PREFIX."element_resources (";
4312  $sql.= "resource_id";
4313  $sql.= ", resource_type";
4314  $sql.= ", element_id";
4315  $sql.= ", element_type";
4316  $sql.= ", busy";
4317  $sql.= ", mandatory";
4318  $sql.= ") VALUES (";
4319  $sql.= $resource_id;
4320  $sql.= ", '".$this->db->escape($resource_type)."'";
4321  $sql.= ", '".$this->db->escape($this->id)."'";
4322  $sql.= ", '".$this->db->escape($this->element)."'";
4323  $sql.= ", '".$this->db->escape($busy)."'";
4324  $sql.= ", '".$this->db->escape($mandatory)."'";
4325  $sql.= ")";
4326 
4327  dol_syslog(get_class($this)."::add_element_resource", LOG_DEBUG);
4328  if ($this->db->query($sql))
4329  {
4330  $this->db->commit();
4331  return 1;
4332  }
4333  else
4334  {
4335  $this->error=$this->db->lasterror();
4336  $this->db->rollback();
4337  return 0;
4338  }
4339  }
4340 
4341  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4350  function delete_resource($rowid, $element, $notrigger=0)
4351  {
4352  // phpcs:enable
4353  global $user;
4354 
4355  $this->db->begin();
4356 
4357  $sql = "DELETE FROM ".MAIN_DB_PREFIX."element_resources";
4358  $sql.= " WHERE rowid=".$rowid;
4359 
4360  dol_syslog(get_class($this)."::delete_resource", LOG_DEBUG);
4361 
4362  $resql=$this->db->query($sql);
4363  if (! $resql)
4364  {
4365  $this->error=$this->db->lasterror();
4366  $this->db->rollback();
4367  return -1;
4368  }
4369  else
4370  {
4371  if (! $notrigger)
4372  {
4373  $result=$this->call_trigger(strtoupper($element).'_DELETE_RESOURCE', $user);
4374  if ($result < 0) { $this->db->rollback(); return -1; }
4375  }
4376  $this->db->commit();
4377  return 1;
4378  }
4379  }
4380 
4381 
4387  function __clone()
4388  {
4389  // Force a copy of this->lines, otherwise it will point to same object.
4390  if (isset($this->lines) && is_array($this->lines))
4391  {
4392  $nboflines=count($this->lines);
4393  for($i=0; $i < $nboflines; $i++)
4394  {
4395  $this->lines[$i] = clone $this->lines[$i];
4396  }
4397  }
4398  }
4399 
4413  protected function commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
4414  {
4415  global $conf, $langs, $user;
4416 
4417  $srctemplatepath='';
4418 
4419  // Increase limit for PDF build
4420  $err=error_reporting();
4421  error_reporting(0);
4422  @set_time_limit(120);
4423  error_reporting($err);
4424 
4425  // If selected model is a filename template (then $modele="modelname" or "modelname:filename")
4426  $tmp=explode(':',$modele,2);
4427  if (! empty($tmp[1]))
4428  {
4429  $modele=$tmp[0];
4430  $srctemplatepath=$tmp[1];
4431  }
4432 
4433  // Search template files
4434  $file=''; $classname=''; $filefound=0;
4435  $dirmodels=array('/');
4436  if (is_array($conf->modules_parts['models'])) $dirmodels=array_merge($dirmodels,$conf->modules_parts['models']);
4437  foreach($dirmodels as $reldir)
4438  {
4439  foreach(array('doc','pdf') as $prefix)
4440  {
4441  if (in_array(get_class($this), array('Adherent'))) $file = $prefix."_".$modele.".class.php"; // Member module use prefix_module.class.php
4442  else $file = $prefix."_".$modele.".modules.php";
4443 
4444  // On verifie l'emplacement du modele
4445  $file=dol_buildpath($reldir.$modelspath.$file,0);
4446  if (file_exists($file))
4447  {
4448  $filefound=1;
4449  $classname=$prefix.'_'.$modele;
4450  break;
4451  }
4452  }
4453  if ($filefound) break;
4454  }
4455 
4456  // If generator was found
4457  if ($filefound)
4458  {
4459  global $db; // Required to solve a conception default in commonstickergenerator.class.php making an include of code using $db
4460 
4461  require_once $file;
4462 
4463  $obj = new $classname($this->db);
4464 
4465  // If generator is ODT, we must have srctemplatepath defined, if not we set it.
4466  if ($obj->type == 'odt' && empty($srctemplatepath))
4467  {
4468  $varfortemplatedir=$obj->scandir;
4469  if ($varfortemplatedir && ! empty($conf->global->$varfortemplatedir))
4470  {
4471  $dirtoscan=$conf->global->$varfortemplatedir;
4472 
4473  $listoffiles=array();
4474 
4475  // Now we add first model found in directories scanned
4476  $listofdir=explode(',',$dirtoscan);
4477  foreach($listofdir as $key => $tmpdir)
4478  {
4479  $tmpdir=trim($tmpdir);
4480  $tmpdir=preg_replace('/DOL_DATA_ROOT/',DOL_DATA_ROOT,$tmpdir);
4481  if (! $tmpdir) { unset($listofdir[$key]); continue; }
4482  if (is_dir($tmpdir))
4483  {
4484  $tmpfiles=dol_dir_list($tmpdir,'files',0,'\.od(s|t)$','','name',SORT_ASC,0);
4485  if (count($tmpfiles)) $listoffiles=array_merge($listoffiles,$tmpfiles);
4486  }
4487  }
4488 
4489  if (count($listoffiles))
4490  {
4491  foreach($listoffiles as $record)
4492  {
4493  $srctemplatepath=$record['fullname'];
4494  break;
4495  }
4496  }
4497  }
4498 
4499  if (empty($srctemplatepath))
4500  {
4501  $this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotDefined';
4502  return -1;
4503  }
4504  }
4505 
4506  if ($obj->type == 'odt' && ! empty($srctemplatepath))
4507  {
4508  if (! dol_is_file($srctemplatepath))
4509  {
4510  $this->error='ErrorGenerationAskedForOdtTemplateWithSrcFileNotFound';
4511  return -1;
4512  }
4513  }
4514 
4515  // We save charset_output to restore it because write_file can change it if needed for
4516  // output format that does not support UTF8.
4517  $sav_charset_output=$outputlangs->charset_output;
4518 
4519  if (in_array(get_class($this), array('Adherent')))
4520  {
4521  $arrayofrecords = array(); // The write_file of templates of adherent class need this var
4522  $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, 'member', 1, $moreparams);
4523  }
4524  else
4525  {
4526  $resultwritefile = $obj->write_file($this, $outputlangs, $srctemplatepath, $hidedetails, $hidedesc, $hideref, $moreparams);
4527  }
4528  // After call of write_file $obj->result['fullpath'] is set with generated file. It will be used to update the ECM database index.
4529 
4530  if ($resultwritefile > 0)
4531  {
4532  $outputlangs->charset_output=$sav_charset_output;
4533 
4534  // We delete old preview
4535  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
4536  dol_delete_preview($this);
4537 
4538  // Index file in database
4539  if (! empty($obj->result['fullpath']))
4540  {
4541  $destfull = $obj->result['fullpath'];
4542  $upload_dir = dirname($destfull);
4543  $destfile = basename($destfull);
4544  $rel_dir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $upload_dir);
4545 
4546  if (! preg_match('/[\\/]temp[\\/]|[\\/]thumbs|\.meta$/', $rel_dir)) // If not a tmp dir
4547  {
4548  $filename = basename($destfile);
4549  $rel_dir = preg_replace('/[\\/]$/', '', $rel_dir);
4550  $rel_dir = preg_replace('/^[\\/]/', '', $rel_dir);
4551 
4552  include_once DOL_DOCUMENT_ROOT.'/ecm/class/ecmfiles.class.php';
4553  $ecmfile=new EcmFiles($this->db);
4554  $result = $ecmfile->fetch(0, '', ($rel_dir?$rel_dir.'/':'').$filename);
4555 
4556  // Set the public "share" key
4557  $setsharekey = false;
4558  if ($this->element == 'propal')
4559  {
4560  $useonlinesignature = $conf->global->MAIN_FEATURES_LEVEL; // Replace this with 1 when feature to make online signature is ok
4561  if ($useonlinesignature) $setsharekey=true;
4562  if (! empty($conf->global->PROPOSAL_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true;
4563  }
4564  if ($this->element == 'commande' && ! empty($conf->global->ORDER_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true;
4565  if ($this->element == 'facture' && ! empty($conf->global->INVOICE_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true;
4566  if ($this->element == 'bank_account' && ! empty($conf->global->BANK_ACCOUNT_ALLOW_EXTERNAL_DOWNLOAD)) $setsharekey=true;
4567 
4568  if ($setsharekey)
4569  {
4570  if (empty($ecmfile->share)) // Because object not found or share not set yet
4571  {
4572  require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
4573  $ecmfile->share = getRandomPassword(true);
4574  }
4575  }
4576 
4577  if ($result > 0)
4578  {
4579  $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
4580  $ecmfile->fullpath_orig = '';
4581  $ecmfile->gen_or_uploaded = 'generated';
4582  $ecmfile->description = ''; // indexed content
4583  $ecmfile->keyword = ''; // keyword content
4584  $result = $ecmfile->update($user);
4585  if ($result < 0)
4586  {
4587  setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4588  }
4589  }
4590  else
4591  {
4592  $ecmfile->entity = $conf->entity;
4593  $ecmfile->filepath = $rel_dir;
4594  $ecmfile->filename = $filename;
4595  $ecmfile->label = md5_file(dol_osencode($destfull)); // hash of file content
4596  $ecmfile->fullpath_orig = '';
4597  $ecmfile->gen_or_uploaded = 'generated';
4598  $ecmfile->description = ''; // indexed content
4599  $ecmfile->keyword = ''; // keyword content
4600  $ecmfile->src_object_type = $this->table_element;
4601  $ecmfile->src_object_id = $this->id;
4602 
4603  $result = $ecmfile->create($user);
4604  if ($result < 0)
4605  {
4606  setEventMessages($ecmfile->error, $ecmfile->errors, 'warnings');
4607  }
4608  }
4609 
4610  /*$this->result['fullname']=$destfull;
4611  $this->result['filepath']=$ecmfile->filepath;
4612  $this->result['filename']=$ecmfile->filename;*/
4613  //var_dump($obj->update_main_doc_field);exit;
4614 
4615  // Update the last_main_doc field into main object (if documenent generator has property ->update_main_doc_field set)
4616  $update_main_doc_field=0;
4617  if (! empty($obj->update_main_doc_field)) $update_main_doc_field=1;
4618  if ($update_main_doc_field && ! empty($this->table_element))
4619  {
4620  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element." SET last_main_doc = '".$this->db->escape($ecmfile->filepath.'/'.$ecmfile->filename)."'";
4621  $sql.= ' WHERE rowid = '.$this->id;
4622 
4623  $resql = $this->db->query($sql);
4624  if (! $resql) dol_print_error($this->db);
4625  else
4626  {
4627  $this->last_main_doc = $ecmfile->filepath.'/'.$ecmfile->filename;
4628  }
4629  }
4630  }
4631  }
4632  else
4633  {
4634  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);
4635  }
4636 
4637  // Success in building document. We build meta file.
4638  dol_meta_create($this);
4639 
4640  return 1;
4641  }
4642  else
4643  {
4644  $outputlangs->charset_output=$sav_charset_output;
4645  dol_print_error($this->db, "Error generating document for ".__CLASS__.". Error: ".$obj->error, $obj->errors);
4646  return -1;
4647  }
4648  }
4649  else
4650  {
4651  $this->error=$langs->trans("Error")." ".$langs->trans("ErrorFileDoesNotExists",$file);
4652  dol_print_error('',$this->error);
4653  return -1;
4654  }
4655  }
4656 
4664  function addThumbs($file)
4665  {
4666  global $maxwidthsmall, $maxheightsmall, $maxwidthmini, $maxheightmini, $quality;
4667 
4668  require_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php'; // This define also $maxwidthsmall, $quality, ...
4669 
4670  $file_osencoded=dol_osencode($file);
4671  if (file_exists($file_osencoded))
4672  {
4673  // Create small thumbs for company (Ratio is near 16/9)
4674  // Used on logon for example
4675  vignette($file_osencoded, $maxwidthsmall, $maxheightsmall, '_small', $quality);
4676 
4677  // Create mini thumbs for company (Ratio is near 16/9)
4678  // Used on menu or for setup page for example
4679  vignette($file_osencoded, $maxwidthmini, $maxheightmini, '_mini', $quality);
4680  }
4681  }
4682 
4683 
4684  /* Functions common to commonobject and commonobjectline */
4685 
4686  /* For default values */
4687 
4700  function getDefaultCreateValueFor($fieldname, $alternatevalue=null)
4701  {
4702  global $conf, $_POST;
4703 
4704  // If param here has been posted, we use this value first.
4705  if (isset($_POST[$fieldname])) return GETPOST($fieldname, 2);
4706 
4707  if (isset($alternatevalue)) return $alternatevalue;
4708 
4709  $newelement=$this->element;
4710  if ($newelement == 'facture') $newelement='invoice';
4711  if ($newelement == 'commande') $newelement='order';
4712  if (empty($newelement))
4713  {
4714  dol_syslog("Ask a default value using common method getDefaultCreateValueForField on an object with no property ->element defined. Return empty string.", LOG_WARNING);
4715  return '';
4716  }
4717 
4718  $keyforfieldname=strtoupper($newelement.'_DEFAULT_'.$fieldname);
4719  //var_dump($keyforfieldname);
4720  if (isset($conf->global->$keyforfieldname)) return $conf->global->$keyforfieldname;
4721 
4722  // TODO Ad here a scan into table llx_overwrite_default with a filter on $this->element and $fieldname
4723  }
4724 
4725 
4726  /* For triggers */
4727 
4728 
4729  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4740  function call_trigger($trigger_name, $user)
4741  {
4742  // phpcs:enable
4743  global $langs,$conf;
4744 
4745  include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php';
4746  $interface=new Interfaces($this->db);
4747  $result=$interface->run_triggers($trigger_name,$this,$user,$langs,$conf);
4748 
4749  if ($result < 0)
4750  {
4751  if (!empty($this->errors))
4752  {
4753  $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.
4754  }
4755  else
4756  {
4757  $this->errors=$interface->errors;
4758  }
4759  }
4760  return $result;
4761  }
4762 
4763 
4764  /* Functions for extrafields */
4765 
4766 
4767  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
4776  function fetch_optionals($rowid=null, $optionsArray=null)
4777  {
4778  // phpcs:enable
4779  if (empty($rowid)) $rowid=$this->id;
4780 
4781  // To avoid SQL errors. Probably not the better solution though
4782  if (!$this->table_element) {
4783  return 0;
4784  }
4785 
4786  $this->array_options=array();
4787 
4788  if (! is_array($optionsArray))
4789  {
4790  // If $extrafields is not a known object, we initialize it. Best practice is to have $extrafields defined into card.php or list.php page.
4791  // TODO Use of existing $extrafield is not yet ready (must mutualize code that use extrafields in form first)
4792  // global $extrafields;
4793  //if (! is_object($extrafields))
4794  //{
4795  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4796  $extrafields = new ExtraFields($this->db);
4797  //}
4798 
4799  // Load array of extrafields for elementype = $this->table_element
4800  if (empty($extrafields->attributes[$this->table_element]['loaded']))
4801  {
4802  $extrafields->fetch_name_optionals_label($this->table_element);
4803  }
4804  $optionsArray = (! empty($extrafields->attributes[$this->table_element]['label'])?$extrafields->attributes[$this->table_element]['label']:null);
4805  }
4806  else
4807  {
4808  global $extrafields;
4809  dol_syslog("Warning: fetch_optionals was called with param optionsArray defined when you should pass null now", LOG_WARNING);
4810  }
4811 
4812  $table_element = $this->table_element;
4813  if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4814 
4815  // Request to get complementary values
4816  if (is_array($optionsArray) && count($optionsArray) > 0)
4817  {
4818  $sql = "SELECT rowid";
4819  foreach ($optionsArray as $name => $label)
4820  {
4821  if (empty($extrafields->attributes[$this->table_element]['type'][$name]) || $extrafields->attributes[$this->table_element]['type'][$name] != 'separate')
4822  {
4823  $sql.= ", ".$name;
4824  }
4825  }
4826  $sql.= " FROM ".MAIN_DB_PREFIX.$table_element."_extrafields";
4827  $sql.= " WHERE fk_object = ".$rowid;
4828 
4829  //dol_syslog(get_class($this)."::fetch_optionals get extrafields data for ".$this->table_element, LOG_DEBUG); // Too verbose
4830  $resql=$this->db->query($sql);
4831  if ($resql)
4832  {
4833  $this->array_options = array();
4834  $numrows=$this->db->num_rows($resql);
4835  if ($numrows)
4836  {
4837  $tab = $this->db->fetch_array($resql);
4838 
4839  foreach ($tab as $key => $value)
4840  {
4841  // 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)
4842  if ($key != 'rowid' && $key != 'tms' && $key != 'fk_member' && ! is_int($key))
4843  {
4844  // we can add this attribute to object
4845  if (! empty($extrafields) && in_array($extrafields->attributes[$this->table_element]['type'][$key], array('date','datetime')))
4846  {
4847  //var_dump($extrafields->attributes[$this->table_element]['type'][$key]);
4848  $this->array_options["options_".$key]=$this->db->jdate($value);
4849  }
4850  else
4851  {
4852  $this->array_options["options_".$key]=$value;
4853  }
4854 
4855  //var_dump('key '.$key.' '.$value.' type='.$extrafields->attributes[$this->table_element]['type'][$key].' '.$this->array_options["options_".$key]);
4856  }
4857  }
4858  }
4859 
4860  $this->db->free($resql);
4861 
4862  if ($numrows) return $numrows;
4863  else return 0;
4864  }
4865  else
4866  {
4867  dol_print_error($this->db);
4868  return -1;
4869  }
4870  }
4871  return 0;
4872  }
4873 
4879  function deleteExtraFields()
4880  {
4881  $this->db->begin();
4882 
4883  $table_element = $this->table_element;
4884  if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
4885 
4886  $sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
4887  dol_syslog(get_class($this)."::deleteExtraFields delete", LOG_DEBUG);
4888  $resql=$this->db->query($sql_del);
4889  if (! $resql)
4890  {
4891  $this->error=$this->db->lasterror();
4892  $this->db->rollback();
4893  return -1;
4894  }
4895  else
4896  {
4897  $this->db->commit();
4898  return 1;
4899  }
4900  }
4901 
4912  function insertExtraFields($trigger='', $userused=null)
4913  {
4914  global $conf,$langs,$user;
4915 
4916  if (empty($userused)) $userused=$user;
4917 
4918  $error=0;
4919 
4920  if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0; // For avoid conflicts if trigger used
4921 
4922  if (! empty($this->array_options))
4923  {
4924  // Check parameters
4925  $langs->load('admin');
4926  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
4927  $extrafields = new ExtraFields($this->db);
4928  $target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
4929 
4930  //Eliminate copied source object extra_fields that do not exist in target object
4931  $new_array_options=array();
4932  foreach ($this->array_options as $key => $value) {
4933  if (in_array(substr($key,8), array_keys($target_extrafields))) // We remove the 'options_' from $key for test
4934  $new_array_options[$key] = $value;
4935  elseif (in_array($key, array_keys($target_extrafields))) // We test on $key that does not contains the 'options_' prefix
4936  $new_array_options['options_'.$key] = $value;
4937  }
4938 
4939  foreach($new_array_options as $key => $value)
4940  {
4941  $attributeKey = substr($key,8); // Remove 'options_' prefix
4942  $attributeType = $extrafields->attributes[$this->table_element]['type'][$attributeKey];
4943  $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$attributeKey];
4944  $attributeParam = $extrafields->attributes[$this->table_element]['param'][$attributeKey];
4945  $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$attributeKey];
4946 
4947  if ($attributeRequired)
4948  {
4949  $mandatorypb=false;
4950  if ($attributeType == 'link' && $this->array_options[$key] == '-1') $mandatorypb=true;
4951  if ($this->array_options[$key] === '') $mandatorypb=true;
4952  if ($mandatorypb)
4953  {
4954  dol_syslog("Mandatory extra field ".$key." is empty");
4955  $this->errors[]=$langs->trans('ErrorFieldRequired', $attributeLabel);
4956  return -1;
4957  }
4958  }
4959 
4960  //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
4961  //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
4962 
4963  switch ($attributeType)
4964  {
4965  case 'int':
4966  if (!is_numeric($value) && $value!='')
4967  {
4968  $this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
4969  return -1;
4970  }
4971  elseif ($value=='')
4972  {
4973  $new_array_options[$key] = null;
4974  }
4975  break;
4976  case 'double':
4977  $value = price2num($value);
4978  if (!is_numeric($value) && $value!='')
4979  {
4980  dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
4981  $this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
4982  return -1;
4983  }
4984  elseif ($value=='')
4985  {
4986  $new_array_options[$key] = null;
4987  }
4988  //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
4989  $new_array_options[$key] = $value;
4990  break;
4991  /*case 'select': // Not required, we chosed value='0' for undefined values
4992  if ($value=='-1')
4993  {
4994  $this->array_options[$key] = null;
4995  }
4996  break;*/
4997  case 'password':
4998  $algo='';
4999  if ($this->array_options[$key] != '' && is_array($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']))
5000  {
5001  // If there is an encryption choice, we use it to crypt data before insert
5002  $tmparrays = array_keys($extrafields->attributes[$this->table_element]['param'][$attributeKey]['options']);
5003  $algo=reset($tmparrays);
5004  if ($algo != '')
5005  {
5006  //global $action; // $action may be 'create', 'update', 'update_extras'...
5007  //var_dump($action);
5008  //var_dump($this->oldcopy);exit;
5009  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
5010  {
5011  //var_dump($this->oldcopy->array_options[$key]); var_dump($this->array_options[$key]);
5012  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.
5013  {
5014  $new_array_options[$key] = $this->array_options[$key]; // Value is kept
5015  }
5016  else
5017  {
5018  // var_dump($algo);
5019  $newvalue = dol_hash($this->array_options[$key], $algo);
5020  $new_array_options[$key] = $newvalue;
5021  }
5022  }
5023  else
5024  {
5025  $new_array_options[$key] = $this->array_options[$key]; // Value is kept
5026  }
5027  }
5028  }
5029  else // Common usage
5030  {
5031  $new_array_options[$key] = $this->array_options[$key];
5032  }
5033  break;
5034  case 'price':
5035  $new_array_options[$key] = price2num($this->array_options[$key]);
5036  break;
5037  case 'date':
5038  case 'datetime':
5039  // If data is a string instead of a timestamp, we convert it
5040  if (! is_int($this->array_options[$key])) {
5041  $this->array_options[$key] = strtotime($this->array_options[$key]);
5042  }
5043  $new_array_options[$key] = $this->db->idate($this->array_options[$key]);
5044  break;
5045  case 'link':
5046  $param_list=array_keys($attributeParam['options']);
5047  // 0 : ObjectName
5048  // 1 : classPath
5049  $InfoFieldList = explode(":", $param_list[0]);
5050  dol_include_once($InfoFieldList[1]);
5051  if ($InfoFieldList[0] && class_exists($InfoFieldList[0]))
5052  {
5053  if ($value == '-1') // -1 is key for no defined in combo list of objects
5054  {
5055  $new_array_options[$key]='';
5056  }
5057  elseif ($value)
5058  {
5059  $object = new $InfoFieldList[0]($this->db);
5060  if (is_numeric($value)) $res=$object->fetch($value);
5061  else $res=$object->fetch('',$value);
5062 
5063  if ($res > 0) $new_array_options[$key]=$object->id;
5064  else
5065  {
5066  $this->error="Id/Ref '".$value."' for object '".$object->element."' not found";
5067  $this->db->rollback();
5068  return -1;
5069  }
5070  }
5071  }
5072  else
5073  {
5074  dol_syslog('Error bad setup of extrafield', LOG_WARNING);
5075  }
5076  break;
5077  }
5078  }
5079 
5080  $this->db->begin();
5081 
5082  $table_element = $this->table_element;
5083  if ($table_element == 'categorie') $table_element = 'categories'; // For compatibility
5084 
5085  $sql_del = "DELETE FROM ".MAIN_DB_PREFIX.$table_element."_extrafields WHERE fk_object = ".$this->id;
5086  dol_syslog(get_class($this)."::insertExtraFields delete", LOG_DEBUG);
5087  $this->db->query($sql_del);
5088 
5089  $sql = "INSERT INTO ".MAIN_DB_PREFIX.$table_element."_extrafields (fk_object";
5090  foreach($new_array_options as $key => $value)
5091  {
5092  $attributeKey = substr($key,8); // Remove 'options_' prefix
5093  // Add field of attribut
5094  if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator
5095  $sql.=",".$attributeKey;
5096  }
5097  $sql .= ") VALUES (".$this->id;
5098 
5099  foreach($new_array_options as $key => $value)
5100  {
5101  $attributeKey = substr($key,8); // Remove 'options_' prefix
5102  // Add field of attribute
5103  if ($extrafields->attributes[$this->table_element]['type'][$attributeKey] != 'separate') // Only for other type than separator)
5104  {
5105  if ($new_array_options[$key] != '')
5106  {
5107  $sql.=",'".$this->db->escape($new_array_options[$key])."'";
5108  }
5109  else
5110  {
5111  $sql.=",null";
5112  }
5113  }
5114  }
5115  $sql.=")";
5116 
5117  dol_syslog(get_class($this)."::insertExtraFields insert", LOG_DEBUG);
5118  $resql = $this->db->query($sql);
5119  if (! $resql)
5120  {
5121  $this->error=$this->db->lasterror();
5122  $error++;
5123  }
5124 
5125  if (! $error && $trigger)
5126  {
5127  // Call trigger
5128  $this->context=array('extrafieldaddupdate'=>1);
5129  $result=$this->call_trigger($trigger, $userused);
5130  if ($result < 0) $error++;
5131  // End call trigger
5132  }
5133 
5134  if ($error)
5135  {
5136  $this->db->rollback();
5137  return -1;
5138  }
5139  else
5140  {
5141  $this->db->commit();
5142  return 1;
5143  }
5144  }
5145  else return 0;
5146  }
5147 
5158  function updateExtraField($key, $trigger=null, $userused=null)
5159  {
5160  global $conf,$langs,$user;
5161 
5162  if (empty($userused)) $userused=$user;
5163 
5164  $error=0;
5165 
5166  if (! empty($conf->global->MAIN_EXTRAFIELDS_DISABLED)) return 0; // For avoid conflicts if trigger used
5167 
5168  if (! empty($this->array_options) && isset($this->array_options["options_".$key]))
5169  {
5170  // Check parameters
5171  $langs->load('admin');
5172  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
5173  $extrafields = new ExtraFields($this->db);
5174  $target_extrafields=$extrafields->fetch_name_optionals_label($this->table_element);
5175 
5176  $value=$this->array_options["options_".$key];
5177 
5178  $attributeType = $extrafields->attributes[$this->table_element]['type'][$key];
5179  $attributeLabel = $extrafields->attributes[$this->table_element]['label'][$key];
5180  $attributeParam = $extrafields->attributes[$this->table_element]['param'][$key];
5181  $attributeRequired = $extrafields->attributes[$this->table_element]['required'][$key];
5182 
5183  //dol_syslog("attributeLabel=".$attributeLabel, LOG_DEBUG);
5184  //dol_syslog("attributeType=".$attributeType, LOG_DEBUG);
5185 
5186  switch ($attributeType)
5187  {
5188  case 'int':
5189  if (!is_numeric($value) && $value!='')
5190  {
5191  $this->errors[]=$langs->trans("ExtraFieldHasWrongValue",$attributeLabel);
5192  return -1;
5193  }
5194  elseif ($value=='')
5195  {
5196  $this->array_options["options_".$key] = null;
5197  }
5198  break;
5199  case 'double':
5200  $value = price2num($value);
5201  if (!is_numeric($value) && $value!='')
5202  {
5203  dol_syslog($langs->trans("ExtraFieldHasWrongValue")." sur ".$attributeLabel."(".$value."is not '".$attributeType."')", LOG_DEBUG);
5204  $this->errors[]=$langs->trans("ExtraFieldHasWrongValue", $attributeLabel);
5205  return -1;
5206  }
5207  elseif ($value=='')
5208  {
5209  $this->array_options["options_".$key] = null;
5210  }
5211  //dol_syslog("double value"." sur ".$attributeLabel."(".$value." is '".$attributeType."')", LOG_DEBUG);
5212  $this->array_options["options_".$key] = $value;
5213  break;
5214  /*case 'select': // Not required, we chosed value='0' for undefined values
5215  if ($value=='-1')
5216  {
5217  $this->array_options[$key] = null;
5218  }
5219  break;*/
5220  case 'price':
5221  $this->array_options["options_".$key] = price2num($this->array_options["options_".$key]);
5222  break;
5223  case 'date':
5224  $this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
5225  break;
5226  case 'datetime':
5227  $this->array_options["options_".$key]=$this->db->idate($this->array_options["options_".$key]);
5228  break;
5229  case 'link':
5230  $param_list=array_keys($attributeParam['options']);
5231  // 0 : ObjectName
5232  // 1 : classPath
5233  $InfoFieldList = explode(":", $param_list[0]);
5234  dol_include_once($InfoFieldList[1]);
5235  if ($value)
5236  {
5237  $object = new $InfoFieldList[0]($this->db);
5238  $object->fetch(0,$value);
5239  $this->array_options["options_".$key]=$object->id;
5240  }
5241  break;
5242  }
5243 
5244  $this->db->begin();
5245  $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element."_extrafields SET ".$key."='".$this->db->escape($this->array_options["options_".$key])."'";
5246  $sql .= " WHERE fk_object = ".$this->id;
5247  $resql = $this->db->query($sql);
5248  if (! $resql)
5249  {
5250  $error++;
5251  $this->error=$this->db->lasterror();
5252  }
5253 
5254  if (! $error && $trigger)
5255  {
5256  // Call trigger
5257  $this->context=array('extrafieldupdate'=>1);
5258  $result=$this->call_trigger($trigger, $userused);
5259  if ($result < 0) $error++;
5260  // End call trigger
5261  }
5262 
5263  if ($error)
5264  {
5265  dol_syslog(get_class($this) . "::".__METHOD__ . $this->error, LOG_ERR);
5266  $this->db->rollback();
5267  return -1;
5268  }
5269  else
5270  {
5271  $this->db->commit();
5272  return 1;
5273  }
5274  }
5275  else return 0;
5276  }
5277 
5278 
5292  function showInputField($val, $key, $value, $moreparam='', $keysuffix='', $keyprefix='', $morecss=0)
5293  {
5294  global $conf,$langs,$form;
5295 
5296  if (! is_object($form))
5297  {
5298  require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5299  $form=new Form($this->db);
5300  }
5301 
5302  $val=$this->fields[$key];
5303 
5304  $out='';
5305  $type='';
5306  $param = array();
5307  $param['options']=array();
5308  $size =$this->fields[$key]['size'];
5309  // Because we work on extrafields
5310  if(preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)){
5311  $param['options']=array($reg[1].':'.$reg[2]=>'N');
5312  $type ='link';
5313  } elseif(preg_match('/^link:(.*):(.*)/i', $val['type'], $reg)) {
5314  $param['options']=array($reg[1].':'.$reg[2]=>'N');
5315  $type ='link';
5316  } elseif(preg_match('/^sellist:(.*):(.*):(.*):(.*)/i', $val['type'], $reg)) {
5317  $param['options']=array($reg[1].':'.$reg[2].':'.$reg[3].':'.$reg[4]=>'N');
5318  $type ='sellist';
5319  } elseif(preg_match('/varchar\((\d+)\)/', $val['type'],$reg)) {
5320  $param['options']=array();
5321  $type ='varchar';
5322  $size=$reg[1];
5323  } elseif(preg_match('/varchar/', $val['type'])) {
5324  $param['options']=array();
5325  $type ='varchar';
5326  } elseif(is_array($this->fields[$key]['arrayofkeyval'])) {
5327  $param['options']=$this->fields[$key]['arrayofkeyval'];
5328  $type ='select';
5329  } else {
5330  $param['options']=array();
5331  $type =$this->fields[$key]['type'];
5332  }
5333 
5334  $label=$this->fields[$key]['label'];
5335  //$elementtype=$this->fields[$key]['elementtype']; // Seems not used
5336  $default=$this->fields[$key]['default'];
5337  $computed=$this->fields[$key]['computed'];
5338  $unique=$this->fields[$key]['unique'];
5339  $required=$this->fields[$key]['required'];
5340 
5341  $langfile=$this->fields[$key]['langfile'];
5342  $list=$this->fields[$key]['list'];
5343  $hidden=abs($this->fields[$key]['visible'])!=1?1:0;
5344 
5345  $objectid = $this->id;
5346 
5347 
5348  if ($computed)
5349  {
5350  if (! preg_match('/^search_/', $keyprefix)) return '<span class="opacitymedium">'.$langs->trans("AutomaticallyCalculated").'</span>';
5351  else return '';
5352  }
5353 
5354 
5355  // Use in priority showsize from parameters, then $val['css'] then autodefine
5356  if (empty($morecss) && ! empty($val['css']))
5357  {
5358  $showsize = $val['css'];
5359  }
5360  if (empty($morecss))
5361  {
5362  if ($type == 'date')
5363  {
5364  $morecss = 'minwidth100imp';
5365  }
5366  elseif ($type == 'datetime')
5367  {
5368  $morecss = 'minwidth200imp';
5369  }
5370  elseif (in_array($type,array('int','integer','price')) || preg_match('/^double(\([0-9],[0-9]\)){0,1}/',$type))
5371  {
5372  $morecss = 'maxwidth75';
5373  }elseif ($type == 'url')
5374  {
5375  $morecss='minwidth400';
5376  }
5377  elseif ($type == 'boolean')
5378  {
5379  $morecss='';
5380  }
5381  else
5382  {
5383  if (round($size) < 12)
5384  {
5385  $morecss = 'minwidth100';
5386  }
5387  else if (round($size) <= 48)
5388  {
5389  $morecss = 'minwidth200';
5390  }
5391  else
5392  {
5393  $morecss = 'minwidth400';
5394  }
5395  }
5396  }
5397 
5398  if (in_array($type,array('date','datetime')))
5399  {
5400  $tmp=explode(',',$size);
5401  $newsize=$tmp[0];
5402 
5403  $showtime = in_array($type,array('datetime')) ? 1 : 0;
5404 
5405  // Do not show current date when field not required (see selectDate() method)
5406  if (!$required && $value == '') $value = '-1';
5407 
5408  // TODO Must also support $moreparam
5409  $out = $form->selectDate($value, $keyprefix.$key.$keysuffix, $showtime, $showtime, $required, '', 1, (($keyprefix != 'search_' && $keyprefix != 'search_options_') ? 1 : 0), 0, 1);
5410  }
5411  elseif (in_array($type,array('int','integer')))
5412  {
5413  $tmp=explode(',',$size);
5414  $newsize=$tmp[0];
5415  $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$newsize.'" value="'.dol_escape_htmltag($value).'"'.($moreparam?$moreparam:'').'>';
5416  }
5417  elseif (preg_match('/varchar/', $type))
5418  {
5419  $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" maxlength="'.$size.'" value="'.dol_escape_htmltag($value).'"'.($moreparam?$moreparam:'').'>';
5420  }
5421  elseif (in_array($type, array('mail', 'phone', 'url')))
5422  {
5423  $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5424  }
5425  elseif ($type == 'text')
5426  {
5427  if (! preg_match('/search_/', $keyprefix)) // If keyprefix is search_ or search_options_, we must just use a simple text field
5428  {
5429  require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
5430  $doleditor=new DolEditor($keyprefix.$key.$keysuffix,$value,'',200,'dolibarr_notes','In',false,false,false,ROWS_5,'90%');
5431  $out=$doleditor->Create(1);
5432  }
5433  else
5434  {
5435  $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5436  }
5437  }
5438  elseif ($type == 'html')
5439  {
5440  if (! preg_match('/search_/', $keyprefix)) // If keyprefix is search_ or search_options_, we must just use a simple text field
5441  {
5442  require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
5443  $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%');
5444  $out=$doleditor->Create(1);
5445  }
5446  else
5447  {
5448  $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.dol_escape_htmltag($value).'" '.($moreparam?$moreparam:'').'>';
5449  }
5450  }
5451  elseif ($type == 'boolean')
5452  {
5453  $checked='';
5454  if (!empty($value)) {
5455  $checked=' checked value="1" ';
5456  } else {
5457  $checked=' value="1" ';
5458  }
5459  $out='<input type="checkbox" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.$checked.' '.($moreparam?$moreparam:'').'>';
5460  }
5461  elseif ($type == 'price')
5462  {
5463  if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
5464  $value=price($value);
5465  }
5466  $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> '.$langs->getCurrencySymbol($conf->currency);
5467  }
5468  elseif (preg_match('/^double(\([0-9],[0-9]\)){0,1}/',$type))
5469  {
5470  if (!empty($value)) { // $value in memory is a php numeric, we format it into user number format.
5471  $value=price($value);
5472  }
5473  $out='<input type="text" class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'> ';
5474  }
5475  elseif ($type == 'select')
5476  {
5477  $out = '';
5478  if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
5479  {
5480  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
5481  $out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
5482  }
5483 
5484  $out.='<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
5485  if((! isset($this->fields[$key]['default'])) ||($this->fields[$key]['notnull']!=1))$out.='<option value="0">&nbsp;</option>';
5486  foreach ($param['options'] as $key => $val)
5487  {
5488  if ((string) $key == '') continue;
5489  list($val, $parent) = explode('|', $val);
5490  $out.='<option value="'.$key.'"';
5491  $out.= (((string) $value == (string) $key)?' selected':'');
5492  $out.= (!empty($parent)?' parent="'.$parent.'"':'');
5493  $out.='>'.$val.'</option>';
5494  }
5495  $out.='</select>';
5496  }
5497  elseif ($type == 'sellist')
5498  {
5499  $out = '';
5500  if (! empty($conf->use_javascript_ajax) && ! empty($conf->global->MAIN_EXTRAFIELDS_USE_SELECT2))
5501  {
5502  include_once DOL_DOCUMENT_ROOT . '/core/lib/ajax.lib.php';
5503  $out.= ajax_combobox($keyprefix.$key.$keysuffix, array(), 0);
5504  }
5505 
5506  $out.='<select class="flat '.$morecss.' maxwidthonsmartphone" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'').'>';
5507  if (is_array($param['options']))
5508  {
5509  $param_list=array_keys($param['options']);
5510  $InfoFieldList = explode(":", $param_list[0]);
5511  $parentName='';
5512  $parentField='';
5513  // 0 : tableName
5514  // 1 : label field name
5515  // 2 : key fields name (if differ of rowid)
5516  // 3 : key field parent (for dependent lists)
5517  // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5518  $keyList=(empty($InfoFieldList[2])?'rowid':$InfoFieldList[2].' as rowid');
5519 
5520 
5521  if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4]))
5522  {
5523  if (strpos($InfoFieldList[4], 'extra.') !== false)
5524  {
5525  $keyList='main.'.$InfoFieldList[2].' as rowid';
5526  } else {
5527  $keyList=$InfoFieldList[2].' as rowid';
5528  }
5529  }
5530  if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3]))
5531  {
5532  list($parentName, $parentField) = explode('|', $InfoFieldList[3]);
5533  $keyList.= ', '.$parentField;
5534  }
5535 
5536  $fields_label = explode('|',$InfoFieldList[1]);
5537  if (is_array($fields_label))
5538  {
5539  $keyList .=', ';
5540  $keyList .= implode(', ', $fields_label);
5541  }
5542 
5543  $sqlwhere='';
5544  $sql = 'SELECT '.$keyList;
5545  $sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
5546  if (!empty($InfoFieldList[4]))
5547  {
5548  // can use SELECT request
5549  if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5550  $InfoFieldList[4]=str_replace('$SEL$','SELECT',$InfoFieldList[4]);
5551  }
5552 
5553  // current object id can be use into filter
5554  if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5555  $InfoFieldList[4]=str_replace('$ID$',$objectid,$InfoFieldList[4]);
5556  } else {
5557  $InfoFieldList[4]=str_replace('$ID$','0',$InfoFieldList[4]);
5558  }
5559  //We have to join on extrafield table
5560  if (strpos($InfoFieldList[4], 'extra')!==false)
5561  {
5562  $sql.= ' as main, '.MAIN_DB_PREFIX .$InfoFieldList[0].'_extrafields as extra';
5563  $sqlwhere.= ' WHERE extra.fk_object=main.'.$InfoFieldList[2]. ' AND '.$InfoFieldList[4];
5564  }
5565  else
5566  {
5567  $sqlwhere.= ' WHERE '.$InfoFieldList[4];
5568  }
5569  }
5570  else
5571  {
5572  $sqlwhere.= ' WHERE 1=1';
5573  }
5574  // Some tables may have field, some other not. For the moment we disable it.
5575  if (in_array($InfoFieldList[0],array('tablewithentity')))
5576  {
5577  $sqlwhere.= ' AND entity = '.$conf->entity;
5578  }
5579  $sql.=$sqlwhere;
5580  //print $sql;
5581 
5582  $sql .= ' ORDER BY ' . implode(', ', $fields_label);
5583 
5584  dol_syslog(get_class($this).'::showInputField type=sellist', LOG_DEBUG);
5585  $resql = $this->db->query($sql);
5586  if ($resql)
5587  {
5588  $out.='<option value="0">&nbsp;</option>';
5589  $num = $this->db->num_rows($resql);
5590  $i = 0;
5591  while ($i < $num)
5592  {
5593  $labeltoshow='';
5594  $obj = $this->db->fetch_object($resql);
5595 
5596  // Several field into label (eq table:code|libelle:rowid)
5597  $notrans = false;
5598  $fields_label = explode('|',$InfoFieldList[1]);
5599  if (is_array($fields_label))
5600  {
5601  $notrans = true;
5602  foreach ($fields_label as $field_toshow)
5603  {
5604  $labeltoshow.= $obj->$field_toshow.' ';
5605  }
5606  }
5607  else
5608  {
5609  $labeltoshow=$obj->{$InfoFieldList[1]};
5610  }
5611  $labeltoshow=dol_trunc($labeltoshow,45);
5612 
5613  if ($value == $obj->rowid)
5614  {
5615  foreach ($fields_label as $field_toshow)
5616  {
5617  $translabel=$langs->trans($obj->$field_toshow);
5618  if ($translabel!=$obj->$field_toshow) {
5619  $labeltoshow=dol_trunc($translabel,18).' ';
5620  }else {
5621  $labeltoshow=dol_trunc($obj->$field_toshow,18).' ';
5622  }
5623  }
5624  $out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
5625  }
5626  else
5627  {
5628  if (! $notrans)
5629  {
5630  $translabel=$langs->trans($obj->{$InfoFieldList[1]});
5631  if ($translabel!=$obj->{$InfoFieldList[1]}) {
5632  $labeltoshow=dol_trunc($translabel,18);
5633  }
5634  else {
5635  $labeltoshow=dol_trunc($obj->{$InfoFieldList[1]},18);
5636  }
5637  }
5638  if (empty($labeltoshow)) $labeltoshow='(not defined)';
5639  if ($value==$obj->rowid)
5640  {
5641  $out.='<option value="'.$obj->rowid.'" selected>'.$labeltoshow.'</option>';
5642  }
5643 
5644  if (!empty($InfoFieldList[3]) && $parentField)
5645  {
5646  $parent = $parentName.':'.$obj->{$parentField};
5647  }
5648 
5649  $out.='<option value="'.$obj->rowid.'"';
5650  $out.= ($value==$obj->rowid?' selected':'');
5651  $out.= (!empty($parent)?' parent="'.$parent.'"':'');
5652  $out.='>'.$labeltoshow.'</option>';
5653  }
5654 
5655  $i++;
5656  }
5657  $this->db->free($resql);
5658  }
5659  else {
5660  print 'Error in request '.$sql.' '.$this->db->lasterror().'. Check setup of extra parameters.<br>';
5661  }
5662  }
5663  $out.='</select>';
5664  }
5665  elseif ($type == 'checkbox')
5666  {
5667  $value_arr=explode(',',$value);
5668  $out=$form->multiselectarray($keyprefix.$key.$keysuffix, (empty($param['options'])?null:$param['options']), $value_arr, '', 0, '', 0, '100%');
5669  }
5670  elseif ($type == 'radio')
5671  {
5672  $out='';
5673  foreach ($param['options'] as $keyopt => $val)
5674  {
5675  $out.='<input class="flat '.$morecss.'" type="radio" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" '.($moreparam?$moreparam:'');
5676  $out.=' value="'.$keyopt.'"';
5677  $out.=' id="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'"';
5678  $out.= ($value==$keyopt?'checked':'');
5679  $out.='/><label for="'.$keyprefix.$key.$keysuffix.'_'.$keyopt.'">'.$val.'</label><br>';
5680  }
5681  }
5682  elseif ($type == 'chkbxlst')
5683  {
5684  if (is_array($value)) {
5685  $value_arr = $value;
5686  }
5687  else {
5688  $value_arr = explode(',', $value);
5689  }
5690 
5691  if (is_array($param['options'])) {
5692  $param_list = array_keys($param['options']);
5693  $InfoFieldList = explode(":", $param_list[0]);
5694  $parentName='';
5695  $parentField='';
5696  // 0 : tableName
5697  // 1 : label field name
5698  // 2 : key fields name (if differ of rowid)
5699  // 3 : key field parent (for dependent lists)
5700  // 4 : where clause filter on column or table extrafield, syntax field='value' or extra.field=value
5701  $keyList = (empty($InfoFieldList[2]) ? 'rowid' : $InfoFieldList[2] . ' as rowid');
5702 
5703  if (count($InfoFieldList) > 3 && ! empty($InfoFieldList[3])) {
5704  list ( $parentName, $parentField ) = explode('|', $InfoFieldList[3]);
5705  $keyList .= ', ' . $parentField;
5706  }
5707  if (count($InfoFieldList) > 4 && ! empty($InfoFieldList[4])) {
5708  if (strpos($InfoFieldList[4], 'extra.') !== false) {
5709  $keyList = 'main.' . $InfoFieldList[2] . ' as rowid';
5710  } else {
5711  $keyList = $InfoFieldList[2] . ' as rowid';
5712  }
5713  }
5714 
5715  $fields_label = explode('|', $InfoFieldList[1]);
5716  if (is_array($fields_label)) {
5717  $keyList .= ', ';
5718  $keyList .= implode(', ', $fields_label);
5719  }
5720 
5721  $sqlwhere = '';
5722  $sql = 'SELECT ' . $keyList;
5723  $sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
5724  if (! empty($InfoFieldList[4])) {
5725 
5726  // can use SELECT request
5727  if (strpos($InfoFieldList[4], '$SEL$')!==false) {
5728  $InfoFieldList[4]=str_replace('$SEL$','SELECT',$InfoFieldList[4]);
5729  }
5730 
5731  // current object id can be use into filter
5732  if (strpos($InfoFieldList[4], '$ID$')!==false && !empty($objectid)) {
5733  $InfoFieldList[4]=str_replace('$ID$',$objectid,$InfoFieldList[4]);
5734  } else {
5735  $InfoFieldList[4]=str_replace('$ID$','0',$InfoFieldList[4]);
5736  }
5737 
5738  // We have to join on extrafield table
5739  if (strpos($InfoFieldList[4], 'extra') !== false) {
5740  $sql .= ' as main, ' . MAIN_DB_PREFIX . $InfoFieldList[0] . '_extrafields as extra';
5741  $sqlwhere .= ' WHERE extra.fk_object=main.' . $InfoFieldList[2] . ' AND ' . $InfoFieldList[4];
5742  } else {
5743  $sqlwhere .= ' WHERE ' . $InfoFieldList[4];
5744  }
5745  } else {
5746  $sqlwhere .= ' WHERE 1=1';
5747  }
5748  // Some tables may have field, some other not. For the moment we disable it.
5749  if (in_array($InfoFieldList[0], array ('tablewithentity')))
5750  {
5751  $sqlwhere .= ' AND entity = ' . $conf->entity;
5752  }
5753  // $sql.=preg_replace('/^ AND /','',$sqlwhere);
5754  // print $sql;
5755 
5756  $sql .= $sqlwhere;
5757  dol_syslog(get_class($this) . '::showInputField type=chkbxlst',LOG_DEBUG);
5758  $resql = $this->db->query($sql);
5759  if ($resql) {
5760  $num = $this->db->num_rows($resql);
5761  $i = 0;
5762 
5763  $data=array();
5764 
5765  while ( $i < $num ) {
5766  $labeltoshow = '';
5767  $obj = $this->db->fetch_object($resql);
5768 
5769  $notrans = false;
5770  // Several field into label (eq table:code|libelle:rowid)
5771  $fields_label = explode('|', $InfoFieldList[1]);
5772  if (is_array($fields_label)) {
5773  $notrans = true;
5774  foreach ( $fields_label as $field_toshow ) {
5775  $labeltoshow .= $obj->$field_toshow . ' ';
5776  }
5777  } else {
5778  $labeltoshow = $obj->{$InfoFieldList[1]};
5779  }
5780  $labeltoshow = dol_trunc($labeltoshow, 45);
5781 
5782  if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5783  foreach ( $fields_label as $field_toshow ) {
5784  $translabel = $langs->trans($obj->$field_toshow);
5785  if ($translabel != $obj->$field_toshow) {
5786  $labeltoshow = dol_trunc($translabel, 18) . ' ';
5787  } else {
5788  $labeltoshow = dol_trunc($obj->$field_toshow, 18) . ' ';
5789  }
5790  }
5791 
5792  $data[$obj->rowid]=$labeltoshow;
5793  } else {
5794  if (! $notrans) {
5795  $translabel = $langs->trans($obj->{$InfoFieldList[1]});
5796  if ($translabel != $obj->{$InfoFieldList[1]}) {
5797  $labeltoshow = dol_trunc($translabel, 18);
5798  } else {
5799  $labeltoshow = dol_trunc($obj->{$InfoFieldList[1]}, 18);
5800  }
5801  }
5802  if (empty($labeltoshow))
5803  $labeltoshow = '(not defined)';
5804 
5805  if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
5806  $data[$obj->rowid]=$labeltoshow;
5807  }
5808 
5809  if (! empty($InfoFieldList[3]) && $parentField) {
5810  $parent = $parentName . ':' . $obj->{$parentField};
5811  }
5812 
5813  $data[$obj->rowid]=$labeltoshow;
5814  }
5815 
5816  $i ++;
5817  }
5818  $this->db->free($resql);
5819 
5820  $out=$form->multiselectarray($keyprefix.$key.$keysuffix, $data, $value_arr, '', 0, '', 0, '100%');
5821  } else {
5822  print 'Error in request ' . $sql . ' ' . $this->db->lasterror() . '. Check setup of extra parameters.<br>';
5823  }
5824  }
5825  }
5826  elseif ($type == 'link')
5827  {
5828  $param_list=array_keys($param['options']); // $param_list='ObjectName:classPath'
5829  $showempty=(($required && $default != '')?0:1);
5830  $out=$form->selectForForms($param_list[0], $keyprefix.$key.$keysuffix, $value, $showempty);
5831  }
5832  elseif ($type == 'password')
5833  {
5834  // If prefix is 'search_', field is used as a filter, we use a common text field.
5835  $out='<input type="'.($keyprefix=='search_'?'text':'password').'" class="flat '.$morecss.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'" value="'.$value.'" '.($moreparam?$moreparam:'').'>';
5836  }
5837  elseif ($type == 'array')
5838  {
5839  $newval = $val;
5840  $newval['type'] = 'varchar(256)';
5841 
5842  $out='';
5843 
5844  $inputs = array();
5845  if(! empty($value)) {
5846  foreach($value as $option) {
5847  $out.= '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
5848  $out.= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', $option, $moreparam, '', '', $showsize).'<br></span>';
5849  }
5850  }
5851 
5852  $out.= '<a id="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_add" href="javascript:;"><span class="fa fa-plus-circle valignmiddle"></span></a>';
5853 
5854  $newInput = '<span><a class="'.dol_escape_htmltag($keyprefix.$key.$keysuffix).'_del" href="javascript:;"><span class="fa fa-minus-circle valignmiddle"></span></a> ';
5855  $newInput.= $this->showInputField($newval, $keyprefix.$key.$keysuffix.'[]', '', $moreparam, '', '', $showsize).'<br></span>';
5856 
5857  if(! empty($conf->use_javascript_ajax)) {
5858  $out.= '
5859  <script type="text/javascript">
5860  $(document).ready(function() {
5861  $("a#'.dol_escape_js($keyprefix.$key.$keysuffix).'_add").click(function() {
5862  $("'.dol_escape_js($newInput).'").insertBefore(this);
5863  });
5864 
5865  $(document).on("click", "a.'.dol_escape_js($keyprefix.$key.$keysuffix).'_del", function() {
5866  $(this).parent().remove();
5867  });
5868  });
5869  </script>';
5870  }
5871  }
5872  if (!empty($hidden)) {
5873  $out='<input type="hidden" value="'.$value.'" name="'.$keyprefix.$key.$keysuffix.'" id="'.$keyprefix.$key.$keysuffix.'"/>';
5874  }
5875  /* Add comments
5876  if ($type == 'date') $out.=' (YYYY-MM-DD)';
5877  elseif ($type == 'datetime') $out.=' (YYYY-MM-DD HH:MM:SS)';
5878  */
5879  return $out;
5880  }
5881 
5895  function showOutputField($val, $key, $value, $moreparam='', $keysuffix='', $keyprefix='', $showsize=0)
5896  {
5897  global $conf,$langs,$form;
5898 
5899  if (! is_object($form))
5900  {
5901  require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
5902  $form=new Form($this->db);
5903  }
5904 
5905  $objectid = $this->id;
5906  $label = $val['label'];
5907  $type = $val['type'];
5908  $size = $val['css'];
5909 
5910  // Convert var to be able to share same code than showOutputField of extrafields
5911  if (preg_match('/varchar\((\d+)\)/', $type, $reg))
5912  {
5913  $type = 'varchar'; // convert varchar(xx) int varchar
5914  $size = $reg[1];
5915  }
5916  elseif (preg_match('/varchar/', $type)) $type = 'varchar'; // convert varchar(xx) int varchar
5917  if (is_array($val['arrayofkeyval'])) $type='select';
5918  if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg)) $type='link';
5919 
5920  $default=$val['default'];
5921  $computed=$val['computed'];
5922  $unique=$val['unique'];
5923  $required=$val['required'];
5924  $param=$val['param'];
5925  if (is_array($val['arrayofkeyval'])) $param['options'] = $val['arrayofkeyval'];
5926  if (preg_match('/^integer:(.*):(.*)/i', $val['type'], $reg))
5927  {
5928  $type='link';
5929  $param['options']=array($reg[1].':'.$reg[2]=>$reg[1].':'.$reg[2]);
5930  }
5931  $langfile=$val['langfile'];
5932  $list=$val['list'];
5933  $help=$val['help'];
5934  $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)
5935 
5936  if ($hidden) return '';
5937 
5938  // If field is a computed field, value must become result of compute
5939  if ($computed)
5940  {
5941  // Make the eval of compute string
5942  //var_dump($computed);
5943  $value = dol_eval($computed, 1, 0);
5944  }
5945 
5946  if (empty($showsize))
5947  {
5948  if ($type == 'date')
5949  {
5950  //$showsize=10;
5951  $showsize = 'minwidth100imp';
5952  }
5953  elseif ($type == 'datetime')
5954  {
5955  //$showsize=19;
5956  $showsize = 'minwidth200imp';
5957  }
5958  elseif (in_array($type,array('int','double','price')))
5959  {
5960  //$showsize=10;
5961  $showsize = 'maxwidth75';
5962  }
5963  elseif ($type == 'url')
5964  {
5965  $showsize='minwidth400';
5966  }
5967  elseif ($type == 'boolean')
5968  {
5969  $showsize='';
5970  }
5971  else
5972  {
5973  if (round($size) < 12)
5974  {
5975  $showsize = 'minwidth100';
5976  }
5977  else if (round($size) <= 48)
5978  {
5979  $showsize = 'minwidth200';
5980  }
5981  else
5982  {
5983  //$showsize=48;
5984  $showsize = 'minwidth400';
5985  }
5986  }
5987  }
5988 
5989  // Format output value differently according to properties of field
5990  if ($key == 'ref' && method_exists($this, 'getNomUrl')) $value=$this->getNomUrl(1, '', 0, '', 1);
5991  elseif ($key == 'status' && method_exists($this, 'getLibStatut')) $value=$this->getLibStatut(3);
5992  elseif ($type == 'date')
5993  {
5994  if(! empty($value)) {
5995  $value=dol_print_date($value,'day');
5996  } else {
5997  $value='';
5998  }
5999  }
6000  elseif ($type == 'datetime')
6001  {
6002  if(! empty($value)) {
6003  $value=dol_print_date($value,'dayhour');
6004  } else {
6005  $value='';
6006  }
6007  }
6008  elseif ($type == 'double')
6009  {
6010  if (!empty($value)) {
6011  $value=price($value);
6012  }
6013  }
6014  elseif ($type == 'boolean')
6015  {
6016  $checked='';
6017  if (!empty($value)) {
6018  $checked=' checked ';
6019  }
6020  $value='<input type="checkbox" '.$checked.' '.($moreparam?$moreparam:'').' readonly disabled>';
6021  }
6022  elseif ($type == 'mail')
6023  {
6024  $value=dol_print_email($value,0,0,0,64,1,1);
6025  }
6026  elseif ($type == 'url')
6027  {
6028  $value=dol_print_url($value,'_blank',32,1);
6029  }
6030  elseif ($type == 'phone')
6031  {
6032  $value=dol_print_phone($value, '', 0, 0, '', '&nbsp;', 1);
6033  }
6034  elseif ($type == 'price')
6035  {
6036  $value=price($value,0,$langs,0,0,-1,$conf->currency);
6037  }
6038  elseif ($type == 'select')
6039  {
6040  $value=$param['options'][$value];
6041  }
6042  elseif ($type == 'sellist')
6043  {
6044  $param_list=array_keys($param['options']);
6045  $InfoFieldList = explode(":", $param_list[0]);
6046 
6047  $selectkey="rowid";
6048  $keyList='rowid';
6049 
6050  if (count($InfoFieldList)>=3)
6051  {
6052  $selectkey = $InfoFieldList[2];
6053  $keyList=$InfoFieldList[2].' as rowid';
6054  }
6055 
6056  $fields_label = explode('|',$InfoFieldList[1]);
6057  if(is_array($fields_label)) {
6058  $keyList .=', ';
6059  $keyList .= implode(', ', $fields_label);
6060  }
6061 
6062  $sql = 'SELECT '.$keyList;
6063  $sql.= ' FROM '.MAIN_DB_PREFIX .$InfoFieldList[0];
6064  if (strpos($InfoFieldList[4], 'extra')!==false)
6065  {
6066  $sql.= ' as main';
6067  }
6068  if ($selectkey=='rowid' && empty($value)) {
6069  $sql.= " WHERE ".$selectkey."=0";
6070  } elseif ($selectkey=='rowid') {
6071  $sql.= " WHERE ".$selectkey."=".$this->db->escape($value);
6072  }else {
6073  $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
6074  }
6075 
6076  //$sql.= ' AND entity = '.$conf->entity;
6077 
6078  dol_syslog(get_class($this).':showOutputField:$type=sellist', LOG_DEBUG);
6079  $resql = $this->db->query($sql);
6080  if ($resql)
6081  {
6082  $value=''; // value was used, so now we reste it to use it to build final output
6083 
6084  $obj = $this->db->fetch_object($resql);
6085 
6086  // Several field into label (eq table:code|libelle:rowid)
6087  $fields_label = explode('|',$InfoFieldList[1]);
6088 
6089  if(is_array($fields_label) && count($fields_label)>1)
6090  {
6091  foreach ($fields_label as $field_toshow)
6092  {
6093  $translabel='';
6094  if (!empty($obj->$field_toshow)) {
6095  $translabel=$langs->trans($obj->$field_toshow);
6096  }
6097  if ($translabel!=$field_toshow) {
6098  $value.=dol_trunc($translabel,18).' ';
6099  }else {
6100  $value.=$obj->$field_toshow.' ';
6101  }
6102  }
6103  }
6104  else
6105  {
6106  $translabel='';
6107  if (!empty($obj->{$InfoFieldList[1]})) {
6108  $translabel=$langs->trans($obj->{$InfoFieldList[1]});
6109  }
6110  if ($translabel!=$obj->{$InfoFieldList[1]}) {
6111  $value=dol_trunc($translabel,18);
6112  }else {
6113  $value=$obj->{$InfoFieldList[1]};
6114  }
6115  }
6116  }
6117  else dol_syslog(get_class($this).'::showOutputField error '.$this->db->lasterror(), LOG_WARNING);
6118  }
6119  elseif ($type == 'radio')
6120  {
6121  $value=$param['options'][$value];
6122  }
6123  elseif ($type == 'checkbox')
6124  {
6125  $value_arr=explode(',',$value);
6126  $value='';
6127  if (is_array($value_arr))
6128  {
6129  foreach ($value_arr as $keyval=>$valueval) {
6130  $toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$param['options'][$valueval].'</li>';
6131  }
6132  }
6133  $value='<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
6134  }
6135  elseif ($type == 'chkbxlst')
6136  {
6137  $value_arr = explode(',', $value);
6138 
6139  $param_list = array_keys($param['options']);
6140  $InfoFieldList = explode(":", $param_list[0]);
6141 
6142  $selectkey = "rowid";
6143  $keyList = 'rowid';
6144 
6145  if (count($InfoFieldList) >= 3) {
6146  $selectkey = $InfoFieldList[2];
6147  $keyList = $InfoFieldList[2] . ' as rowid';
6148  }
6149 
6150  $fields_label = explode('|', $InfoFieldList[1]);
6151  if (is_array($fields_label)) {
6152  $keyList .= ', ';
6153  $keyList .= implode(', ', $fields_label);
6154  }
6155 
6156  $sql = 'SELECT ' . $keyList;
6157  $sql .= ' FROM ' . MAIN_DB_PREFIX . $InfoFieldList[0];
6158  if (strpos($InfoFieldList[4], 'extra') !== false) {
6159  $sql .= ' as main';
6160  }
6161  // $sql.= " WHERE ".$selectkey."='".$this->db->escape($value)."'";
6162  // $sql.= ' AND entity = '.$conf->entity;
6163 
6164  dol_syslog(get_class($this) . ':showOutputField:$type=chkbxlst',LOG_DEBUG);
6165  $resql = $this->db->query($sql);
6166  if ($resql) {
6167  $value = ''; // value was used, so now we reste it to use it to build final output
6168  $toprint=array();
6169  while ( $obj = $this->db->fetch_object($resql) ) {
6170 
6171  // Several field into label (eq table:code|libelle:rowid)
6172  $fields_label = explode('|', $InfoFieldList[1]);
6173  if (is_array($value_arr) && in_array($obj->rowid, $value_arr)) {
6174  if (is_array($fields_label) && count($fields_label) > 1) {
6175  foreach ( $fields_label as $field_toshow ) {
6176  $translabel = '';
6177  if (! empty($obj->$field_toshow)) {
6178  $translabel = $langs->trans($obj->$field_toshow);
6179  }
6180  if ($translabel != $field_toshow) {
6181  $toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.dol_trunc($translabel, 18).'</li>';
6182  } else {
6183  $toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$obj->$field_toshow.'</li>';
6184  }
6185  }
6186  } else {
6187  $translabel = '';
6188  if (! empty($obj->{$InfoFieldList[1]})) {
6189  $translabel = $langs->trans($obj->{$InfoFieldList[1]});
6190  }
6191  if ($translabel != $obj->{$InfoFieldList[1]}) {
6192  $toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.dol_trunc($translabel, 18).'</li>';
6193  } else {
6194  $toprint[]='<li class="select2-search-choice-dolibarr noborderoncategories" style="background: #aaa">'.$obj->{$InfoFieldList[1]}.'</li>';
6195  }
6196  }
6197  }
6198  }
6199  $value='<div class="select2-container-multi-dolibarr" style="width: 90%;"><ul class="select2-choices-dolibarr">'.implode(' ', $toprint).'</ul></div>';
6200  } else {
6201  dol_syslog(get_class($this) . '::showOutputField error ' . $this->db->lasterror(), LOG_WARNING);
6202  }
6203  }
6204  elseif ($type == 'link')
6205  {
6206  $out='';
6207 
6208  // only if something to display (perf)
6209  if ($value)
6210  {
6211  $param_list=array_keys($param['options']); // $param_list='ObjectName:classPath'
6212 
6213  $InfoFieldList = explode(":", $param_list[0]);
6214  $classname=$InfoFieldList[0];
6215  $classpath=$InfoFieldList[1];
6216  $getnomurlparam=(empty($InfoFieldList[2]) ? 3 : $InfoFieldList[2]);
6217  if (! empty($classpath))
6218  {
6219  dol_include_once($InfoFieldList[1]);
6220  if ($classname && class_exists($classname))
6221  {
6222  $object = new $classname($this->db);
6223  $object->fetch($value);
6224  $value=$object->getNomUrl($getnomurlparam);
6225  }
6226  }
6227  else
6228  {
6229  dol_syslog('Error bad setup of extrafield', LOG_WARNING);
6230  return 'Error bad setup of extrafield';
6231  }
6232  }
6233  else $value='';
6234  }
6235  elseif ($type == 'text' || $type == 'html')
6236  {
6237  $value=dol_htmlentitiesbr($value);
6238  }
6239  elseif ($type == 'password')
6240  {
6241  $value=preg_replace('/./i','*',$value);
6242  }
6243  elseif ($type == 'array')
6244  {
6245  $value = implode('<br>', $value);
6246  }
6247 
6248  //print $type.'-'.$size;
6249  $out=$value;
6250 
6251  return $out;
6252  }
6253 
6254 
6266  function showOptionals($extrafields, $mode='view', $params=null, $keysuffix='', $keyprefix='', $onetrtd=0)
6267  {
6268  global $db, $conf, $langs, $action, $form;
6269 
6270  if (! is_object($form)) $form=new Form($db);
6271 
6272  $out = '';
6273 
6274  if (is_array($extrafields->attributes[$this->table_element]['label']) && count($extrafields->attributes[$this->table_element]['label']) > 0)
6275  {
6276  $out .= "\n";
6277  $out .= '<!-- showOptionalsInput --> ';
6278  $out .= "\n";
6279 
6280  $e = 0;
6281  foreach($extrafields->attributes[$this->table_element]['label'] as $key=>$label)
6282  {
6283  // Show only the key field in params
6284  if (is_array($params) && array_key_exists('onlykey',$params) && $key != $params['onlykey']) continue;
6285 
6286  // @TODO Add test also on 'enabled' (different than 'list' that is 'visibility')
6287  $enabled = 1;
6288 
6289  $visibility = 1;
6290  if ($visibility && isset($extrafields->attributes[$this->table_element]['list'][$key]))
6291  {
6292  $visibility = dol_eval($extrafields->attributes[$this->table_element]['list'][$key], 1);
6293  }
6294 
6295  $perms = 1;
6296  if ($perms && isset($extrafields->attributes[$this->table_element]['perms'][$key]))
6297  {
6298  $perms = dol_eval($extrafields->attributes[$this->table_element]['perms'][$key], 1);
6299  }
6300 
6301  if (($mode == 'create' || $mode == 'edit') && abs($visibility) != 1 && abs($visibility) != 3) continue; // <> -1 and <> 1 and <> 3 = not visible on forms, only on list
6302  if (empty($perms)) continue;
6303 
6304  // Load language if required
6305  if (! empty($extrafields->attributes[$this->table_element]['langfile'][$key])) $langs->load($extrafields->attributes[$this->table_element]['langfile'][$key]);
6306 
6307  $colspan='3';
6308  if (is_array($params) && count($params)>0) {
6309  if (array_key_exists('colspan',$params)) {
6310  $colspan=$params['colspan'];
6311  }
6312  }
6313 
6314  switch($mode) {
6315  case "view":
6316  $value=$this->array_options["options_".$key.$keysuffix];
6317  break;
6318  case "edit":
6319  $getposttemp = GETPOST($keyprefix.'options_'.$key.$keysuffix, 'none'); // GETPOST can get value from GET, POST or setup of default values.
6320  // GETPOST("options_" . $key) can be 'abc' or array(0=>'abc')
6321  if (is_array($getposttemp) || $getposttemp != '' || GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix))
6322  {
6323  if (is_array($getposttemp)) {
6324  // $getposttemp is an array but following code expects a comma separated string
6325  $value = implode(",", $getposttemp);
6326  } else {
6327  $value = $getposttemp;
6328  }
6329  } else {
6330  $value = $this->array_options["options_" . $key]; // No GET, no POST, no default value, so we take value of object.
6331  }
6332  //var_dump($keyprefix.' - '.$key.' - '.$keysuffix.' - '.$keyprefix.'options_'.$key.$keysuffix.' - '.$this->array_options["options_".$key.$keysuffix].' - '.$getposttemp.' - '.$value);
6333  break;
6334  }
6335 
6336  if ($extrafields->attributes[$this->table_element]['type'][$key] == 'separate')
6337  {
6338  $out .= $extrafields->showSeparator($key, $this);
6339  }
6340  else
6341  {
6342  $csstyle='';
6343  $class=(!empty($extrafields->attributes[$this->table_element]['hidden'][$key]) ? 'hideobject ' : '');
6344  if (is_array($params) && count($params)>0) {
6345  if (array_key_exists('style',$params)) {
6346  $csstyle=$params['style'];
6347  }
6348  }
6349 
6350  // add html5 elements
6351  $domData = ' data-element="extrafield"';
6352  $domData .= ' data-targetelement="'.$this->element.'"';
6353  $domData .= ' data-targetid="'.$this->id.'"';
6354 
6355  $html_id = !empty($this->id) ? 'extrarow-'.$this->element.'_'.$key.'_'.$this->id : '';
6356 
6357  $out .= '<tr id="'.$html_id.'" '.$csstyle.' class="'.$class.$this->element.'_extras_'.$key.'" '.$domData.' >';
6358 
6359  if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0)
6360  {
6361  if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && ($e % 2) == 0) { $colspan='0'; }
6362  }
6363 
6364  if ($action == 'selectlines') { $colspan++; }
6365 
6366  // Convert date into timestamp format (value in memory must be a timestamp)
6367  if (in_array($extrafields->attributes[$this->table_element]['type'][$key],array('date','datetime')))
6368  {
6369  $datenotinstring = $this->array_options['options_' . $key];
6370  if (! is_numeric($this->array_options['options_' . $key])) // For backward compatibility
6371  {
6372  $datenotinstring = $this->db->jdate($datenotinstring);
6373  }
6374  $value = GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)?dol_mktime(GETPOST($keyprefix.'options_'.$key.$keysuffix."hour", 'int', 3), GETPOST($keyprefix.'options_'.$key.$keysuffix."min",'int',3), 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;
6375  }
6376  // Convert float submited string into real php numeric (value in memory must be a php numeric)
6377  if (in_array($extrafields->attributes[$this->table_element]['type'][$key],array('price','double')))
6378  {
6379  $value = GETPOSTISSET($keyprefix.'options_'.$key.$keysuffix)?price2num(GETPOST($keyprefix.'options_'.$key.$keysuffix, 'alpha', 3)):$this->array_options['options_'.$key];
6380  }
6381 
6382  $labeltoshow = $langs->trans($label);
6383 
6384  $out .= '<td class="titlefield';
6385  if (GETPOST('action','none') == 'create') $out.='create';
6386  if ($mode != 'view' && ! empty($extrafields->attributes[$this->table_element]['required'][$key])) $out .= ' fieldrequired';
6387  $out .= '">';
6388  if (! empty($extrafields->attributes[$object->table_element]['help'][$key])) $out .= $form->textwithpicto($labeltoshow, $extrafields->attributes[$object->table_element]['help'][$key]);
6389  else $out .= $labeltoshow;
6390  $out .= '</td>';
6391 
6392  $html_id = !empty($this->id) ? $this->element.'_extras_'.$key.'_'.$this->id : '';
6393  $out .='<td id="'.$html_id.'" class="'.$this->element.'_extras_'.$key.'" '.($colspan?' colspan="'.$colspan.'"':'').'>';
6394 
6395  switch($mode) {
6396  case "view":
6397  $out .= $extrafields->showOutputField($key, $value);
6398  break;
6399  case "edit":
6400  $out .= $extrafields->showInputField($key, $value, '', $keysuffix, '', 0, $this->id);
6401  break;
6402  }
6403 
6404  $out .= '</td>';
6405 
6406  if (! empty($conf->global->MAIN_EXTRAFIELDS_USE_TWO_COLUMS) && (($e % 2) == 1)) $out .= '</tr>';
6407  else $out .= '</tr>';
6408  $e++;
6409  }
6410  }
6411  $out .= "\n";
6412  // Add code to manage list depending on others
6413  if (! empty($conf->use_javascript_ajax)) {
6414  $out .= '
6415  <script type="text/javascript">
6416  jQuery(document).ready(function() {
6417  function showOptions(child_list, parent_list)
6418  {
6419  var val = $("select[name=\"options_"+parent_list+"\"]").val();
6420  var parentVal = parent_list + ":" + val;
6421  if(val > 0) {
6422  $("select[name=\""+child_list+"\"] option[parent]").hide();
6423  $("select[name=\""+child_list+"\"] option[parent=\""+parentVal+"\"]").show();
6424  } else {
6425  $("select[name=\""+child_list+"\"] option").show();
6426  }
6427  }
6428  function setListDependencies() {
6429  jQuery("select option[parent]").parent().each(function() {
6430  var child_list = $(this).attr("name");
6431  var parent = $(this).find("option[parent]:first").attr("parent");
6432  var infos = parent.split(":");
6433  var parent_list = infos[0];
6434  $("select[name=\""+parent_list+"\"]").change(function() {
6435  showOptions(child_list, parent_list);
6436  });
6437  });
6438  }
6439 
6440  setListDependencies();
6441  });
6442  </script>'."\n";
6443  $out .= '<!-- /showOptionalsInput --> '."\n";
6444  }
6445  }
6446  return $out;
6447  }
6448 
6449 
6454  public function getRights()
6455  {
6456  global $user;
6457 
6458  $element = $this->element;
6459  if ($element == 'facturerec') $element='facture';
6460 
6461  return $user->rights->{$element};
6462  }
6463 
6476  public static function commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
6477  {
6478  foreach ($tables as $table)
6479  {
6480  $sql = 'UPDATE '.MAIN_DB_PREFIX.$table.' SET fk_soc = '.$dest_id.' WHERE fk_soc = '.$origin_id;
6481 
6482  if (! $db->query($sql))
6483  {
6484  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.
6485  //$this->errors = $db->lasterror();
6486  return false;
6487  }
6488  }
6489 
6490  return true;
6491  }
6492 
6505  public function defineBuyPrice($unitPrice = 0.0, $discountPercent = 0.0, $fk_product = 0)
6506  {
6507  global $conf;
6508 
6509  $buyPrice = 0;
6510 
6511  if (($unitPrice > 0) && (isset($conf->global->ForceBuyingPriceIfNull) && $conf->global->ForceBuyingPriceIfNull == 1)) // In most cases, test here is false
6512  {
6513  $buyPrice = $unitPrice * (1 - $discountPercent / 100);
6514  }
6515  else
6516  {
6517  // Get cost price for margin calculation
6518  if (! empty($fk_product))
6519  {
6520  if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'costprice')
6521  {
6522  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6523  $product = new Product($this->db);
6524  $result = $product->fetch($fk_product);
6525  if ($result <= 0)
6526  {
6527  $this->errors[] = 'ErrorProductIdDoesNotExists';
6528  return -1;
6529  }
6530  if ($product->cost_price > 0)
6531  {
6532  $buyPrice = $product->cost_price;
6533  }
6534  else if ($product->pmp > 0)
6535  {
6536  $buyPrice = $product->pmp;
6537  }
6538  }
6539  else if (isset($conf->global->MARGIN_TYPE) && $conf->global->MARGIN_TYPE == 'pmp')
6540  {
6541  require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
6542  $product = new Product($this->db);
6543  $result = $product->fetch($fk_product);
6544  if ($result <= 0)
6545  {
6546  $this->errors[] = 'ErrorProductIdDoesNotExists';
6547  return -1;
6548  }
6549  if ($product->pmp > 0)
6550  {
6551  $buyPrice = $product->pmp;
6552  }
6553  }
6554 
6555  if (empty($buyPrice) && isset($conf->global->MARGIN_TYPE) && in_array($conf->global->MARGIN_TYPE, array('1','pmp','costprice')))
6556  {
6557  require_once DOL_DOCUMENT_ROOT.'/fourn/class/fournisseur.product.class.php';
6558  $productFournisseur = new ProductFournisseur($this->db);
6559  if (($result = $productFournisseur->find_min_price_product_fournisseur($fk_product)) > 0)
6560  {
6561  $buyPrice = $productFournisseur->fourn_unitprice;
6562  }
6563  else if ($result < 0)
6564  {
6565  $this->errors[] = $productFournisseur->error;
6566  return -2;
6567  }
6568  }
6569  }
6570  }
6571  return $buyPrice;
6572  }
6573 
6574  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.NotCamelCaps
6592  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)
6593  {
6594  // phpcs:enable
6595  global $conf,$user,$langs;
6596 
6597  include_once DOL_DOCUMENT_ROOT .'/core/lib/files.lib.php';
6598  include_once DOL_DOCUMENT_ROOT .'/core/lib/images.lib.php';
6599 
6600  $sortfield='position_name';
6601  $sortorder='asc';
6602 
6603  $dir = $sdir . '/';
6604  $pdir = '/';
6605  if ($modulepart == 'ticket')
6606  {
6607  $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->track_id.'/';
6608  $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->track_id.'/';
6609  }
6610  else
6611  {
6612  $dir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/';
6613  $pdir .= get_exdir(0, 0, 0, 0, $this, $modulepart).$this->ref.'/';
6614  }
6615 
6616  // For backward compatibility
6617  if ($modulepart == 'product' && ! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO))
6618  {
6619  $dir = $sdir . '/'. get_exdir($this->id,2,0,0,$this,$modulepart) . $this->id ."/photos/";
6620  $pdir = '/' . get_exdir($this->id,2,0,0,$this,$modulepart) . $this->id ."/photos/";
6621  }
6622 
6623  // Defined relative dir to DOL_DATA_ROOT
6624  $relativedir = '';
6625  if ($dir)
6626  {
6627  $relativedir = preg_replace('/^'.preg_quote(DOL_DATA_ROOT,'/').'/', '', $dir);
6628  $relativedir = preg_replace('/^[\\/]/','',$relativedir);
6629  $relativedir = preg_replace('/[\\/]$/','',$relativedir);
6630  }
6631 
6632  $dirthumb = $dir.'thumbs/';
6633  $pdirthumb = $pdir.'thumbs/';
6634 
6635  $return ='<!-- Photo -->'."\n";
6636  $nbphoto=0;
6637 
6638  $filearray=dol_dir_list($dir,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
6639 
6640  /*if (! empty($conf->global->PRODUCT_USE_OLD_PATH_FOR_PHOTO)) // For backward compatiblity, we scan also old dirs
6641  {
6642  $filearrayold=dol_dir_list($dirold,"files",0,'','(\.meta|_preview.*\.png)$',$sortfield,(strtolower($sortorder)=='desc'?SORT_DESC:SORT_ASC),1);
6643  $filearray=array_merge($filearray, $filearrayold);
6644  }*/
6645 
6646  completeFileArrayWithDatabaseInfo($filearray, $relativedir);
6647 
6648  if (count($filearray))
6649  {
6650  if ($sortfield && $sortorder)
6651  {
6652  $filearray=dol_sort_array($filearray, $sortfield, $sortorder);
6653  }
6654 
6655  foreach($filearray as $key => $val)
6656  {
6657  $photo='';
6658  $file = $val['name'];
6659 
6660  //if (! utf8_check($file)) $file=utf8_encode($file); // To be sure file is stored in UTF8 in memory
6661 
6662  //if (dol_is_file($dir.$file) && image_format_supported($file) >= 0)
6663  if (image_format_supported($file) >= 0)
6664  {
6665  $nbphoto++;
6666  $photo = $file;
6667  $viewfilename = $file;
6668 
6669  if ($size == 1 || $size == 'small') { // Format vignette
6670 
6671  // Find name of thumb file
6672  $photo_vignette=basename(getImageFileNameForSize($dir.$file, '_small'));
6673  if (! dol_is_file($dirthumb.$photo_vignette)) $photo_vignette='';
6674 
6675  // Get filesize of original file
6676  $imgarray=dol_getImageSize($dir.$photo);
6677 
6678  if ($nbbyrow > 0)
6679  {
6680  if ($nbphoto == 1) $return.= '<table width="100%" valign="top" align="center" border="0" cellpadding="2" cellspacing="2">';
6681 
6682  if ($nbphoto % $nbbyrow == 1) $return.= '<tr align=center valign=middle border=1>';
6683  $return.= '<td width="'.ceil(100/$nbbyrow).'%" class="photo">';
6684  }
6685  else if ($nbbyrow < 0) $return .= '<div class="inline-block">';
6686 
6687  $return.= "\n";
6688 
6689  $relativefile=preg_replace('/^\//', '', $pdir.$photo);
6690  if (empty($nolink))
6691  {
6692  $urladvanced=getAdvancedPreviewUrl($modulepart, $relativefile, 0, 'entity='.$this->entity);
6693  if ($urladvanced) $return.='<a href="'.$urladvanced.'">';
6694  else $return.= '<a href="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" class="aphoto" target="_blank">';
6695  }
6696 
6697  // Show image (width height=$maxHeight)
6698  // Si fichier vignette disponible et image source trop grande, on utilise la vignette, sinon on utilise photo origine
6699  $alt=$langs->transnoentitiesnoconv('File').': '.$relativefile;
6700  $alt.=' - '.$langs->transnoentitiesnoconv('Size').': '.$imgarray['width'].'x'.$imgarray['height'];
6701  if ($notitle) $alt='';
6702 
6703  if ($usesharelink)
6704  {
6705  if ($val['share'])
6706  {
6707  if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight)
6708  {
6709  $return.= '<!-- Show original file (thumb not yet available with shared links) -->';
6710  $return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
6711  }
6712  else {
6713  $return.= '<!-- Show original file -->';
6714  $return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($val['share']).'" title="'.dol_escape_htmltag($alt).'">';
6715  }
6716  }
6717  else
6718  {
6719  $return.= '<!-- Show nophoto file (because file is not shared) -->';
6720  $return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/public/theme/common/nophoto.png" title="'.dol_escape_htmltag($alt).'">';
6721  }
6722  }
6723  else
6724  {
6725  if (empty($maxHeight) || $photo_vignette && $imgarray['height'] > $maxHeight)
6726  {
6727  $return.= '<!-- Show thumb -->';
6728  $return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdirthumb.$photo_vignette).'" title="'.dol_escape_htmltag($alt).'">';
6729  }
6730  else {
6731  $return.= '<!-- Show original file -->';
6732  $return.= '<img class="photo photowithmargin" border="0" height="'.$maxHeight.'" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'" title="'.dol_escape_htmltag($alt).'">';
6733  }
6734  }
6735 
6736  if (empty($nolink)) $return.= '</a>';
6737  $return.="\n";
6738 
6739  if ($showfilename) $return.= '<br>'.$viewfilename;
6740  if ($showaction)
6741  {
6742  $return.= '<br>';
6743  // On propose la generation de la vignette si elle n'existe pas et si la taille est superieure aux limites
6744  if ($photo_vignette && (image_format_supported($photo) > 0) && ($this->imgWidth > $maxWidth || $this->imgHeight > $maxHeight))
6745  {
6746  $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>';
6747  }
6748  // Special cas for product
6749  if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
6750  {
6751  // Link to resize
6752  $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; ';
6753 
6754  // Link to delete
6755  $return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
6756  $return.= img_delete().'</a>';
6757  }
6758  }
6759  $return.= "\n";
6760 
6761  if ($nbbyrow > 0)
6762  {
6763  $return.= '</td>';
6764  if (($nbphoto % $nbbyrow) == 0) $return.= '</tr>';
6765  }
6766  else if ($nbbyrow < 0) $return.='</div>';
6767  }
6768 
6769  if (empty($size)) { // Format origine
6770  $return.= '<img class="photo photowithmargin" border="0" src="'.DOL_URL_ROOT.'/viewimage.php?modulepart='.$modulepart.'&entity='.$this->entity.'&file='.urlencode($pdir.$photo).'">';
6771 
6772  if ($showfilename) $return.= '<br>'.$viewfilename;
6773  if ($showaction)
6774  {
6775  // Special case for product
6776  if ($modulepart == 'product' && ($user->rights->produit->creer || $user->rights->service->creer))
6777  {
6778  // Link to resize
6779  $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; ';
6780 
6781  // Link to delete
6782  $return.= '<a href="'.$_SERVER["PHP_SELF"].'?id='.$this->id.'&amp;action=delete&amp;file='.urlencode($pdir.$viewfilename).'">';
6783  $return.= img_delete().'</a>';
6784  }
6785  }
6786  }
6787 
6788  // On continue ou on arrete de boucler ?
6789  if ($nbmax && $nbphoto >= $nbmax) break;
6790  }
6791  }
6792 
6793  if ($size==1 || $size=='small')
6794  {
6795  if ($nbbyrow > 0)
6796  {
6797  // Ferme tableau
6798  while ($nbphoto % $nbbyrow)
6799  {
6800  $return.= '<td width="'.ceil(100/$nbbyrow).'%">&nbsp;</td>';
6801  $nbphoto++;
6802  }
6803 
6804  if ($nbphoto) $return.= '</table>';
6805  }
6806  }
6807  }
6808 
6809  $this->nbphoto = $nbphoto;
6810 
6811  return $return;
6812  }
6813 
6814 
6821  protected function isArray($info)
6822  {
6823  if(is_array($info))
6824  {
6825  if(isset($info['type']) && $info['type']=='array') return true;
6826  else return false;
6827  }
6828  else return false;
6829  }
6830 
6837  protected function isNull($info)
6838  {
6839  if(is_array($info))
6840  {
6841  if(isset($info['type']) && $info['type']=='null') return true;
6842  else return false;
6843  }
6844  else return false;
6845  }
6846 
6853  public function isDate($info)
6854  {
6855  if(isset($info['type']) && ($info['type']=='date' || $info['type']=='datetime' || $info['type']=='timestamp')) return true;
6856  else return false;
6857  }
6858 
6865  public function isInt($info)
6866  {
6867  if(is_array($info))
6868  {
6869  if(isset($info['type']) && ($info['type']=='int' || preg_match('/^integer/i',$info['type']) ) ) return true;
6870  else return false;
6871  }
6872  else return false;
6873  }
6874 
6881  public function isFloat($info)
6882  {
6883  if(is_array($info))
6884  {
6885  if (isset($info['type']) && (preg_match('/^(double|real)/i', $info['type']))) return true;
6886  else return false;
6887  }
6888  else return false;
6889  }
6890 
6897  public function isText($info)
6898  {
6899  if(is_array($info))
6900  {
6901  if(isset($info['type']) && $info['type']=='text') return true;
6902  else return false;
6903  }
6904  else return false;
6905  }
6906 
6913  protected function isIndex($info)
6914  {
6915  if(is_array($info))
6916  {
6917  if(isset($info['index']) && $info['index']==true) return true;
6918  else return false;
6919  }
6920  else return false;
6921  }
6922 
6929  protected function setSaveQuery()
6930  {
6931  global $conf;
6932 
6933  $queryarray=array();
6934  foreach ($this->fields as $field=>$info) // Loop on definition of fields
6935  {
6936  // Depending on field type ('datetime', ...)
6937  if($this->isDate($info))
6938  {
6939  if(empty($this->{$field}))
6940  {
6941  $queryarray[$field] = null;
6942  }
6943  else
6944  {
6945  $queryarray[$field] = $this->db->idate($this->{$field});
6946  }
6947  }
6948  else if($this->isArray($info))
6949  {
6950  if(! empty($this->{$field})) {
6951  if(! is_array($this->{$field})) {
6952  $this->{$field} = array($this->{$field});
6953  }
6954  $queryarray[$field] = serialize($this->{$field});
6955  } else {
6956  $queryarray[$field] = null;
6957  }
6958  }
6959  else if($this->isInt($info))
6960  {
6961  if ($field == 'entity' && is_null($this->{$field})) $queryarray[$field]=$conf->entity;
6962  else
6963  {
6964  $queryarray[$field] = (int) price2num($this->{$field});
6965  if (empty($queryarray[$field])) $queryarray[$field]=0; // May be reset to null later if property 'notnull' is -1 for this field.
6966  }
6967  }
6968  else if($this->isFloat($info))
6969  {
6970  $queryarray[$field] = (double) price2num($this->{$field});
6971  if (empty($queryarray[$field])) $queryarray[$field]=0;
6972  }
6973  else
6974  {
6975  $queryarray[$field] = $this->{$field};
6976  }
6977 
6978  if ($info['type'] == 'timestamp' && empty($queryarray[$field])) unset($queryarray[$field]);
6979  if (! empty($info['notnull']) && $info['notnull'] == -1 && empty($queryarray[$field])) $queryarray[$field] = null;
6980  }
6981 
6982  return $queryarray;
6983  }
6984 
6991  protected function setVarsFromFetchObj(&$obj)
6992  {
6993  foreach ($this->fields as $field => $info)
6994  {
6995  if($this->isDate($info))
6996  {
6997  if(empty($obj->{$field}) || $obj->{$field} === '0000-00-00 00:00:00' || $obj->{$field} === '1000-01-01 00:00:00') $this->{$field} = 0;
6998  else $this->{$field} = strtotime($obj->{$field});
6999  }
7000  elseif($this->isArray($info))
7001  {
7002  if(! empty($obj->{$field})) {
7003  $this->{$field} = @unserialize($obj->{$field});
7004  // Hack for data not in UTF8
7005  if($this->{$field } === false) @unserialize(utf8_decode($obj->{$field}));
7006  } else {
7007  $this->{$field} = array();
7008  }
7009  }
7010  elseif($this->isInt($info))
7011  {
7012  if ($field == 'rowid') $this->id = (int) $obj->{$field};
7013  else $this->{$field} = (int) $obj->{$field};
7014  }
7015  elseif($this->isFloat($info))
7016  {
7017  $this->{$field} = (double) $obj->{$field};
7018  }
7019  elseif($this->isNull($info))
7020  {
7021  $val = $obj->{$field};
7022  // zero is not null
7023  $this->{$field} = (is_null($val) || (empty($val) && $val!==0 && $val!=='0') ? null : $val);
7024  }
7025  else
7026  {
7027  $this->{$field} = $obj->{$field};
7028  }
7029  }
7030 
7031  // If there is no 'ref' field, we force property ->ref to ->id for a better compatibility with common functions.
7032  if (! isset($this->fields['ref']) && isset($this->id)) $this->ref = $this->id;
7033  }
7034 
7040  protected function getFieldList()
7041  {
7042  $keys = array_keys($this->fields);
7043  return implode(',', $keys);
7044  }
7045 
7053  protected function quote($value, $fieldsentry)
7054  {
7055  if (is_null($value)) return 'NULL';
7056  else if (preg_match('/^(int|double|real)/i', $fieldsentry['type'])) return $this->db->escape("$value");
7057  else return "'".$this->db->escape($value)."'";
7058  }
7059 
7060 
7068  public function createCommon(User $user, $notrigger = false)
7069  {
7070  global $langs;
7071 
7072  $error = 0;
7073 
7074  $now=dol_now();
7075 
7076  $fieldvalues = $this->setSaveQuery();
7077  if (array_key_exists('date_creation', $fieldvalues) && empty($fieldvalues['date_creation'])) $fieldvalues['date_creation']=$this->db->idate($now);
7078  if (array_key_exists('fk_user_creat', $fieldvalues) && ! ($fieldvalues['fk_user_creat'] > 0)) $fieldvalues['fk_user_creat']=$user->id;
7079  unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into insert.
7080 
7081  $keys=array();
7082  $values = array();
7083  foreach ($fieldvalues as $k => $v) {
7084  $keys[$k] = $k;
7085  $value = $this->fields[$k];
7086  $values[$k] = $this->quote($v, $value);
7087  }
7088 
7089  // Clean and check mandatory
7090  foreach($keys as $key)
7091  {
7092  // If field is an implicit foreign key field
7093  if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key]='';
7094  if (! empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key]='';
7095 
7096  //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
7097  if (isset($this->fields[$key]['notnull']) && $this->fields[$key]['notnull'] == 1 && ! isset($values[$key]) && is_null($val['default']))
7098  {
7099  $error++;
7100  $this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7101  }
7102 
7103  // If field is an implicit foreign key field
7104  if (preg_match('/^integer:/i', $this->fields[$key]['type']) && empty($values[$key])) $values[$key]='null';
7105  if (! empty($this->fields[$key]['foreignkey']) && empty($values[$key])) $values[$key]='null';
7106  }
7107 
7108  if ($error) return -1;
7109 
7110  $this->db->begin();
7111 
7112  if (! $error)
7113  {
7114  $sql = 'INSERT INTO '.MAIN_DB_PREFIX.$this->table_element;
7115  $sql.= ' ('.implode( ", ", $keys ).')';
7116  $sql.= ' VALUES ('.implode( ", ", $values ).')';
7117 
7118  $res = $this->db->query($sql);
7119  if ($res===false) {
7120  $error++;
7121  $this->errors[] = $this->db->lasterror();
7122  }
7123  }
7124 
7125  if (! $error)
7126  {
7127  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX . $this->table_element);
7128  }
7129 
7130  // Create extrafields
7131  if (! $error)
7132  {
7133  $result=$this->insertExtraFields();
7134  if ($result < 0) $error++;
7135  }
7136 
7137  // Triggers
7138  if (! $error && ! $notrigger)
7139  {
7140  // Call triggers
7141  $result=$this->call_trigger(strtoupper(get_class($this)).'_CREATE',$user);
7142  if ($result < 0) { $error++; }
7143  // End call triggers
7144  }
7145 
7146  // Commit or rollback
7147  if ($error) {
7148  $this->db->rollback();
7149  return -1;
7150  } else {
7151  $this->db->commit();
7152  return $this->id;
7153  }
7154  }
7155 
7156 
7165  public function fetchCommon($id, $ref = null, $morewhere = '')
7166  {
7167  if (empty($id) && empty($ref) && empty($morewhere)) return -1;
7168 
7169  $sql = 'SELECT '.$this->getFieldList();
7170  $sql.= ' FROM '.MAIN_DB_PREFIX.$this->table_element;
7171 
7172  if (!empty($id)) $sql.= ' WHERE rowid = '.$id;
7173  elseif (!empty($ref)) $sql.= " WHERE ref = ".$this->quote($ref, $this->fields['ref']);
7174  else $sql.=' WHERE 1 = 1'; // usage with empty id and empty ref is very rare
7175  if ($morewhere) $sql.= $morewhere;
7176  $sql.=' LIMIT 1'; // This is a fetch, to be sure to get only one record
7177 
7178  $res = $this->db->query($sql);
7179  if ($res)
7180  {
7181  $obj = $this->db->fetch_object($res);
7182  if ($obj)
7183  {
7184  $this->setVarsFromFetchObj($obj);
7185  return $this->id;
7186  }
7187  else
7188  {
7189  return 0;
7190  }
7191  }
7192  else
7193  {
7194  $this->error = $this->db->lasterror();
7195  $this->errors[] = $this->error;
7196  return -1;
7197  }
7198  }
7199 
7207  public function updateCommon(User $user, $notrigger = false)
7208  {
7209  global $conf, $langs;
7210 
7211  $error = 0;
7212 
7213  $now=dol_now();
7214 
7215  $fieldvalues = $this->setSaveQuery();
7216  if (array_key_exists('date_modification', $fieldvalues) && empty($fieldvalues['date_modification'])) $fieldvalues['date_modification']=$this->db->idate($now);
7217  if (array_key_exists('fk_user_modif', $fieldvalues) && ! ($fieldvalues['fk_user_modif'] > 0)) $fieldvalues['fk_user_modif']=$user->id;
7218  unset($fieldvalues['rowid']); // The field 'rowid' is reserved field name for autoincrement field so we don't need it into update.
7219 
7220  $keys=array();
7221  $values = array();
7222  foreach ($fieldvalues as $k => $v) {
7223  $keys[$k] = $k;
7224  $value = $this->fields[$k];
7225  $values[$k] = $this->quote($v, $value);
7226  $tmp[] = $k.'='.$this->quote($v, $this->fields[$k]);
7227  }
7228 
7229  // Clean and check mandatory
7230  foreach($keys as $key)
7231  {
7232  if (preg_match('/^integer:/i', $this->fields[$key]['type']) && $values[$key] == '-1') $values[$key]=''; // This is an implicit foreign key field
7233  if (! empty($this->fields[$key]['foreignkey']) && $values[$key] == '-1') $values[$key]=''; // This is an explicit foreign key field
7234 
7235  //var_dump($key.'-'.$values[$key].'-'.($this->fields[$key]['notnull'] == 1));
7236  /*
7237  if ($this->fields[$key]['notnull'] == 1 && empty($values[$key]))
7238  {
7239  $error++;
7240  $this->errors[]=$langs->trans("ErrorFieldRequired", $this->fields[$key]['label']);
7241  }*/
7242  }
7243 
7244  $sql = 'UPDATE '.MAIN_DB_PREFIX.$this->table_element.' SET '.implode( ',', $tmp ).' WHERE rowid='.$this->id ;
7245 
7246  $this->db->begin();
7247  if (! $error)
7248  {
7249  $res = $this->db->query($sql);
7250  if ($res===false)
7251  {
7252  $error++;
7253  $this->errors[] = $this->db->lasterror();
7254  }
7255  }
7256 
7257  // Update extrafield
7258  if (! $error && empty($conf->global->MAIN_EXTRAFIELDS_DISABLED) && is_array($this->array_options) && count($this->array_options)>0)
7259  {
7260  $result=$this->insertExtraFields();
7261  if ($result < 0)
7262  {
7263  $error++;
7264  }
7265  }
7266 
7267  // Triggers
7268  if (! $error && ! $notrigger)
7269  {
7270  // Call triggers
7271  $result=$this->call_trigger(strtoupper(get_class($this)).'_MODIFY',$user);
7272  if ($result < 0) { $error++; } //Do also here what you must do to rollback action if trigger fail
7273  // End call triggers
7274  }
7275 
7276  // Commit or rollback
7277  if ($error)