dolibarr  13.0.2
contrat.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2003 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004-2012 Destailleur Laurent <eldy@users.sourceforge.net>
4  * Copyright (C) 2005-2014 Regis Houssin <regis.houssin@inodbox.com>
5  * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
6  * Copyright (C) 2008 Raphael Bertrand <raphael.bertrand@resultic.fr>
7  * Copyright (C) 2010-2016 Juanjo Menent <jmenent@2byte.es>
8  * Copyright (C) 2013 Christophe Battarel <christophe.battarel@altairis.fr>
9  * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
10  * Copyright (C) 2014-2015 Marcos García <marcosgdf@gmail.com>
11  * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
12  * Copyright (C) 2018-2020 Frédéric France <frederic.france@netlogic.fr>
13  * Copyright (C) 2015-2018 Ferran Marcet <fmarcet@2byte.es>
14  *
15  * This program is free software; you can redistribute it and/or modify
16  * it under the terms of the GNU General Public License as published by
17  * the Free Software Foundation; either version 3 of the License, or
18  * (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program. If not, see <https://www.gnu.org/licenses/>.
27  */
28 
35 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
36 require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
37 require_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
38 require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
39 
43 class Contrat extends CommonObject
44 {
48  public $element = 'contrat';
49 
53  public $table_element = 'contrat';
54 
58  public $table_element_line = 'contratdet';
59 
63  public $fk_element = 'fk_contrat';
64 
68  public $picto = 'contract';
69 
74  public $ismultientitymanaged = 1;
75 
79  public $isextrafieldmanaged = 1;
80 
85  public $restrictiononfksoc = 1;
86 
90  protected $table_ref_field = 'ref';
91 
96  public $ref_customer;
97 
102  public $ref_supplier;
103 
108  public $entity;
109 
114  public $socid;
115 
116  public $societe; // Objet societe
117 
122  public $statut = 0; // 0=Draft,
123 
124  public $product;
125 
129  public $fk_user_author;
130 
136  public $user_author_id;
137 
141  public $user_creation;
142 
146  public $user_cloture;
147 
151  public $date_creation;
152 
156  public $date_modification;
157 
161  public $date_validation;
162 
166  public $date_contrat;
167 
168  public $commercial_signature_id;
169  public $commercial_suivi_id;
170 
175  public $fk_projet;
176 
177  public $extraparams = array();
178 
182  public $lines = array();
183 
188  protected $lines_id_index_mapper = array();
189 
190 
215  // BEGIN MODULEBUILDER PROPERTIES
219  public $fields = array(
220  'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
221  'ref' =>array('type'=>'varchar(50)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>-1, 'showoncombobox'=>1, 'position'=>15),
222  'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'Ref ext', 'enabled'=>1, 'visible'=>0, 'position'=>20),
223  'ref_supplier' =>array('type'=>'varchar(50)', 'label'=>'Ref supplier', 'enabled'=>1, 'visible'=>-1, 'position'=>25),
224  'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>30, 'index'=>1),
225  'tms' =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>35),
226  'datec' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-1, 'position'=>40),
227  'date_contrat' =>array('type'=>'datetime', 'label'=>'Date contrat', 'enabled'=>1, 'visible'=>-1, 'position'=>45),
228  'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>70),
229  'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label'=>'Fk projet', 'enabled'=>1, 'visible'=>-1, 'position'=>75),
230  'fk_commercial_signature' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'Fk commercial signature', 'enabled'=>1, 'visible'=>-1, 'position'=>80),
231  'fk_commercial_suivi' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'Fk commercial suivi', 'enabled'=>1, 'visible'=>-1, 'position'=>85),
232  'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'Fk user author', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>90),
233  'note_private' =>array('type'=>'text', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>105),
234  'note_public' =>array('type'=>'text', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>110),
235  'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'Model pdf', 'enabled'=>1, 'visible'=>0, 'position'=>115),
236  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>120),
237  'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>125),
238  'ref_customer' =>array('type'=>'varchar(50)', 'label'=>'Ref customer', 'enabled'=>1, 'visible'=>-1, 'position'=>130),
239  'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>135),
240  'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'Last main doc', 'enabled'=>1, 'visible'=>-1, 'position'=>140),
241  'statut' =>array('type'=>'smallint(6)', 'label'=>'Statut', 'enabled'=>1, 'visible'=>-1, 'position'=>500, 'arrayofkeyval'=>array(0=>'Draft', 1=>'Validated', 2=>'Closed'))
242  );
243  // END MODULEBUILDER PROPERTIES
244 
245  const STATUS_DRAFT = 0;
246  const STATUS_VALIDATED = 1;
247  const STATUS_CLOSED = 2;
248 
249 
250 
256  public function __construct($db)
257  {
258  $this->db = $db;
259  }
260 
267  public function getNextNumRef($soc)
268  {
269  global $db, $langs, $conf;
270  $langs->load("contracts");
271 
272  if (!empty($conf->global->CONTRACT_ADDON))
273  {
274  $mybool = false;
275 
276  $file = $conf->global->CONTRACT_ADDON.".php";
277  $classname = $conf->global->CONTRACT_ADDON;
278 
279  // Include file with class
280  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
281 
282  foreach ($dirmodels as $reldir) {
283  $dir = dol_buildpath($reldir."core/modules/contract/");
284 
285  // Load file with numbering class (if found)
286  $mybool |= @include_once $dir.$file;
287  }
288 
289  if (!$mybool)
290  {
291  dol_print_error('', "Failed to include file ".$file);
292  return '';
293  }
294 
295  $obj = new $classname();
296  $numref = $obj->getNextValue($soc, $this);
297 
298  if ($numref != "")
299  {
300  return $numref;
301  } else {
302  $this->error = $obj->error;
303  dol_print_error($db, get_class($this)."::getNextValue ".$obj->error);
304  return "";
305  }
306  } else {
307  $langs->load("errors");
308  print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("Contract"));
309  return "";
310  }
311  }
312 
313  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
324  public function active_line($user, $line_id, $date, $date_end = '', $comment = '')
325  {
326  // phpcs:enable
327  $result = $this->lines[$this->lines_id_index_mapper[$line_id]]->active_line($user, $date, $date_end, $comment);
328  if ($result < 0)
329  {
330  $this->error = $this->lines[$this->lines_id_index_mapper[$line_id]]->error;
331  $this->errors = $this->lines[$this->lines_id_index_mapper[$line_id]]->errors;
332  }
333  return $result;
334  }
335 
336 
337  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
347  public function close_line($user, $line_id, $date_end, $comment = '')
348  {
349  // phpcs:enable
350  $result = $this->lines[$this->lines_id_index_mapper[$line_id]]->close_line($user, $date_end, $comment);
351  if ($result < 0)
352  {
353  $this->error = $this->lines[$this->lines_id_index_mapper[$line_id]]->error;
354  $this->errors = $this->lines[$this->lines_id_index_mapper[$line_id]]->errors;
355  }
356  return $result;
357  }
358 
359 
370  public function activateAll($user, $date_start = '', $notrigger = 0, $comment = '')
371  {
372  if (empty($date_start)) $date_start = dol_now();
373 
374  $this->db->begin();
375 
376  $error = 0;
377 
378  // Load lines
379  $this->fetch_lines();
380 
381  foreach ($this->lines as $contratline)
382  {
383  // Open lines not already open
384  if ($contratline->statut != ContratLigne::STATUS_OPEN)
385  {
386  $contratline->context = $this->context;
387 
388  $result = $contratline->active_line($user, $date_start, -1, $comment);
389  if ($result < 0)
390  {
391  $error++;
392  $this->error = $contratline->error;
393  $this->errors = $contratline->errors;
394  break;
395  }
396  }
397  }
398 
399  if (!$error && $this->statut == 0)
400  {
401  $result = $this->validate($user, '', $notrigger);
402  if ($result < 0) $error++;
403  }
404 
405  if (!$error)
406  {
407  $this->db->commit();
408  return 1;
409  } else {
410  $this->db->rollback();
411  return -1;
412  }
413  }
414 
424  public function closeAll(User $user, $notrigger = 0, $comment = '')
425  {
426  $this->db->begin();
427 
428  // Load lines
429  $this->fetch_lines();
430 
431  $now = dol_now();
432 
433  $error = 0;
434 
435  foreach ($this->lines as $contratline)
436  {
437  // Close lines not already closed
438  if ($contratline->statut != ContratLigne::STATUS_CLOSED)
439  {
440  $contratline->date_cloture = $now;
441  $contratline->fk_user_cloture = $user->id;
442  $contratline->statut = ContratLigne::STATUS_CLOSED;
443  $result = $contratline->close_line($user, $now, $comment, $notrigger);
444  if ($result < 0)
445  {
446  $error++;
447  $this->error = $contratline->error;
448  $this->errors = $contratline->errors;
449  break;
450  }
451  }
452  }
453 
454  if (!$error && $this->statut == 0)
455  {
456  $result = $this->validate($user, '', $notrigger);
457  if ($result < 0) $error++;
458  }
459 
460  if (!$error)
461  {
462  $this->db->commit();
463  return 1;
464  } else {
465  $this->db->rollback();
466  return -1;
467  }
468  }
469 
478  public function validate(User $user, $force_number = '', $notrigger = 0)
479  {
480  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
481  global $langs, $conf;
482 
483  $now = dol_now();
484 
485  $error = 0;
486  dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number);
487 
488 
489  $this->db->begin();
490 
491  $this->fetch_thirdparty();
492 
493  // A contract is validated so we can move thirdparty to status customer
494  if (empty($conf->global->CONTRACT_DISABLE_AUTOSET_AS_CLIENT_ON_CONTRACT_VALIDATION))
495  {
496  $result = $this->thirdparty->set_as_client();
497  }
498 
499  // Define new ref
500  if ($force_number)
501  {
502  $num = $force_number;
503  } elseif (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) // empty should not happened, but when it occurs, the test save life
504  {
505  $num = $this->getNextNumRef($this->thirdparty);
506  } else {
507  $num = $this->ref;
508  }
509  $this->newref = dol_sanitizeFileName($num);
510 
511  if ($num)
512  {
513  $sql = "UPDATE ".MAIN_DB_PREFIX."contrat SET ref = '".$this->db->escape($num)."', statut = 1";
514  //$sql.= ", fk_user_valid = ".$user->id.", date_valid = '".$this->db->idate($now)."'";
515  $sql .= " WHERE rowid = ".$this->id." AND statut = 0";
516 
517  dol_syslog(get_class($this)."::validate", LOG_DEBUG);
518  $resql = $this->db->query($sql);
519  if (!$resql)
520  {
521  dol_print_error($this->db);
522  $error++;
523  $this->error = $this->db->lasterror();
524  }
525 
526  // Trigger calls
527  if (!$error && !$notrigger)
528  {
529  // Call trigger
530  $result = $this->call_trigger('CONTRACT_VALIDATE', $user);
531  if ($result < 0) { $error++; }
532  // End call triggers
533  }
534 
535  if (!$error)
536  {
537  $this->oldref = $this->ref;
538 
539  // Rename directory if dir was a temporary ref
540  if (preg_match('/^[\(]?PROV/i', $this->ref))
541  {
542  // Now we rename also files into index
543  $sql = 'UPDATE '.MAIN_DB_PREFIX."ecm_files set filename = CONCAT('".$this->db->escape($this->newref)."', SUBSTR(filename, ".(strlen($this->ref) + 1).")), filepath = 'contract/".$this->db->escape($this->newref)."'";
544  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'contract/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
545  $resql = $this->db->query($sql);
546  if (!$resql) { $error++; $this->error = $this->db->lasterror(); }
547 
548  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
549  $oldref = dol_sanitizeFileName($this->ref);
550  $newref = dol_sanitizeFileName($num);
551  $dirsource = $conf->contract->dir_output.'/'.$oldref;
552  $dirdest = $conf->contract->dir_output.'/'.$newref;
553  if (!$error && file_exists($dirsource))
554  {
555  dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
556 
557  if (@rename($dirsource, $dirdest))
558  {
559  dol_syslog("Rename ok");
560  // Rename docs starting with $oldref with $newref
561  $listoffiles = dol_dir_list($conf->contract->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
562  foreach ($listoffiles as $fileentry)
563  {
564  $dirsource = $fileentry['name'];
565  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
566  $dirsource = $fileentry['path'].'/'.$dirsource;
567  $dirdest = $fileentry['path'].'/'.$dirdest;
568  @rename($dirsource, $dirdest);
569  }
570  }
571  }
572  }
573  }
574 
575  // Set new ref and define current statut
576  if (!$error)
577  {
578  $this->ref = $num;
579  $this->statut = 1;
580  $this->brouillon = 0;
581  $this->date_validation = $now;
582  }
583  } else {
584  $error++;
585  }
586 
587  if (!$error)
588  {
589  $this->db->commit();
590  return 1;
591  } else {
592  $this->db->rollback();
593  return -1;
594  }
595  }
596 
604  public function reopen($user, $notrigger = 0)
605  {
606  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
607  global $langs, $conf;
608 
609  $now = dol_now();
610 
611  $error = 0;
612  dol_syslog(get_class($this).'::reopen user='.$user->id);
613 
614  $this->db->begin();
615 
616  $this->fetch_thirdparty();
617 
618  $sql = "UPDATE ".MAIN_DB_PREFIX."contrat SET statut = 0";
619  //$sql.= ", fk_user_valid = null, date_valid = null";
620  $sql .= " WHERE rowid = ".$this->id." AND statut = 1";
621 
622  dol_syslog(get_class($this)."::validate", LOG_DEBUG);
623  $resql = $this->db->query($sql);
624  if (!$resql)
625  {
626  dol_print_error($this->db);
627  $error++;
628  $this->error = $this->db->lasterror();
629  }
630 
631  // Trigger calls
632  if (!$error && !$notrigger)
633  {
634  // Call trigger
635  $result = $this->call_trigger('CONTRACT_REOPEN', $user);
636  if ($result < 0) {
637  $error++;
638  }
639  // End call triggers
640  }
641 
642  // Set new ref and define current status
643  if (!$error)
644  {
645  $this->statut = 0;
646  $this->brouillon = 1;
647  $this->date_validation = $now;
648  }
649 
650  if (!$error)
651  {
652  $this->db->commit();
653  return 1;
654  } else {
655  $this->db->rollback();
656  return -1;
657  }
658  }
659 
669  public function fetch($id, $ref = '', $ref_customer = '', $ref_supplier = '')
670  {
671  $sql = "SELECT rowid, statut, ref, fk_soc,";
672  $sql .= " ref_supplier, ref_customer,";
673  $sql .= " ref_ext,";
674  $sql .= " entity,";
675  $sql .= " date_contrat as datecontrat,";
676  $sql .= " fk_user_author,";
677  $sql .= " fk_projet as fk_project,";
678  $sql .= " fk_commercial_signature, fk_commercial_suivi,";
679  $sql .= " note_private, note_public, model_pdf, extraparams";
680  $sql .= " FROM ".MAIN_DB_PREFIX."contrat";
681  if (!$id) $sql .= " WHERE entity IN (".getEntity('contract').")";
682  else $sql .= " WHERE rowid=".(int) $id;
683  if ($ref_customer)
684  {
685  $sql .= " AND ref_customer = '".$this->db->escape($ref_customer)."'";
686  }
687  if ($ref_supplier)
688  {
689  $sql .= " AND ref_supplier = '".$this->db->escape($ref_supplier)."'";
690  }
691  if ($ref)
692  {
693  $sql .= " AND ref='".$this->db->escape($ref)."'";
694  }
695 
696  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
697  $resql = $this->db->query($sql);
698  if ($resql)
699  {
700  $num = $this->db->num_rows($resql);
701  if ($num > 1)
702  {
703  $this->error = 'Fetch found several records.';
704  dol_syslog($this->error, LOG_ERR);
705  $result = -2;
706  } elseif ($num) // $num = 1
707  {
708  $obj = $this->db->fetch_object($resql);
709  if ($obj)
710  {
711  $this->id = $obj->rowid;
712  $this->ref = (!isset($obj->ref) || !$obj->ref) ? $obj->rowid : $obj->ref;
713  $this->ref_customer = $obj->ref_customer;
714  $this->ref_supplier = $obj->ref_supplier;
715  $this->ref_ext = $obj->ref_ext;
716  $this->entity = $obj->entity;
717  $this->statut = $obj->statut;
718 
719  $this->date_contrat = $this->db->jdate($obj->datecontrat);
720  $this->date_creation = $this->db->jdate($obj->datecontrat);
721 
722  $this->user_author_id = $obj->fk_user_author;
723 
724  $this->commercial_signature_id = $obj->fk_commercial_signature;
725  $this->commercial_suivi_id = $obj->fk_commercial_suivi;
726 
727  $this->note_private = $obj->note_private;
728  $this->note_public = $obj->note_public;
729  $this->model_pdf = $obj->model_pdf;
730  $this->modelpdf = $obj->model_pdf; // deprecated
731 
732  $this->fk_projet = $obj->fk_project; // deprecated
733  $this->fk_project = $obj->fk_project;
734 
735  $this->socid = $obj->fk_soc;
736  $this->fk_soc = $obj->fk_soc;
737 
738  $this->extraparams = (array) json_decode($obj->extraparams, true);
739 
740  $this->db->free($resql);
741 
742  // Retrieve all extrafields
743  // fetch optionals attributes and labels
744  $this->fetch_optionals();
745 
746  // Lines
747  $result = $this->fetch_lines();
748  if ($result < 0)
749  {
750  $this->error = $this->db->lasterror();
751  return -3;
752  }
753 
754  return $this->id;
755  }
756  } else {
757  dol_syslog(get_class($this)."::fetch Contract not found");
758  $this->error = "Contract not found";
759  return 0;
760  }
761  } else {
762  dol_syslog(get_class($this)."::fetch Error searching contract");
763  $this->error = $this->db->error();
764  return -1;
765  }
766  }
767 
768  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
778  public function fetch_lines($only_product = 0, $loadalsotranslation = 0)
779  {
780  // phpcs:enable
781  global $langs, $conf, $extrafields;
782 
783  $this->nbofserviceswait = 0;
784  $this->nbofservicesopened = 0;
785  $this->nbofservicesexpired = 0;
786  $this->nbofservicesclosed = 0;
787 
788  $total_ttc = 0;
789  $total_vat = 0;
790  $total_ht = 0;
791 
792  $now = dol_now();
793 
794  if (!is_object($extrafields))
795  {
796  require_once DOL_DOCUMENT_ROOT.'/core/class/extrafields.class.php';
797  $extrafields = new ExtraFields($this->db);
798  }
799 
800  $line = new ContratLigne($this->db);
801  $extrafields->fetch_name_optionals_label($line->table_element, true);
802 
803  $this->lines = array();
804  $pos = 0;
805 
806  // Selects contract lines related to a product
807  $sql = "SELECT p.label as product_label, p.description as product_desc, p.ref as product_ref, p.fk_product_type as product_type,";
808  $sql .= " d.rowid, d.fk_contrat, d.statut, d.description, d.price_ht, d.vat_src_code, d.tva_tx, d.localtax1_tx, d.localtax2_tx, d.localtax1_type, d.localtax2_type, d.qty, d.remise_percent, d.subprice, d.fk_product_fournisseur_price as fk_fournprice, d.buy_price_ht as pa_ht,";
809  $sql .= " d.total_ht,";
810  $sql .= " d.total_tva,";
811  $sql .= " d.total_localtax1,";
812  $sql .= " d.total_localtax2,";
813  $sql .= " d.total_ttc,";
814  $sql .= " d.info_bits, d.fk_product,";
815  $sql .= " d.date_ouverture_prevue, d.date_ouverture,";
816  $sql .= " d.date_fin_validite, d.date_cloture,";
817  $sql .= " d.fk_user_author,";
818  $sql .= " d.fk_user_ouverture,";
819  $sql .= " d.fk_user_cloture,";
820  $sql .= " d.fk_unit,";
821  $sql .= " d.product_type as type";
822  $sql .= " FROM ".MAIN_DB_PREFIX."contratdet as d LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
823  $sql .= " WHERE d.fk_contrat = ".$this->id;
824  $sql .= " ORDER by d.rowid ASC";
825 
826  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
827  $result = $this->db->query($sql);
828  if ($result)
829  {
830  $num = $this->db->num_rows($result);
831  $i = 0;
832 
833  while ($i < $num)
834  {
835  $objp = $this->db->fetch_object($result);
836 
837  $line = new ContratLigne($this->db);
838  $line->id = $objp->rowid;
839  $line->ref = $objp->rowid;
840  $line->fk_contrat = $objp->fk_contrat;
841  $line->desc = $objp->description; // Description line
842  $line->qty = $objp->qty;
843  $line->vat_src_code = $objp->vat_src_code;
844  $line->tva_tx = $objp->tva_tx;
845  $line->localtax1_tx = $objp->localtax1_tx;
846  $line->localtax2_tx = $objp->localtax2_tx;
847  $line->localtax1_type = $objp->localtax1_type;
848  $line->localtax2_type = $objp->localtax2_type;
849  $line->subprice = $objp->subprice;
850  $line->statut = $objp->statut;
851  $line->remise_percent = $objp->remise_percent;
852  $line->price_ht = $objp->price_ht;
853  $line->price = $objp->price_ht; // For backward compatibility
854  $line->total_ht = $objp->total_ht;
855  $line->total_tva = $objp->total_tva;
856  $line->total_localtax1 = $objp->total_localtax1;
857  $line->total_localtax2 = $objp->total_localtax2;
858  $line->total_ttc = $objp->total_ttc;
859  $line->fk_product = (($objp->fk_product > 0) ? $objp->fk_product : 0);
860  $line->info_bits = $objp->info_bits;
861  $line->type = $objp->type;
862 
863  $line->fk_fournprice = $objp->fk_fournprice;
864  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $line->fk_fournprice, $objp->pa_ht);
865  $line->pa_ht = $marginInfos[0];
866 
867  $line->fk_user_author = $objp->fk_user_author;
868  $line->fk_user_ouverture = $objp->fk_user_ouverture;
869  $line->fk_user_cloture = $objp->fk_user_cloture;
870  $line->fk_unit = $objp->fk_unit;
871 
872  $line->ref = $objp->product_ref; // deprecated
873  $line->product_ref = $objp->product_ref; // Product Ref
874  $line->product_type = $objp->product_type; // Product Type
875  $line->product_desc = $objp->product_desc; // Product Description
876  $line->product_label = $objp->product_label; // Product Label
877 
878  $line->description = $objp->description;
879 
880  $line->date_start = $this->db->jdate($objp->date_ouverture_prevue);
881  $line->date_start_real = $this->db->jdate($objp->date_ouverture);
882  $line->date_end = $this->db->jdate($objp->date_fin_validite);
883  $line->date_end_real = $this->db->jdate($objp->date_cloture);
884  // For backward compatibility
885  $line->date_ouverture_prevue = $this->db->jdate($objp->date_ouverture_prevue);
886  $line->date_ouverture = $this->db->jdate($objp->date_ouverture);
887  $line->date_fin_validite = $this->db->jdate($objp->date_fin_validite);
888  $line->date_cloture = $this->db->jdate($objp->date_cloture);
889  $line->date_debut_prevue = $this->db->jdate($objp->date_ouverture_prevue);
890  $line->date_debut_reel = $this->db->jdate($objp->date_ouverture);
891  $line->date_fin_prevue = $this->db->jdate($objp->date_fin_validite);
892  $line->date_fin_reel = $this->db->jdate($objp->date_cloture);
893 
894  // Retrieve all extrafields for contract
895  // fetch optionals attributes and labels
896  $line->fetch_optionals();
897 
898  // multilangs
899  if (!empty($conf->global->MAIN_MULTILANGS) && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
900  $line = new Product($this->db);
901  $line->fetch($objp->fk_product);
902  $line->getMultiLangs();
903  }
904 
905  $this->lines[$pos] = $line;
906  $this->lines_id_index_mapper[$line->id] = $pos;
907 
908  //dol_syslog("1 ".$line->desc);
909  //dol_syslog("2 ".$line->product_desc);
910 
911  if ($line->statut == ContratLigne::STATUS_INITIAL) $this->nbofserviceswait++;
912  if ($line->statut == ContratLigne::STATUS_OPEN && (empty($line->date_fin_prevue) || $line->date_fin_prevue >= $now)) $this->nbofservicesopened++;
913  if ($line->statut == ContratLigne::STATUS_OPEN && (!empty($line->date_fin_prevue) && $line->date_fin_prevue < $now)) $this->nbofservicesexpired++;
914  if ($line->statut == ContratLigne::STATUS_CLOSED) $this->nbofservicesclosed++;
915 
916  $total_ttc += $objp->total_ttc; // TODO Not saved into database
917  $total_vat += $objp->total_tva;
918  $total_ht += $objp->total_ht;
919 
920  $i++;
921  $pos++;
922  }
923  $this->db->free($result);
924  } else {
925  dol_syslog(get_class($this)."::Fetch Error when reading lines of contracts linked to products");
926  return -3;
927  }
928 
929  $this->nbofservices = count($this->lines);
930  $this->total_ttc = price2num($total_ttc); // TODO For the moment value is false as value is not stored in database for line linked to products
931  $this->total_tva = price2num($total_vat); // TODO For the moment value is false as value is not stored in database for line linked to products
932  $this->total_ht = price2num($total_ht); // TODO For the moment value is false as value is not stored in database for line linked to products
933 
934  return $this->lines;
935  }
936 
943  public function create($user)
944  {
945  global $conf, $langs, $mysoc;
946 
947  // Check parameters
948  $paramsok = 1;
949  if ($this->commercial_signature_id <= 0)
950  {
951  $langs->load("commercial");
952  $this->error .= $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("SalesRepresentativeSignature"));
953  $paramsok = 0;
954  }
955  if ($this->commercial_suivi_id <= 0)
956  {
957  $langs->load("commercial");
958  $this->error .= ($this->error ? "<br>" : '');
959  $this->error .= $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("SalesRepresentativeFollowUp"));
960  $paramsok = 0;
961  }
962  if (!$paramsok) return -1;
963 
964 
965  $this->db->begin();
966 
967  $now = dol_now();
968 
969  // Insert contract
970  $sql = "INSERT INTO ".MAIN_DB_PREFIX."contrat (datec, fk_soc, fk_user_author, date_contrat,";
971  $sql .= " fk_commercial_signature, fk_commercial_suivi, fk_projet,";
972  $sql .= " ref, entity, note_private, note_public, ref_customer, ref_supplier, ref_ext)";
973  $sql .= " VALUES ('".$this->db->idate($now)."',".$this->socid.",".$user->id;
974  $sql .= ", ".(dol_strlen($this->date_contrat) != 0 ? "'".$this->db->idate($this->date_contrat)."'" : "NULL");
975  $sql .= ",".($this->commercial_signature_id > 0 ? $this->commercial_signature_id : "NULL");
976  $sql .= ",".($this->commercial_suivi_id > 0 ? $this->commercial_suivi_id : "NULL");
977  $sql .= ",".($this->fk_project > 0 ? $this->fk_project : "NULL");
978  $sql .= ", ".(dol_strlen($this->ref) <= 0 ? "null" : "'".$this->db->escape($this->ref)."'");
979  $sql .= ", ".$conf->entity;
980  $sql .= ", ".(!empty($this->note_private) ? ("'".$this->db->escape($this->note_private)."'") : "NULL");
981  $sql .= ", ".(!empty($this->note_public) ? ("'".$this->db->escape($this->note_public)."'") : "NULL");
982  $sql .= ", ".(!empty($this->ref_customer) ? ("'".$this->db->escape($this->ref_customer)."'") : "NULL");
983  $sql .= ", ".(!empty($this->ref_supplier) ? ("'".$this->db->escape($this->ref_supplier)."'") : "NULL");
984  $sql .= ", ".(!empty($this->ref_ext) ? ("'".$this->db->escape($this->ref_ext)."'") : "NULL");
985  $sql .= ")";
986  $resql = $this->db->query($sql);
987 
988  if ($resql)
989  {
990  $error = 0;
991 
992  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."contrat");
993 
994  // Load object modContract
995  $module = (!empty($conf->global->CONTRACT_ADDON) ? $conf->global->CONTRACT_ADDON : 'mod_contract_serpis');
996  if (substr($module, 0, 13) == 'mod_contract_' && substr($module, -3) == 'php')
997  {
998  $module = substr($module, 0, dol_strlen($module) - 4);
999  }
1000  $result = dol_include_once('/core/modules/contract/'.$module.'.php');
1001  if ($result > 0)
1002  {
1003  $modCodeContract = new $module();
1004 
1005  if (!empty($modCodeContract->code_auto)) {
1006  // Force the ref to a draft value if numbering module is an automatic numbering
1007  $sql = 'UPDATE '.MAIN_DB_PREFIX."contrat SET ref='(PROV".$this->id.")' WHERE rowid=".$this->id;
1008  if ($this->db->query($sql))
1009  {
1010  if ($this->id)
1011  {
1012  $this->ref = "(PROV".$this->id.")";
1013  }
1014  }
1015  }
1016  }
1017 
1018  if (!$error)
1019  {
1020  $result = $this->insertExtraFields();
1021  if ($result < 0)
1022  {
1023  $error++;
1024  }
1025  }
1026 
1027  // Insert business contacts ('SALESREPSIGN','contrat')
1028  if (!$error)
1029  {
1030  $result = $this->add_contact($this->commercial_signature_id, 'SALESREPSIGN', 'internal');
1031  if ($result < 0) $error++;
1032  }
1033 
1034  // Insert business contacts ('SALESREPFOLL','contrat')
1035  if (!$error) {
1036  $result = $this->add_contact($this->commercial_suivi_id, 'SALESREPFOLL', 'internal');
1037  if ($result < 0) $error++;
1038  }
1039 
1040  if (!$error) {
1041  if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) // To use new linkedObjectsIds instead of old linked_objects
1042  {
1043  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1044  }
1045 
1046  // Add object linked
1047  if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects))
1048  {
1049  foreach ($this->linked_objects as $origin => $tmp_origin_id)
1050  {
1051  if (is_array($tmp_origin_id)) // New behaviour, if linked_object can have several links per type, so is something like array('contract'=>array(id1, id2, ...))
1052  {
1053  foreach ($tmp_origin_id as $origin_id)
1054  {
1055  $ret = $this->add_object_linked($origin, $origin_id);
1056  if (!$ret)
1057  {
1058  $this->error = $this->db->lasterror();
1059  $error++;
1060  }
1061  }
1062  } else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1063  {
1064  $origin_id = $tmp_origin_id;
1065  $ret = $this->add_object_linked($origin, $origin_id);
1066  if (!$ret)
1067  {
1068  $this->error = $this->db->lasterror();
1069  $error++;
1070  }
1071  }
1072  }
1073  }
1074 
1075  if (!$error && $this->id && !empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN) && !empty($this->origin) && !empty($this->origin_id)) // Get contact from origin object
1076  {
1077  $originforcontact = $this->origin;
1078  $originidforcontact = $this->origin_id;
1079  if ($originforcontact == 'shipping') // shipment and order share the same contacts. If creating from shipment we take data of order
1080  {
1081  require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1082  $exp = new Expedition($this->db);
1083  $exp->fetch($this->origin_id);
1084  $exp->fetchObjectLinked();
1085  if (count($exp->linkedObjectsIds['commande']) > 0)
1086  {
1087  foreach ($exp->linkedObjectsIds['commande'] as $key => $value)
1088  {
1089  $originforcontact = 'commande';
1090  $originidforcontact = $value->id;
1091  break; // We take first one
1092  }
1093  }
1094  }
1095 
1096  $sqlcontact = "SELECT ctc.code, ctc.source, ec.fk_socpeople FROM ".MAIN_DB_PREFIX."element_contact as ec, ".MAIN_DB_PREFIX."c_type_contact as ctc";
1097  $sqlcontact .= " WHERE element_id = ".$originidforcontact." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$this->db->escape($originforcontact)."'";
1098 
1099  $resqlcontact = $this->db->query($sqlcontact);
1100  if ($resqlcontact)
1101  {
1102  while ($objcontact = $this->db->fetch_object($resqlcontact))
1103  {
1104  if ($objcontact->source == 'internal' && in_array($objcontact->code, array('SALESREPSIGN', 'SALESREPFOLL'))) continue; // ignore this, already forced previously
1105 
1106  //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1107  $this->add_contact($objcontact->fk_socpeople, $objcontact->code, $objcontact->source); // May failed because of duplicate key or because code of contact type does not exists for new object
1108  }
1109  } else dol_print_error($resqlcontact);
1110  }
1111  }
1112 
1113  if (!$error)
1114  {
1115  // Call trigger
1116  $result = $this->call_trigger('CONTRACT_CREATE', $user);
1117  if ($result < 0) { $error++; }
1118  // End call triggers
1119 
1120  if (!$error)
1121  {
1122  $this->db->commit();
1123  return $this->id;
1124  } else {
1125  dol_syslog(get_class($this)."::create - 30 - ".$this->error, LOG_ERR);
1126  $this->db->rollback();
1127  return -3;
1128  }
1129  } else {
1130  $this->error = "Failed to add contract";
1131  dol_syslog(get_class($this)."::create - 20 - ".$this->error, LOG_ERR);
1132  $this->db->rollback();
1133  return -2;
1134  }
1135  } else {
1136  $this->error = $langs->trans("UnknownError: ".$this->db->error()." -", LOG_DEBUG);
1137 
1138  $this->db->rollback();
1139  return -1;
1140  }
1141  }
1142 
1143 
1150  public function delete($user)
1151  {
1152  global $conf, $langs;
1153  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1154 
1155  $error = 0;
1156 
1157  $this->db->begin();
1158 
1159  // Call trigger
1160  $result = $this->call_trigger('CONTRACT_DELETE', $user);
1161  if ($result < 0) { $error++; }
1162  // End call triggers
1163 
1164  if (!$error)
1165  {
1166  // Delete linked contacts
1167  $res = $this->delete_linked_contact();
1168  if ($res < 0)
1169  {
1170  dol_syslog(get_class($this)."::delete error", LOG_ERR);
1171  $error++;
1172  }
1173  }
1174 
1175  if (!$error)
1176  {
1177  // Delete linked object
1178  $res = $this->deleteObjectLinked();
1179  if ($res < 0) $error++;
1180  }
1181 
1182  if (!$error)
1183  {
1184  // Delete contratdet_log
1185  /*
1186  $sql = "DELETE cdl";
1187  $sql.= " FROM ".MAIN_DB_PREFIX."contratdet_log as cdl, ".MAIN_DB_PREFIX."contratdet as cd";
1188  $sql.= " WHERE cdl.fk_contratdet=cd.rowid AND cd.fk_contrat=".$this->id;
1189  */
1190  $sql = "SELECT cdl.rowid as cdlrowid ";
1191  $sql .= " FROM ".MAIN_DB_PREFIX."contratdet_log as cdl, ".MAIN_DB_PREFIX."contratdet as cd";
1192  $sql .= " WHERE cdl.fk_contratdet=cd.rowid AND cd.fk_contrat=".$this->id;
1193 
1194  dol_syslog(get_class($this)."::delete contratdet_log", LOG_DEBUG);
1195  $resql = $this->db->query($sql);
1196  if (!$resql)
1197  {
1198  $this->error = $this->db->error();
1199  $error++;
1200  }
1201  $numressql = $this->db->num_rows($resql);
1202  if (!$error && $numressql)
1203  {
1204  $tab_resql = array();
1205  for ($i = 0; $i < $numressql; $i++)
1206  {
1207  $objresql = $this->db->fetch_object($resql);
1208  $tab_resql[] = $objresql->cdlrowid;
1209  }
1210  $this->db->free($resql);
1211 
1212  $sql = "DELETE FROM ".MAIN_DB_PREFIX."contratdet_log ";
1213  $sql .= " WHERE ".MAIN_DB_PREFIX."contratdet_log.rowid IN (".implode(",", $tab_resql).")";
1214 
1215  dol_syslog(get_class($this)."::delete contratdet_log", LOG_DEBUG);
1216  $resql = $this->db->query($sql);
1217  if (!$resql)
1218  {
1219  $this->error = $this->db->error();
1220  $error++;
1221  }
1222  }
1223  }
1224 
1225  // Delete lines
1226  if (!$error) {
1227  // Delete contratdet extrafields
1228  $main = MAIN_DB_PREFIX.'contratdet';
1229  $ef = $main."_extrafields";
1230  $sql = "DELETE FROM $ef WHERE fk_object IN (SELECT rowid FROM $main WHERE fk_contrat = ".$this->id.")";
1231 
1232  dol_syslog(get_class($this)."::delete contratdet_extrafields", LOG_DEBUG);
1233  $resql = $this->db->query($sql);
1234  if (!$resql)
1235  {
1236  $this->error = $this->db->error();
1237  $error++;
1238  }
1239  }
1240 
1241  if (!$error)
1242  {
1243  // Delete contratdet
1244  $sql = "DELETE FROM ".MAIN_DB_PREFIX."contratdet";
1245  $sql .= " WHERE fk_contrat=".$this->id;
1246 
1247  dol_syslog(get_class($this)."::delete contratdet", LOG_DEBUG);
1248  $resql = $this->db->query($sql);
1249  if (!$resql)
1250  {
1251  $this->error = $this->db->error();
1252  $error++;
1253  }
1254  }
1255 
1256  // Delete llx_ecm_files
1257  if (!$error) {
1258  $sql = 'DELETE FROM '.MAIN_DB_PREFIX."ecm_files WHERE src_object_type = '".$this->db->escape($this->table_element.(empty($this->module) ? '' : '@'.$this->module))."' AND src_object_id = ".$this->id;
1259  $resql = $this->db->query($sql);
1260  if (!$resql)
1261  {
1262  $this->error = $this->db->lasterror();
1263  $this->errors[] = $this->error;
1264  $error++;
1265  }
1266  }
1267 
1268  // Delete contract
1269  if (!$error)
1270  {
1271  $sql = "DELETE FROM ".MAIN_DB_PREFIX."contrat";
1272  $sql .= " WHERE rowid=".$this->id;
1273 
1274  dol_syslog(get_class($this)."::delete contrat", LOG_DEBUG);
1275  $resql = $this->db->query($sql);
1276  if (!$resql)
1277  {
1278  $this->error = $this->db->error();
1279  $error++;
1280  }
1281  }
1282 
1283  // Removed extrafields
1284  if (!$error) {
1285  $result = $this->deleteExtraFields();
1286  if ($result < 0)
1287  {
1288  $error++;
1289  dol_syslog(get_class($this)."::delete error -3 ".$this->error, LOG_ERR);
1290  }
1291  }
1292 
1293  if (!$error)
1294  {
1295  // We remove directory
1296  $ref = dol_sanitizeFileName($this->ref);
1297  if ($conf->contrat->dir_output)
1298  {
1299  $dir = $conf->contrat->multidir_output[$this->entity]."/".$ref;
1300  if (file_exists($dir))
1301  {
1302  $res = @dol_delete_dir_recursive($dir);
1303  if (!$res)
1304  {
1305  $this->error = 'ErrorFailToDeleteDir';
1306  $error++;
1307  }
1308  }
1309  }
1310  }
1311 
1312  if (!$error)
1313  {
1314  $this->db->commit();
1315  return 1;
1316  } else {
1317  $this->error = $this->db->lasterror();
1318  $this->db->rollback();
1319  return -1;
1320  }
1321  }
1322 
1330  public function update($user, $notrigger = 0)
1331  {
1332  global $conf, $langs;
1333  $error = 0;
1334 
1335  // Clean parameters
1336  if (empty($this->fk_commercial_signature) && $this->commercial_signature_id > 0) $this->fk_commercial_signature = $this->commercial_signature_id;
1337  if (empty($this->fk_commercial_suivi) && $this->commercial_suivi_id > 0) $this->fk_commercial_suivi = $this->commercial_suivi_id;
1338  if (empty($this->fk_soc) && $this->socid > 0) $this->fk_soc = (int) $this->socid;
1339  if (empty($this->fk_project) && $this->projet > 0) $this->fk_project = (int) $this->projet;
1340 
1341  if (isset($this->ref)) $this->ref = trim($this->ref);
1342  if (isset($this->ref_customer)) $this->ref_customer = trim($this->ref_customer);
1343  if (isset($this->ref_supplier)) $this->ref_supplier = trim($this->ref_supplier);
1344  if (isset($this->ref_ext)) $this->ref_ext = trim($this->ref_ext);
1345  if (isset($this->entity)) $this->entity = (int) $this->entity;
1346  if (isset($this->statut)) $this->statut = (int) $this->statut;
1347  if (isset($this->fk_soc)) $this->fk_soc = (int) $this->fk_soc;
1348  if (isset($this->fk_commercial_signature)) $this->fk_commercial_signature = trim($this->fk_commercial_signature);
1349  if (isset($this->fk_commercial_suivi)) $this->fk_commercial_suivi = trim($this->fk_commercial_suivi);
1350  if (isset($this->note_private)) $this->note_private = trim($this->note_private);
1351  if (isset($this->note_public)) $this->note_public = trim($this->note_public);
1352  if (isset($this->import_key)) $this->import_key = trim($this->import_key);
1353  //if (isset($this->extraparams)) $this->extraparams=trim($this->extraparams);
1354 
1355  // Check parameters
1356  // Put here code to add a control on parameters values
1357 
1358  // Update request
1359  $sql = "UPDATE ".MAIN_DB_PREFIX."contrat SET";
1360  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1361  $sql .= " ref_customer=".(isset($this->ref_customer) ? "'".$this->db->escape($this->ref_customer)."'" : "null").",";
1362  $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1363  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1364  $sql .= " entity=".$conf->entity.",";
1365  $sql .= " date_contrat=".(dol_strlen($this->date_contrat) != 0 ? "'".$this->db->idate($this->date_contrat)."'" : 'null').",";
1366  $sql .= " statut=".(isset($this->statut) ? $this->statut : "null").",";
1367  $sql .= " fk_soc=".($this->fk_soc > 0 ? $this->fk_soc : "null").",";
1368  $sql .= " fk_projet=".($this->fk_project > 0 ? $this->fk_project : "null").",";
1369  $sql .= " fk_commercial_signature=".(isset($this->fk_commercial_signature) ? $this->fk_commercial_signature : "null").",";
1370  $sql .= " fk_commercial_suivi=".(isset($this->fk_commercial_suivi) ? $this->fk_commercial_suivi : "null").",";
1371  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1372  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1373  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null")."";
1374  //$sql.= " extraparams=".(isset($this->extraparams)?"'".$this->db->escape($this->extraparams)."'":"null")."";
1375  $sql .= " WHERE rowid=".$this->id;
1376 
1377  $this->db->begin();
1378 
1379  $resql = $this->db->query($sql);
1380  if (!$resql) { $error++; $this->errors[] = "Error ".$this->db->lasterror(); }
1381 
1382  if (!$error)
1383  {
1384  $result = $this->insertExtraFields();
1385  if ($result < 0)
1386  {
1387  $error++;
1388  }
1389  }
1390 
1391  if (!$error && !$notrigger)
1392  {
1393  // Call triggers
1394  $result = $this->call_trigger('CONTRACT_MODIFY', $user);
1395  if ($result < 0) { $error++; }
1396  // End call triggers
1397  }
1398 
1399  // Commit or rollback
1400  if ($error)
1401  {
1402  foreach ($this->errors as $errmsg)
1403  {
1404  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1405  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1406  }
1407  $this->db->rollback();
1408  return -1 * $error;
1409  } else {
1410  $this->db->commit();
1411  return 1;
1412  }
1413  }
1414 
1415 
1439  public function addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2, $fk_product, $remise_percent, $date_start, $date_end, $price_base_type = 'HT', $pu_ttc = 0.0, $info_bits = 0, $fk_fournprice = null, $pa_ht = 0, $array_options = 0, $fk_unit = null, $rang = 0)
1440  {
1441  global $user, $langs, $conf, $mysoc;
1442  $error = 0;
1443 
1444  dol_syslog(get_class($this)."::addline $desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2, $fk_product, $remise_percent, $date_start, $date_end, $price_base_type, $pu_ttc, $info_bits, $rang");
1445 
1446  // Check parameters
1447  if ($fk_product <= 0 && empty($desc))
1448  {
1449  $this->error = "ErrorDescRequiredForFreeProductLines";
1450  return -1;
1451  }
1452 
1453  if ($this->statut >= 0)
1454  {
1455  // Clean parameters
1456  $pu_ht = price2num($pu_ht);
1457  $pu_ttc = price2num($pu_ttc);
1458  $pa_ht = price2num($pa_ht);
1459 
1460  // Clean vat code
1461  $reg = array();
1462  $vat_src_code = '';
1463  if (preg_match('/\((.*)\)/', $txtva, $reg))
1464  {
1465  $vat_src_code = $reg[1];
1466  $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
1467  }
1468  $txtva = price2num($txtva);
1469  $txlocaltax1 = price2num($txlocaltax1);
1470  $txlocaltax2 = price2num($txlocaltax2);
1471 
1472  $remise_percent = price2num($remise_percent);
1473  $qty = price2num($qty);
1474  if (empty($qty)) $qty = 1;
1475  if (empty($info_bits)) $info_bits = 0;
1476  if (empty($pu_ht) || !is_numeric($pu_ht)) $pu_ht = 0;
1477  if (empty($pu_ttc)) $pu_ttc = 0;
1478  if (empty($txtva) || !is_numeric($txtva)) $txtva = 0;
1479  if (empty($txlocaltax1) || !is_numeric($txlocaltax1)) $txlocaltax1 = 0;
1480  if (empty($txlocaltax2) || !is_numeric($txlocaltax2)) $txlocaltax2 = 0;
1481 
1482  if ($price_base_type == 'HT')
1483  {
1484  $pu = $pu_ht;
1485  } else {
1486  $pu = $pu_ttc;
1487  }
1488 
1489  // Check parameters
1490  if (empty($remise_percent)) $remise_percent = 0;
1491 
1492  if ($date_start && $date_end && $date_start > $date_end) {
1493  $langs->load("errors");
1494  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1495  return -1;
1496  }
1497 
1498  $this->db->begin();
1499 
1500  $localtaxes_type = getLocalTaxesFromRate($txtva.($vat_src_code ? ' ('.$vat_src_code.')' : ''), 0, $this->societe, $mysoc);
1501 
1502  // Calcul du total TTC et de la TVA pour la ligne a partir de
1503  // qty, pu, remise_percent et txtva
1504  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1505  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1506 
1507  $tabprice = calcul_price_total($qty, $pu, $remise_percent, $txtva, $txlocaltax1, $txlocaltax2, 0, $price_base_type, $info_bits, 1, $mysoc, $localtaxes_type);
1508  $total_ht = $tabprice[0];
1509  $total_tva = $tabprice[1];
1510  $total_ttc = $tabprice[2];
1511  $total_localtax1 = $tabprice[9];
1512  $total_localtax2 = $tabprice[10];
1513 
1514  $localtax1_type = $localtaxes_type[0];
1515  $localtax2_type = $localtaxes_type[2];
1516 
1517  // TODO A virer
1518  // Anciens indicateurs: $price, $remise (a ne plus utiliser)
1519  $remise = 0;
1520  $price = price2num(round($pu_ht, 2));
1521  if (dol_strlen($remise_percent) > 0)
1522  {
1523  $remise = round(($pu_ht * $remise_percent / 100), 2);
1524  $price = $pu_ht - $remise;
1525  }
1526 
1527  if (empty($pa_ht)) $pa_ht = 0;
1528 
1529 
1530  // if buy price not defined, define buyprice as configured in margin admin
1531  if ($this->pa_ht == 0)
1532  {
1533  if (($result = $this->defineBuyPrice($pu_ht, $remise_percent, $fk_product)) < 0)
1534  {
1535  return $result;
1536  } else {
1537  $pa_ht = $result;
1538  }
1539  }
1540 
1541  // Insertion dans la base
1542  $sql = "INSERT INTO ".MAIN_DB_PREFIX."contratdet";
1543  $sql .= " (fk_contrat, label, description, fk_product, qty, tva_tx, vat_src_code,";
1544  $sql .= " localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice,";
1545  $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc,";
1546  $sql .= " info_bits,";
1547  $sql .= " price_ht, remise, fk_product_fournisseur_price, buy_price_ht";
1548  if ($date_start > 0) { $sql .= ",date_ouverture_prevue"; }
1549  if ($date_end > 0) { $sql .= ",date_fin_validite"; }
1550  $sql .= ", fk_unit";
1551  $sql .= ") VALUES (";
1552  $sql .= $this->id.", '', '".$this->db->escape($desc)."',";
1553  $sql .= ($fk_product > 0 ? $fk_product : "null").",";
1554  $sql .= " ".$qty.",";
1555  $sql .= " ".$txtva.",";
1556  $sql .= " ".($vat_src_code ? "'".$this->db->escape($vat_src_code)."'" : "null").",";
1557  $sql .= " ".$txlocaltax1.",";
1558  $sql .= " ".$txlocaltax2.",";
1559  $sql .= " '".$this->db->escape($localtax1_type)."',";
1560  $sql .= " '".$this->db->escape($localtax2_type)."',";
1561  $sql .= " ".price2num($remise_percent).",";
1562  $sql .= " ".price2num($pu_ht).",";
1563  $sql .= " ".price2num($total_ht).",".price2num($total_tva).",".price2num($total_localtax1).",".price2num($total_localtax2).",".price2num($total_ttc).",";
1564  $sql .= " '".$this->db->escape($info_bits)."',";
1565  $sql .= " ".price2num($price).",".price2num($remise).",";
1566  if (isset($fk_fournprice)) $sql .= ' '.$fk_fournprice.',';
1567  else $sql .= ' null,';
1568  if (isset($pa_ht)) $sql .= ' '.price2num($pa_ht);
1569  else $sql .= ' null';
1570  if ($date_start > 0) { $sql .= ",'".$this->db->idate($date_start)."'"; }
1571  if ($date_end > 0) { $sql .= ",'".$this->db->idate($date_end)."'"; }
1572  $sql .= ", ".($fk_unit ? "'".$this->db->escape($fk_unit)."'" : "null");
1573  $sql .= ")";
1574 
1575  $resql = $this->db->query($sql);
1576  if ($resql)
1577  {
1578  $contractlineid = $this->db->last_insert_id(MAIN_DB_PREFIX."contratdet");
1579 
1580  if (!$error)
1581  {
1582  $contractline = new ContratLigne($this->db);
1583  $contractline->array_options = $array_options;
1584  $contractline->id = $contractlineid;
1585  $result = $contractline->insertExtraFields();
1586  if ($result < 0)
1587  {
1588  $this->error[] = $contractline->error;
1589  $error++;
1590  }
1591  }
1592 
1593  if (empty($error)) {
1594  // Call trigger
1595  $result = $this->call_trigger('LINECONTRACT_INSERT', $user);
1596  if ($result < 0)
1597  {
1598  $error++;
1599  }
1600  // End call triggers
1601  }
1602 
1603  if ($error)
1604  {
1605  $this->db->rollback();
1606  return -1;
1607  } else {
1608  $this->db->commit();
1609  return $contractlineid;
1610  }
1611  } else {
1612  $this->db->rollback();
1613  $this->error = $this->db->error()." sql=".$sql;
1614  return -1;
1615  }
1616  } else {
1617  dol_syslog(get_class($this)."::addline ErrorTryToAddLineOnValidatedContract", LOG_ERR);
1618  return -2;
1619  }
1620  }
1621 
1645  public function updateline($rowid, $desc, $pu, $qty, $remise_percent, $date_start, $date_end, $tvatx, $localtax1tx = 0.0, $localtax2tx = 0.0, $date_debut_reel = '', $date_fin_reel = '', $price_base_type = 'HT', $info_bits = 0, $fk_fournprice = null, $pa_ht = 0, $array_options = 0, $fk_unit = null)
1646  {
1647  global $user, $conf, $langs, $mysoc;
1648 
1649  $error = 0;
1650 
1651  // Clean parameters
1652  $qty = trim($qty);
1653  $desc = trim($desc);
1654  $desc = trim($desc);
1655  $price = price2num($pu);
1656  $tvatx = price2num($tvatx);
1657  $localtax1tx = price2num($localtax1tx);
1658  $localtax2tx = price2num($localtax2tx);
1659  $pa_ht = price2num($pa_ht);
1660  if (empty($fk_fournprice)) $fk_fournprice = 0;
1661 
1662  $subprice = $price;
1663  $remise = 0;
1664  if (dol_strlen($remise_percent) > 0)
1665  {
1666  $remise = round(($pu * $remise_percent / 100), 2);
1667  $price = $pu - $remise;
1668  } else {
1669  $remise_percent = 0;
1670  }
1671 
1672  if ($date_start && $date_end && $date_start > $date_end) {
1673  $langs->load("errors");
1674  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1675  return -1;
1676  }
1677 
1678  dol_syslog(get_class($this)."::updateline $rowid, $desc, $pu, $qty, $remise_percent, $date_start, $date_end, $date_debut_reel, $date_fin_reel, $tvatx, $localtax1tx, $localtax2tx, $price_base_type, $info_bits");
1679 
1680  $this->db->begin();
1681 
1682  // Calcul du total TTC et de la TVA pour la ligne a partir de
1683  // qty, pu, remise_percent et tvatx
1684  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1685  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1686 
1687  $localtaxes_type = getLocalTaxesFromRate($tvatx, 0, $this->societe, $mysoc);
1688  $tvatx = preg_replace('/\s*\(.*\)/', '', $tvatx); // Remove code into vatrate.
1689 
1690  $tabprice = calcul_price_total($qty, $pu, $remise_percent, $tvatx, $localtax1tx, $localtax2tx, 0, $price_base_type, $info_bits, 1, $mysoc, $localtaxes_type);
1691  $total_ht = $tabprice[0];
1692  $total_tva = $tabprice[1];
1693  $total_ttc = $tabprice[2];
1694  $total_localtax1 = $tabprice[9];
1695  $total_localtax2 = $tabprice[10];
1696 
1697  $localtax1_type = $localtaxes_type[0];
1698  $localtax2_type = $localtaxes_type[2];
1699 
1700  // TODO A virer
1701  // Anciens indicateurs: $price, $remise (a ne plus utiliser)
1702  $remise = 0;
1703  $price = price2num(round($pu, 2));
1704  if (dol_strlen($remise_percent) > 0)
1705  {
1706  $remise = round(($pu * $remise_percent / 100), 2);
1707  $price = $pu - $remise;
1708  }
1709 
1710  if (empty($pa_ht)) $pa_ht = 0;
1711 
1712  // if buy price not defined, define buyprice as configured in margin admin
1713  if ($this->pa_ht == 0)
1714  {
1715  if (($result = $this->defineBuyPrice($pu, $remise_percent)) < 0)
1716  {
1717  return $result;
1718  } else {
1719  $pa_ht = $result;
1720  }
1721  }
1722 
1723  $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet set description='".$this->db->escape($desc)."'";
1724  $sql .= ",price_ht='".price2num($price)."'";
1725  $sql .= ",subprice='".price2num($subprice)."'";
1726  $sql .= ",remise='".price2num($remise)."'";
1727  $sql .= ",remise_percent='".price2num($remise_percent)."'";
1728  $sql .= ",qty='".$qty."'";
1729  $sql .= ",tva_tx='".price2num($tvatx)."'";
1730  $sql .= ",localtax1_tx='".price2num($localtax1tx)."'";
1731  $sql .= ",localtax2_tx='".price2num($localtax2tx)."'";
1732  $sql .= ",localtax1_type='".$this->db->escape($localtax1_type)."'";
1733  $sql .= ",localtax2_type='".$this->db->escape($localtax2_type)."'";
1734  $sql .= ", total_ht='".price2num($total_ht)."'";
1735  $sql .= ", total_tva='".price2num($total_tva)."'";
1736  $sql .= ", total_localtax1='".price2num($total_localtax1)."'";
1737  $sql .= ", total_localtax2='".price2num($total_localtax2)."'";
1738  $sql .= ", total_ttc='".price2num($total_ttc)."'";
1739  $sql .= ", fk_product_fournisseur_price=".($fk_fournprice > 0 ? $fk_fournprice : "null");
1740  $sql .= ", buy_price_ht='".price2num($pa_ht)."'";
1741  if ($date_start > 0) { $sql .= ",date_ouverture_prevue='".$this->db->idate($date_start)."'"; } else { $sql .= ",date_ouverture_prevue=null"; }
1742  if ($date_end > 0) { $sql .= ",date_fin_validite='".$this->db->idate($date_end)."'"; } else { $sql .= ",date_fin_validite=null"; }
1743  if ($date_debut_reel > 0) { $sql .= ",date_ouverture='".$this->db->idate($date_debut_reel)."'"; } else { $sql .= ",date_ouverture=null"; }
1744  if ($date_fin_reel > 0) { $sql .= ",date_cloture='".$this->db->idate($date_fin_reel)."'"; } else { $sql .= ",date_cloture=null"; }
1745  $sql .= ", fk_unit=".($fk_unit ? "'".$this->db->escape($fk_unit)."'" : "null");
1746  $sql .= " WHERE rowid = ".$rowid;
1747 
1748  dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
1749  $result = $this->db->query($sql);
1750  if ($result)
1751  {
1752  $result = $this->update_statut($user);
1753  if ($result >= 0)
1754  {
1755  if (is_array($array_options) && count($array_options) > 0) // For avoid conflicts if trigger used
1756  {
1757  $contractline = new ContratLigne($this->db);
1758  $contractline->fetch($rowid);
1759  $contractline->fetch_optionals();
1760 
1761  // We replace values in $contractline->array_options only for entries defined into $array_options
1762  foreach ($array_options as $key => $value) {
1763  $contractline->array_options[$key] = $array_options[$key];
1764  }
1765 
1766  $result = $contractline->insertExtraFields();
1767  if ($result < 0)
1768  {
1769  $this->error[] = $contractline->error;
1770  $error++;
1771  }
1772  }
1773 
1774  if (empty($error)) {
1775  // Call trigger
1776  $result = $this->call_trigger('LINECONTRACT_UPDATE', $user);
1777  if ($result < 0)
1778  {
1779  $this->db->rollback();
1780  return -3;
1781  }
1782  // End call triggers
1783 
1784  $this->db->commit();
1785  return 1;
1786  }
1787  } else {
1788  $this->db->rollback();
1789  dol_syslog(get_class($this)."::updateline Erreur -2");
1790  return -2;
1791  }
1792  } else {
1793  $this->db->rollback();
1794  $this->error = $this->db->error();
1795  dol_syslog(get_class($this)."::updateline Erreur -1");
1796  return -1;
1797  }
1798  }
1799 
1807  public function deleteline($idline, User $user)
1808  {
1809  global $conf, $langs;
1810 
1811  $error = 0;
1812 
1813  if ($this->statut >= 0)
1814  {
1815  // Call trigger
1816  $result = $this->call_trigger('LINECONTRACT_DELETE', $user);
1817  if ($result < 0) return -1;
1818  // End call triggers
1819 
1820  $this->db->begin();
1821 
1822  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element_line;
1823  $sql .= " WHERE rowid=".$idline;
1824 
1825  dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
1826  $resql = $this->db->query($sql);
1827  if (!$resql)
1828  {
1829  $this->error = "Error ".$this->db->lasterror();
1830  $error++;
1831  }
1832 
1833  if (!$error) {
1834  // Remove extrafields
1835  $contractline = new ContratLigne($this->db);
1836  $contractline->id = $idline;
1837  $result = $contractline->deleteExtraFields();
1838  if ($result < 0)
1839  {
1840  $error++;
1841  $this->error = "Error ".get_class($this)."::deleteline deleteExtraFields error -4 ".$contractline->error;
1842  }
1843  }
1844 
1845  if (empty($error)) {
1846  $this->db->commit();
1847  return 1;
1848  } else {
1849  dol_syslog(get_class($this)."::deleteline ERROR:".$this->error, LOG_ERR);
1850  $this->db->rollback();
1851  return -1;
1852  }
1853  } else {
1854  $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
1855  return -2;
1856  }
1857  }
1858 
1859 
1860  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1868  public function update_statut($user)
1869  {
1870  // phpcs:enable
1871  dol_syslog(__METHOD__." is deprecated", LOG_WARNING);
1872 
1873  // If draft, we keep it (should not happen)
1874  if ($this->statut == 0) return 1;
1875 
1876  // Load $this->lines array
1877  // $this->fetch_lines();
1878 
1879  // $newstatut=1;
1880  // foreach($this->lines as $key => $contractline)
1881  // {
1882  // // if ($contractline) // Loop on each service
1883  // }
1884 
1885  return 1;
1886  }
1887 
1888 
1895  public function getLibStatut($mode)
1896  {
1897  return $this->LibStatut($this->statut, $mode);
1898  }
1899 
1900  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1908  public function LibStatut($status, $mode)
1909  {
1910  // phpcs:enable
1911  global $langs;
1912 
1913  if (empty($this->labelStatus) || empty($this->labelStatusShort))
1914  {
1915  global $langs;
1916  $langs->load("contracts");
1917  $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('ContractStatusDraft');
1918  $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('ContractStatusValidated');
1919  $this->labelStatus[self::STATUS_CLOSED] = $langs->transnoentitiesnoconv('ContractStatusClosed');
1920  $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('ContractStatusDraft');
1921  $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('ContractStatusValidated');
1922  $this->labelStatusShort[self::STATUS_CLOSED] = $langs->transnoentitiesnoconv('ContractStatusClosed');
1923  }
1924 
1925  $statusType = 'status'.$status;
1926  if ($status == self::STATUS_VALIDATED) $statusType = 'status6';
1927 
1928  if ($mode == 4 || $mode == 6 || $mode == 7)
1929  {
1930  $text = '';
1931  if ($mode == 4) {
1932  $text = '<span class="hideonsmartphone">';
1933  $text .= ($this->nbofserviceswait + $this->nbofservicesopened + $this->nbofservicesexpired + $this->nbofservicesclosed);
1934  $text .= ' '.$langs->trans("Services");
1935  $text .= ': &nbsp; &nbsp; ';
1936  $text .= '</span>';
1937  }
1938  $text .= ($mode == 7 ? '<span class="nowraponall">' : '');
1939  $text .= ($mode != 7 || $this->nbofserviceswait > 0) ? ($this->nbofserviceswait.ContratLigne::LibStatut(0, 3, -1, 'class="marginleft2"')).(($mode != 7 || $this->nbofservicesopened || $this->nbofservicesexpired || $this->nbofservicesclosed) ? ' &nbsp; ' : '') : '';
1940  $text .= ($mode == 7 ? '</span><span class="nowraponall">' : '');
1941  $text .= ($mode != 7 || $this->nbofservicesopened > 0) ? ($this->nbofservicesopened.ContratLigne::LibStatut(4, 3, 0, 'class="marginleft2"')).(($mode != 7 || $this->nbofservicesexpired || $this->nbofservicesclosed) ? ' &nbsp; ' : '') : '';
1942  $text .= ($mode == 7 ? '</span><span class="nowraponall">' : '');
1943  $text .= ($mode != 7 || $this->nbofservicesexpired > 0) ? ($this->nbofservicesexpired.ContratLigne::LibStatut(4, 3, 1, 'class="marginleft2"')).(($mode != 7 || $this->nbofservicesclosed) ? ' &nbsp; ' : '') : '';
1944  $text .= ($mode == 7 ? '</span><span class="nowraponall">' : '');
1945  $text .= ($mode != 7 || $this->nbofservicesclosed > 0) ? ($this->nbofservicesclosed.ContratLigne::LibStatut(5, 3, -1, 'class="marginleft2"')) : '';
1946  $text .= ($mode == 7 ? '</span>' : '');
1947  return $text;
1948  } else {
1949  return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
1950  }
1951  }
1952 
1953 
1963  public function getNomUrl($withpicto = 0, $maxlength = 0, $notooltip = 0, $save_lastsearch_value = -1)
1964  {
1965  global $conf, $langs, $user, $hookmanager;
1966 
1967  $result = '';
1968 
1969  $url = DOL_URL_ROOT.'/contrat/card.php?id='.$this->id;
1970 
1971  //if ($option !== 'nolink')
1972  //{
1973  // Add param to save lastsearch_values or not
1974  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1975  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) $add_save_lastsearch_values = 1;
1976  if ($add_save_lastsearch_values) $url .= '&save_lastsearch_values=1';
1977  //}
1978 
1979  $label = '';
1980 
1981  if ($user->rights->contrat->lire) {
1982  $label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Contract").'</u>';
1983  /* Status of a contract is status of all services, so disabled
1984  if (isset($this->statut)) {
1985  $label .= ' '.$this->getLibStatut(5);
1986  }*/
1987  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.($this->ref ? $this->ref : $this->id);
1988  $label .= '<br><b>'.$langs->trans('RefCustomer').':</b> '.($this->ref_customer ? $this->ref_customer : $this->ref_client);
1989  $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
1990  if (!empty($this->total_ht)) {
1991  $label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
1992  }
1993  if (!empty($this->total_tva)) {
1994  $label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
1995  }
1996  if (!empty($this->total_ttc)) {
1997  $label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
1998  }
1999  }
2000 
2001  $linkclose = '';
2002  if (empty($notooltip) && $user->rights->contrat->lire)
2003  {
2004  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER))
2005  {
2006  $label = $langs->trans("ShowOrder");
2007  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2008  }
2009  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
2010  $linkclose .= ' class="classfortooltip"';
2011  }
2012 
2013  $linkstart = '<a href="'.$url.'"';
2014  $linkstart .= $linkclose.'>';
2015  $linkend = '</a>';
2016 
2017  $result .= $linkstart;
2018  if ($withpicto) $result .= img_object(($notooltip ? '' : $label), $this->picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
2019  if ($withpicto != 2) $result .= ($this->ref ? $this->ref : $this->id);
2020  $result .= $linkend;
2021 
2022  global $action;
2023  $hookmanager->initHooks(array('contractdao'));
2024  $parameters = array('id'=>$this->id, 'getnomurl'=>$result);
2025  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2026  if ($reshook > 0) {
2027  $result = $hookmanager->resPrint;
2028  } else {
2029  $result .= $hookmanager->resPrint;
2030  }
2031 
2032  return $result;
2033  }
2034 
2041  public function info($id)
2042  {
2043  $sql = "SELECT c.rowid, c.ref, c.datec,";
2044  $sql .= " c.tms as date_modification,";
2045  $sql .= " fk_user_author";
2046  $sql .= " FROM ".MAIN_DB_PREFIX."contrat as c";
2047  $sql .= " WHERE c.rowid = ".((int) $id);
2048 
2049  $result = $this->db->query($sql);
2050  if ($result)
2051  {
2052  if ($this->db->num_rows($result))
2053  {
2054  $obj = $this->db->fetch_object($result);
2055 
2056  $this->id = $obj->rowid;
2057 
2058  if ($obj->fk_user_author) {
2059  $cuser = new User($this->db);
2060  $cuser->fetch($obj->fk_user_author);
2061  $this->user_creation = $cuser;
2062  }
2063 
2064  $this->ref = (!$obj->ref) ? $obj->rowid : $obj->ref;
2065  $this->date_creation = $this->db->jdate($obj->datec);
2066  $this->date_modification = $this->db->jdate($obj->date_modification);
2067  }
2068 
2069  $this->db->free($result);
2070  } else {
2071  dol_print_error($this->db);
2072  }
2073  }
2074 
2075  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2082  public function array_detail($status = -1)
2083  {
2084  // phpcs:enable
2085  $tab = array();
2086 
2087  $sql = "SELECT cd.rowid";
2088  $sql .= " FROM ".MAIN_DB_PREFIX."contratdet as cd";
2089  $sql .= " WHERE fk_contrat =".$this->id;
2090  if ($status >= 0) $sql .= " AND statut = ".$status;
2091 
2092  dol_syslog(get_class($this)."::array_detail()", LOG_DEBUG);
2093  $resql = $this->db->query($sql);
2094  if ($resql)
2095  {
2096  $num = $this->db->num_rows($resql);
2097  $i = 0;
2098  while ($i < $num)
2099  {
2100  $obj = $this->db->fetch_object($resql);
2101  $tab[$i] = $obj->rowid;
2102  $i++;
2103  }
2104  return $tab;
2105  } else {
2106  $this->error = $this->db->error();
2107  return -1;
2108  }
2109  }
2110 
2117  public function getListOfContracts($option = 'all')
2118  {
2119  $tab = array();
2120 
2121  $sql = "SELECT c.rowid, c.ref";
2122  $sql .= " FROM ".MAIN_DB_PREFIX."contrat as c";
2123  $sql .= " WHERE fk_soc =".$this->socid;
2124  if ($option == 'others') $sql .= " AND c.rowid != ".$this->id;
2125 
2126  dol_syslog(get_class($this)."::getOtherContracts()", LOG_DEBUG);
2127  $resql = $this->db->query($sql);
2128  if ($resql)
2129  {
2130  $num = $this->db->num_rows($resql);
2131  $i = 0;
2132  while ($i < $num)
2133  {
2134  $obj = $this->db->fetch_object($resql);
2135  $contrat = new Contrat($this->db);
2136  $contrat->fetch($obj->rowid);
2137  $tab[] = $contrat;
2138  $i++;
2139  }
2140  return $tab;
2141  } else {
2142  $this->error = $this->db->error();
2143  return -1;
2144  }
2145  }
2146 
2147 
2148  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2156  public function load_board($user, $mode)
2157  {
2158  // phpcs:enable
2159  global $conf, $langs;
2160 
2161  $this->from = " FROM ".MAIN_DB_PREFIX."contrat as c";
2162  $this->from .= ", ".MAIN_DB_PREFIX."contratdet as cd";
2163  $this->from .= ", ".MAIN_DB_PREFIX."societe as s";
2164  if (!$user->rights->societe->client->voir && !$user->socid) $this->from .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2165 
2166  if ($mode == 'inactive')
2167  {
2168  $sql = "SELECT cd.rowid, cd.date_ouverture_prevue as datefin";
2169  $sql .= $this->from;
2170  $sql .= " WHERE c.statut = 1";
2171  $sql .= " AND c.rowid = cd.fk_contrat";
2172  $sql .= " AND cd.statut = 0";
2173  } elseif ($mode == 'expired')
2174  {
2175  $sql = "SELECT cd.rowid, cd.date_fin_validite as datefin";
2176  $sql .= $this->from;
2177  $sql .= " WHERE c.statut = 1";
2178  $sql .= " AND c.rowid = cd.fk_contrat";
2179  $sql .= " AND cd.statut = 4";
2180  $sql .= " AND cd.date_fin_validite < '".$this->db->idate(dol_now())."'";
2181  } elseif ($mode == 'active')
2182  {
2183  $sql = "SELECT cd.rowid, cd.date_fin_validite as datefin";
2184  $sql .= $this->from;
2185  $sql .= " WHERE c.statut = 1";
2186  $sql .= " AND c.rowid = cd.fk_contrat";
2187  $sql .= " AND cd.statut = 4";
2188  //$datetouse = dol_now();
2189  //$sql.= " AND cd.date_fin_validite < '".$this->db->idate($datetouse)."'";
2190  }
2191  $sql .= " AND c.fk_soc = s.rowid";
2192  $sql .= " AND c.entity = ".$conf->entity;
2193  if ($user->socid) $sql .= " AND c.fk_soc = ".$user->socid;
2194  if (!$user->rights->societe->client->voir && !$user->socid) $sql .= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = ".$user->id;
2195 
2196  $resql = $this->db->query($sql);
2197  if ($resql)
2198  {
2199  $langs->load("contracts");
2200  $now = dol_now();
2201 
2202  if ($mode == 'inactive') {
2203  $warning_delay = $conf->contrat->services->inactifs->warning_delay;
2204  $label = $langs->trans("BoardNotActivatedServices");
2205  $labelShort = $langs->trans("BoardNotActivatedServicesShort");
2206  $url = DOL_URL_ROOT.'/contrat/services_list.php?mainmenu=commercial&leftmenu=contracts&mode=0&sortfield=cd.date_fin_validite&sortorder=asc';
2207  } elseif ($mode == 'expired') {
2208  $warning_delay = $conf->contrat->services->expires->warning_delay;
2209  $url = DOL_URL_ROOT.'/contrat/services_list.php?mainmenu=commercial&leftmenu=contracts&mode=4&filter=expired&sortfield=cd.date_fin_validite&sortorder=asc';
2210  $label = $langs->trans("BoardExpiredServices");
2211  $labelShort = $langs->trans("BoardExpiredServicesShort");
2212  } else {
2213  $warning_delay = $conf->contrat->services->expires->warning_delay;
2214  $url = DOL_URL_ROOT.'/contrat/services_list.php?mainmenu=commercial&leftmenu=contracts&mode=4&sortfield=cd.date_fin_validite&sortorder=asc';
2215  //$url.= '&op2day='.$arraydatetouse['mday'].'&op2month='.$arraydatetouse['mon'].'&op2year='.$arraydatetouse['year'];
2216  //if ($warning_delay >= 0) $url.='&amp;filter=expired';
2217  $label = $langs->trans("BoardRunningServices");
2218  $labelShort = $langs->trans("BoardRunningServicesShort");
2219  }
2220 
2221  $response = new WorkboardResponse();
2222  $response->warning_delay = $warning_delay / 60 / 60 / 24;
2223  $response->label = $label;
2224  $response->labelShort = $labelShort;
2225  $response->url = $url;
2226  $response->img = img_object('', "contract");
2227 
2228  while ($obj = $this->db->fetch_object($resql))
2229  {
2230  $response->nbtodo++;
2231 
2232  if ($obj->datefin && $this->db->jdate($obj->datefin) < ($now - $warning_delay)) {
2233  $response->nbtodolate++;
2234  }
2235  }
2236 
2237  return $response;
2238  } else {
2239  dol_print_error($this->db);
2240  $this->error = $this->db->error();
2241  return -1;
2242  }
2243  }
2244 
2245  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2251  public function load_state_board()
2252  {
2253  // phpcs:enable
2254  global $conf, $user;
2255 
2256  $this->nb = array();
2257  $clause = "WHERE";
2258 
2259  $sql = "SELECT count(c.rowid) as nb";
2260  $sql .= " FROM ".MAIN_DB_PREFIX."contrat as c";
2261  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON c.fk_soc = s.rowid";
2262  if (!$user->rights->societe->client->voir && !$user->socid)
2263  {
2264  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2265  $sql .= " WHERE sc.fk_user = ".$user->id;
2266  $clause = "AND";
2267  }
2268  $sql .= " ".$clause." c.entity = ".$conf->entity;
2269 
2270  $resql = $this->db->query($sql);
2271  if ($resql)
2272  {
2273  while ($obj = $this->db->fetch_object($resql))
2274  {
2275  $this->nb["contracts"] = $obj->nb;
2276  }
2277  $this->db->free($resql);
2278  return 1;
2279  } else {
2280  dol_print_error($this->db);
2281  $this->error = $this->db->error();
2282  return -1;
2283  }
2284  }
2285 
2286 
2287  /* gestion des contacts d'un contrat */
2288 
2294  public function getIdBillingContact()
2295  {
2296  return $this->getIdContact('external', 'BILLING');
2297  }
2298 
2304  public function getIdServiceContact()
2305  {
2306  return $this->getIdContact('external', 'SERVICE');
2307  }
2308 
2309 
2317  public function initAsSpecimen()
2318  {
2319  global $user, $langs, $conf;
2320 
2321  // Load array of products prodids
2322  $num_prods = 0;
2323  $prodids = array();
2324  $sql = "SELECT rowid";
2325  $sql .= " FROM ".MAIN_DB_PREFIX."product";
2326  $sql .= " WHERE entity IN (".getEntity('product').")";
2327  $sql .= " AND tosell = 1";
2328  $sql .= $this->db->plimit(100);
2329 
2330  $resql = $this->db->query($sql);
2331  if ($resql)
2332  {
2333  $num_prods = $this->db->num_rows($resql);
2334  $i = 0;
2335  while ($i < $num_prods)
2336  {
2337  $i++;
2338  $row = $this->db->fetch_row($resql);
2339  $prodids[$i] = $row[0];
2340  }
2341  }
2342 
2343  // Initialise parametres
2344  $this->id = 0;
2345  $this->specimen = 1;
2346 
2347  $this->ref = 'SPECIMEN';
2348  $this->ref_customer = 'SPECIMENCUST';
2349  $this->ref_supplier = 'SPECIMENSUPP';
2350  $this->socid = 1;
2351  $this->statut = 0;
2352  $this->date_creation = (dol_now() - 3600 * 24 * 7);
2353  $this->date_contrat = dol_now();
2354  $this->commercial_signature_id = 1;
2355  $this->commercial_suivi_id = 1;
2356  $this->note_private = 'This is a comment (private)';
2357  $this->note_public = 'This is a comment (public)';
2358  $this->fk_projet = 0;
2359  // Lines
2360  $nbp = 5;
2361  $xnbp = 0;
2362  while ($xnbp < $nbp)
2363  {
2364  $line = new ContratLigne($this->db);
2365  $line->qty = 1;
2366  $line->subprice = 100;
2367  $line->price = 100;
2368  $line->tva_tx = 19.6;
2369  $line->remise_percent = 10;
2370  $line->total_ht = 90;
2371  $line->total_ttc = 107.64; // 90 * 1.196
2372  $line->total_tva = 17.64;
2373  $line->date_start = dol_now() - 500000;
2374  $line->date_start_real = dol_now() - 200000;
2375  $line->date_end = dol_now() + 500000;
2376  $line->date_end_real = dol_now() - 100000;
2377  if ($num_prods > 0)
2378  {
2379  $prodid = mt_rand(1, $num_prods);
2380  $line->fk_product = $prodids[$prodid];
2381  }
2382  $this->lines[$xnbp] = $line;
2383  $xnbp++;
2384  }
2385  }
2386 
2392  public function getLinesArray()
2393  {
2394  return $this->fetch_lines();
2395  }
2396 
2397 
2409  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2410  {
2411  global $conf, $langs;
2412 
2413  $langs->load("contracts");
2414  $outputlangs->load("products");
2415 
2416  if (!dol_strlen($modele)) {
2417  $modele = 'strato';
2418 
2419  if (!empty($this->model_pdf)) {
2420  $modele = $this->model_pdf;
2421  } elseif (!empty($this->modelpdf)) { // deprecated
2422  $modele = $this->modelpdf;
2423  } elseif (!empty($conf->global->CONTRACT_ADDON_PDF)) {
2424  $modele = $conf->global->CONTRACT_ADDON_PDF;
2425  }
2426  }
2427 
2428  $modelpath = "core/modules/contract/doc/";
2429 
2430  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2431  }
2432 
2441  public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
2442  {
2443  $tables = array(
2444  'contrat'
2445  );
2446 
2447  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
2448  }
2449 
2458  public function createFromClone(User $user, $socid = 0, $notrigger = 0)
2459  {
2460  global $db, $langs, $conf, $hookmanager, $extrafields;
2461 
2462  dol_include_once('/projet/class/project.class.php');
2463 
2464  $error = 0;
2465 
2466  $this->fetch($this->id);
2467 
2468  // Load dest object
2469  $clonedObj = clone $this;
2470  $clonedObj->socid = $socid;
2471 
2472  $this->db->begin();
2473 
2474  $objsoc = new Societe($this->db);
2475 
2476  $objsoc->fetch($clonedObj->socid);
2477 
2478  // Clean data
2479  $clonedObj->statut = 0;
2480  // Clean extrafields
2481  if (is_array($clonedObj->array_options) && count($clonedObj->array_options) > 0)
2482  {
2483  $extrafields->fetch_name_optionals_label($this->table_element);
2484  foreach ($clonedObj->array_options as $key => $option)
2485  {
2486  $shortkey = preg_replace('/options_/', '', $key);
2487  //var_dump($shortkey); var_dump($extrafields->attributes[$this->element]['unique'][$shortkey]);
2488  if (!empty($extrafields->attributes[$this->element]['unique'][$shortkey]))
2489  {
2490  //var_dump($key); var_dump($clonedObj->array_options[$key]); exit;
2491  unset($clonedObj->array_options[$key]);
2492  }
2493  }
2494  }
2495 
2496  if (empty($conf->global->CONTRACT_ADDON) || !is_readable(DOL_DOCUMENT_ROOT."/core/modules/contract/".$conf->global->CONTRACT_ADDON.".php")) {
2497  $this->error = 'ErrorSetupNotComplete';
2498  dol_syslog($this->error);
2499  return -1;
2500  }
2501 
2502  // Set ref
2503  require_once DOL_DOCUMENT_ROOT."/core/modules/contract/".$conf->global->CONTRACT_ADDON.'.php';
2504  $obj = $conf->global->CONTRACT_ADDON;
2505  $modContract = new $obj();
2506  $clonedObj->ref = $modContract->getNextValue($objsoc, $clonedObj);
2507 
2508  // get extrafields so they will be clone
2509  foreach ($this->lines as $line) {
2510  $line->fetch_optionals($line->id);
2511  }
2512 
2513  // Create clone
2514  $clonedObj->context['createfromclone'] = 'createfromclone';
2515  $result = $clonedObj->create($user);
2516  if ($result < 0) {
2517  $error++;
2518  $this->error = $clonedObj->error;
2519  $this->errors[] = $clonedObj->error;
2520  } else {
2521  // copy external contacts if same company
2522  if ($this->socid == $clonedObj->socid) {
2523  if ($clonedObj->copy_linked_contact($this, 'external') < 0) {
2524  $error++;
2525  }
2526  }
2527  }
2528 
2529  if (!$error) {
2530  foreach ($this->lines as $line) {
2531  $result = $clonedObj->addline($line->desc, $line->subprice, $line->qty, $line->tva_tx, $line->localtax1_tx, $line->localtax2_tx, $line->fk_product, $line->remise_percent, $line->date_ouverture, $line->date_cloture, 'HT', 0, $line->info_bits, $line->fk_fournprice, $line->pa_ht, $line->array_options, $line->fk_unit);
2532  if ($result < 0) {
2533  $error++;
2534  $this->error = $clonedObj->error;
2535  $this->errors[] = $clonedObj->error;
2536  }
2537  }
2538  }
2539 
2540  if (!$error) {
2541  // Hook of thirdparty module
2542  if (is_object($hookmanager)) {
2543  $parameters = array(
2544  'objFrom' => $this,
2545  'clonedObj' => $clonedObj
2546  );
2547  $action = '';
2548  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $clonedObj, $action); // Note that $action and $object may have been modified by some hooks
2549  if ($reshook < 0)
2550  $error++;
2551  }
2552  }
2553 
2554  unset($clonedObj->context['createfromclone']);
2555 
2556  // End
2557  if (!$error) {
2558  $this->db->commit();
2559  return $clonedObj->id;
2560  } else {
2561  $this->db->rollback();
2562  return -1;
2563  }
2564  }
2565 }
2566 
2567 
2572 {
2576  public $element = 'contratdet';
2577 
2581  public $table_element = 'contratdet';
2582 
2586  public $id;
2587 
2591  public $ref;
2592 
2593  public $tms;
2594 
2598  public $fk_contrat;
2599 
2603  public $fk_product;
2604 
2605  public $statut; // 0 inactive, 4 active, 5 closed
2606  public $type; // 0 for product, 1 for service
2607 
2612  public $label;
2613 
2618  public $libelle;
2619 
2623  public $description;
2624 
2625  public $product_type; // 0 for product, 1 for service
2626  public $product_ref;
2627  public $product_label;
2628 
2629  public $date_commande;
2630 
2631  public $date_start; // date start planned
2632  public $date_start_real; // date start real
2633  public $date_end; // date end planned
2634  public $date_end_real; // date end real
2635  // For backward compatibility
2636  public $date_ouverture_prevue; // date start planned
2637  public $date_ouverture; // date start real
2638  public $date_fin_validite; // date end planned
2639  public $date_cloture; // date end real
2640  public $tva_tx;
2641  public $localtax1_tx;
2642  public $localtax2_tx;
2643  public $localtax1_type; // Local tax 1 type
2644  public $localtax2_type; // Local tax 2 type
2645  public $qty;
2646  public $remise_percent;
2647  public $remise;
2648 
2652  public $fk_remise_except;
2653 
2654  public $subprice; // Unit price HT
2655 
2661  public $price;
2662 
2663  public $price_ht;
2664 
2665  public $total_ht;
2666  public $total_tva;
2667  public $total_localtax1;
2668  public $total_localtax2;
2669  public $total_ttc;
2670 
2674  public $fk_fournprice;
2675 
2676  public $pa_ht;
2677 
2678  public $info_bits;
2679 
2683  public $fk_user_author;
2684 
2688  public $fk_user_ouverture;
2689 
2693  public $fk_user_cloture;
2694 
2695  public $commentaire;
2696 
2697  const STATUS_INITIAL = 0;
2698  const STATUS_OPEN = 4;
2699  const STATUS_CLOSED = 5;
2700 
2701 
2702 
2708  public function __construct($db)
2709  {
2710  $this->db = $db;
2711  }
2712 
2713 
2720  public function getLibStatut($mode)
2721  {
2722  return $this->LibStatut($this->statut, $mode, ((!empty($this->date_fin_validite)) ? ($this->date_fin_validite < dol_now() ? 1 : 0) : -1));
2723  }
2724 
2725  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2735  public static function LibStatut($status, $mode, $expired = -1, $moreatt = '')
2736  {
2737  // phpcs:enable
2738  global $langs;
2739  $langs->load("contracts");
2740 
2741  if ($status == self::STATUS_INITIAL) {
2742  $labelStatus = $langs->trans("ServiceStatusInitial");
2743  $labelStatusShort = $langs->trans("ServiceStatusInitial");
2744  } elseif ($status == self::STATUS_OPEN && $expired == -1) {
2745  $labelStatus = $langs->trans("ServiceStatusRunning");
2746  $labelStatusShort = $langs->trans("ServiceStatusRunning");
2747  } elseif ($status == self::STATUS_OPEN && $expired == 0) {
2748  $labelStatus = $langs->trans("ServiceStatusNotLate");
2749  $labelStatusShort = $langs->trans("ServiceStatusNotLateShort");
2750  } elseif ($status == self::STATUS_OPEN && $expired == 1) {
2751  $labelStatus = $langs->trans("ServiceStatusLate");
2752  $labelStatusShort = $langs->trans("ServiceStatusLateShort");
2753  } elseif ($status == self::STATUS_CLOSED) {
2754  $labelStatus = $langs->trans("ServiceStatusClosed");
2755  $labelStatusShort = $langs->trans("ServiceStatusClosed");
2756  }
2757 
2758  $statusType = 'status'.$status;
2759  if ($status == self::STATUS_OPEN && $expired == 1) $statusType = 'status1';
2760  if ($status == self::STATUS_CLOSED) $statusType = 'status6';
2761 
2762  $params = array(); $reg = array();
2763  if (preg_match('/class="(.*)"/', $moreatt, $reg))
2764  {
2765  $params = array('badgeParams'=>array('css' => $reg[1]));
2766  }
2767  return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', $params);
2768  }
2769 
2777  public function getNomUrl($withpicto = 0, $maxlength = 0)
2778  {
2779  global $langs;
2780 
2781  $result = '';
2782  $label = $langs->trans("ShowContractOfService").': '.$this->label;
2783  if (empty($label)) $label = $this->description;
2784 
2785  $link = '<a href="'.DOL_URL_ROOT.'/contrat/card.php?id='.$this->fk_contrat.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
2786  $linkend = '</a>';
2787 
2788  $picto = 'service';
2789  if ($this->type == 0) $picto = 'product';
2790 
2791  if ($withpicto) $result .= ($link.img_object($label, $picto, 'class="classfortooltip"').$linkend);
2792  if ($withpicto && $withpicto != 2) $result .= ' ';
2793  if ($withpicto != 2) $result .= $link.($this->product_ref ? $this->product_ref.' ' : '').($this->label ? $this->label : $this->description).$linkend;
2794  return $result;
2795  }
2796 
2804  public function fetch($id, $ref = '')
2805  {
2806  // Check parameters
2807  if (empty($id) && empty($ref)) return -1;
2808 
2809  $sql = "SELECT";
2810  $sql .= " t.rowid,";
2811  $sql .= " t.tms,";
2812  $sql .= " t.fk_contrat,";
2813  $sql .= " t.fk_product,";
2814  $sql .= " t.statut,";
2815  $sql .= " t.label,"; // This field is not used. Only label of product
2816  $sql .= " p.ref as product_ref,";
2817  $sql .= " p.label as product_label,";
2818  $sql .= " p.description as product_desc,";
2819  $sql .= " p.fk_product_type as product_type,";
2820  $sql .= " t.description,";
2821  $sql .= " t.date_commande,";
2822  $sql .= " t.date_ouverture_prevue as date_ouverture_prevue,";
2823  $sql .= " t.date_ouverture as date_ouverture,";
2824  $sql .= " t.date_fin_validite as date_fin_validite,";
2825  $sql .= " t.date_cloture as date_cloture,";
2826  $sql .= " t.tva_tx,";
2827  $sql .= " t.vat_src_code,";
2828  $sql .= " t.localtax1_tx,";
2829  $sql .= " t.localtax2_tx,";
2830  $sql .= " t.localtax1_type,";
2831  $sql .= " t.localtax2_type,";
2832  $sql .= " t.qty,";
2833  $sql .= " t.remise_percent,";
2834  $sql .= " t.remise,";
2835  $sql .= " t.fk_remise_except,";
2836  $sql .= " t.subprice,";
2837  $sql .= " t.price_ht,";
2838  $sql .= " t.total_ht,";
2839  $sql .= " t.total_tva,";
2840  $sql .= " t.total_localtax1,";
2841  $sql .= " t.total_localtax2,";
2842  $sql .= " t.total_ttc,";
2843  $sql .= " t.fk_product_fournisseur_price as fk_fournprice,";
2844  $sql .= " t.buy_price_ht as pa_ht,";
2845  $sql .= " t.info_bits,";
2846  $sql .= " t.fk_user_author,";
2847  $sql .= " t.fk_user_ouverture,";
2848  $sql .= " t.fk_user_cloture,";
2849  $sql .= " t.commentaire,";
2850  $sql .= " t.fk_unit";
2851  $sql .= " FROM ".MAIN_DB_PREFIX."contratdet as t LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = t.fk_product";
2852  if ($id) $sql .= " WHERE t.rowid = ".$id;
2853  if ($ref) $sql .= " WHERE t.rowid = '".$this->db->escape($ref)."'";
2854 
2855  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
2856  $resql = $this->db->query($sql);
2857  if ($resql)
2858  {
2859  if ($this->db->num_rows($resql))
2860  {
2861  $obj = $this->db->fetch_object($resql);
2862 
2863  $this->id = $obj->rowid;
2864  $this->ref = $obj->rowid;
2865 
2866  $this->tms = $this->db->jdate($obj->tms);
2867  $this->fk_contrat = $obj->fk_contrat;
2868  $this->fk_product = $obj->fk_product;
2869  $this->statut = $obj->statut;
2870  $this->product_ref = $obj->product_ref;
2871  $this->product_label = $obj->product_label;
2872  $this->product_description = $obj->product_description;
2873  $this->product_type = $obj->product_type;
2874  $this->label = $obj->label; // deprecated. We do not use this field. Only ref and label of product, and description of contract line
2875  $this->description = $obj->description;
2876  $this->date_commande = $this->db->jdate($obj->date_commande);
2877 
2878  $this->date_start = $this->db->jdate($obj->date_ouverture_prevue);
2879  $this->date_start_real = $this->db->jdate($obj->date_ouverture);
2880  $this->date_end = $this->db->jdate($obj->date_fin_validite);
2881  $this->date_end_real = $this->db->jdate($obj->date_cloture);
2882  // For backward compatibility
2883  $this->date_ouverture_prevue = $this->db->jdate($obj->date_ouverture_prevue);
2884  $this->date_ouverture = $this->db->jdate($obj->date_ouverture);
2885  $this->date_fin_validite = $this->db->jdate($obj->date_fin_validite);
2886  $this->date_cloture = $this->db->jdate($obj->date_cloture);
2887 
2888  $this->tva_tx = $obj->tva_tx;
2889  $this->vat_src_code = $obj->vat_src_code;
2890  $this->localtax1_tx = $obj->localtax1_tx;
2891  $this->localtax2_tx = $obj->localtax2_tx;
2892  $this->localtax1_type = $obj->localtax1_type;
2893  $this->localtax2_type = $obj->localtax2_type;
2894  $this->qty = $obj->qty;
2895  $this->remise_percent = $obj->remise_percent;
2896  $this->remise = $obj->remise;
2897  $this->fk_remise_except = $obj->fk_remise_except;
2898  $this->subprice = $obj->subprice;
2899  $this->price_ht = $obj->price_ht;
2900  $this->total_ht = $obj->total_ht;
2901  $this->total_tva = $obj->total_tva;
2902  $this->total_localtax1 = $obj->total_localtax1;
2903  $this->total_localtax2 = $obj->total_localtax2;
2904  $this->total_ttc = $obj->total_ttc;
2905  $this->info_bits = $obj->info_bits;
2906  $this->fk_user_author = $obj->fk_user_author;
2907  $this->fk_user_ouverture = $obj->fk_user_ouverture;
2908  $this->fk_user_cloture = $obj->fk_user_cloture;
2909  $this->commentaire = $obj->commentaire;
2910  $this->fk_fournprice = $obj->fk_fournprice;
2911 
2912  $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->fk_fournprice, $obj->pa_ht);
2913  $this->pa_ht = $marginInfos[0];
2914  $this->fk_unit = $obj->fk_unit;
2915 
2916  $this->fetch_optionals();
2917  }
2918 
2919  $this->db->free($resql);
2920 
2921  return 1;
2922  } else {
2923  $this->error = "Error ".$this->db->lasterror();
2924  return -1;
2925  }
2926  }
2927 
2928 
2936  public function update($user, $notrigger = 0)
2937  {
2938  global $conf, $langs, $mysoc;
2939 
2940  $error = 0;
2941 
2942  // Clean parameters
2943  $this->fk_contrat = (int) $this->fk_contrat;
2944  $this->fk_product = (int) $this->fk_product;
2945  $this->statut = (int) $this->statut;
2946  $this->label = trim($this->label);
2947  $this->description = trim($this->description);
2948  $this->vat_src_code = trim($this->vat_src_code);
2949  $this->tva_tx = trim($this->tva_tx);
2950  $this->localtax1_tx = trim($this->localtax1_tx);
2951  $this->localtax2_tx = trim($this->localtax2_tx);
2952  $this->qty = trim($this->qty);
2953  $this->remise_percent = trim($this->remise_percent);
2954  $this->remise = trim($this->remise);
2955  $this->fk_remise_except = (int) $this->fk_remise_except;
2956  $this->subprice = price2num($this->subprice);
2957  $this->price_ht = price2num($this->price_ht);
2958  $this->total_ht = trim($this->total_ht);
2959  $this->total_tva = trim($this->total_tva);
2960  $this->total_localtax1 = trim($this->total_localtax1);
2961  $this->total_localtax2 = trim($this->total_localtax2);
2962  $this->total_ttc = trim($this->total_ttc);
2963  $this->info_bits = trim($this->info_bits);
2964  $this->fk_user_author = (int) $this->fk_user_author;
2965  $this->fk_user_ouverture = (int) $this->fk_user_ouverture;
2966  $this->fk_user_cloture = (int) $this->fk_user_cloture;
2967  $this->commentaire = trim($this->commentaire);
2968  //if (empty($this->subprice)) $this->subprice = 0;
2969  if (empty($this->price_ht)) $this->price_ht = 0;
2970  if (empty($this->total_ht)) $this->total_ht = 0;
2971  if (empty($this->total_tva)) $this->total_tva = 0;
2972  if (empty($this->total_ttc)) $this->total_ttc = 0;
2973  if (empty($this->localtax1_tx)) $this->localtax1_tx = 0;
2974  if (empty($this->localtax2_tx)) $this->localtax2_tx = 0;
2975  if (empty($this->remise_percent)) $this->remise_percent = 0;
2976  // For backward compatibility
2977  if (empty($this->date_start)) $this->date_start = $this->date_ouverture_prevue;
2978  if (empty($this->date_start_real)) $this->date_start = $this->date_ouverture;
2979  if (empty($this->date_end)) $this->date_start = $this->date_fin_validite;
2980  if (empty($this->date_end_real)) $this->date_start = $this->date_cloture;
2981 
2982 
2983  // Check parameters
2984  // Put here code to add control on parameters values
2985 
2986  // Calcul du total TTC et de la TVA pour la ligne a partir de
2987  // qty, pu, remise_percent et txtva
2988  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
2989  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
2990  $localtaxes_type = getLocalTaxesFromRate($this->txtva, 0, $this->societe, $mysoc);
2991 
2992  $tabprice = calcul_price_total($this->qty, $this->price_ht, $this->remise_percent, $this->tva_tx, $this->localtax1_tx, $this->localtax2_tx, 0, 'HT', 0, 1, $mysoc, $localtaxes_type);
2993  $this->total_ht = $tabprice[0];
2994  $this->total_tva = $tabprice[1];
2995  $this->total_ttc = $tabprice[2];
2996  $this->total_localtax1 = $tabprice[9];
2997  $this->total_localtax2 = $tabprice[10];
2998 
2999  if (empty($this->pa_ht)) $this->pa_ht = 0;
3000 
3001  // if buy price not defined, define buyprice as configured in margin admin
3002  if ($this->pa_ht == 0)
3003  {
3004  if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0)
3005  {
3006  return $result;
3007  } else {
3008  $this->pa_ht = $result;
3009  }
3010  }
3011 
3012 
3013  $this->db->begin();
3014 
3015  $this->oldcopy = new ContratLigne($this->db);
3016  $this->oldcopy->fetch($this->id);
3017  $this->oldcopy->fetch_optionals();
3018 
3019  // Update request
3020  $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET";
3021  $sql .= " fk_contrat=".$this->fk_contrat.",";
3022  $sql .= " fk_product=".($this->fk_product ? "'".$this->db->escape($this->fk_product)."'" : 'null').",";
3023  $sql .= " statut=".$this->statut.",";
3024  $sql .= " label='".$this->db->escape($this->label)."',";
3025  $sql .= " description='".$this->db->escape($this->description)."',";
3026  $sql .= " date_commande=".($this->date_commande != '' ? "'".$this->db->idate($this->date_commande)."'" : "null").",";
3027  $sql .= " date_ouverture_prevue=".($this->date_ouverture_prevue != '' ? "'".$this->db->idate($this->date_ouverture_prevue)."'" : "null").",";
3028  $sql .= " date_ouverture=".($this->date_ouverture != '' ? "'".$this->db->idate($this->date_ouverture)."'" : "null").",";
3029  $sql .= " date_fin_validite=".($this->date_fin_validite != '' ? "'".$this->db->idate($this->date_fin_validite)."'" : "null").",";
3030  $sql .= " date_cloture=".($this->date_cloture != '' ? "'".$this->db->idate($this->date_cloture)."'" : "null").",";
3031  $sql .= " vat_src_code='".$this->db->escape($this->vat_src_code)."',";
3032  $sql .= " tva_tx=".price2num($this->tva_tx).",";
3033  $sql .= " localtax1_tx=".price2num($this->localtax1_tx).",";
3034  $sql .= " localtax2_tx=".price2num($this->localtax2_tx).",";
3035  $sql .= " qty=".price2num($this->qty).",";
3036  $sql .= " remise_percent=".price2num($this->remise_percent).",";
3037  $sql .= " remise=".($this->remise ?price2num($this->remise) : "null").",";
3038  $sql .= " fk_remise_except=".($this->fk_remise_except > 0 ? $this->fk_remise_except : "null").",";
3039  $sql .= " subprice=".($this->subprice != '' ? $this->subprice : "null").",";
3040  $sql .= " price_ht=".($this->price_ht != '' ? $this->price_ht : "null").",";
3041  $sql .= " total_ht=".$this->total_ht.",";
3042  $sql .= " total_tva=".$this->total_tva.",";
3043  $sql .= " total_localtax1=".$this->total_localtax1.",";
3044  $sql .= " total_localtax2=".$this->total_localtax2.",";
3045  $sql .= " total_ttc=".$this->total_ttc.",";
3046  $sql .= " fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? $this->fk_fournprice : "NULL").",";
3047  $sql .= " buy_price_ht='".price2num($this->pa_ht)."',";
3048  $sql .= " info_bits='".$this->db->escape($this->info_bits)."',";
3049  $sql .= " fk_user_author=".($this->fk_user_author >= 0 ? $this->fk_user_author : "NULL").",";
3050  $sql .= " fk_user_ouverture=".($this->fk_user_ouverture > 0 ? $this->fk_user_ouverture : "NULL").",";
3051  $sql .= " fk_user_cloture=".($this->fk_user_cloture > 0 ? $this->fk_user_cloture : "NULL").",";
3052  $sql .= " commentaire='".$this->db->escape($this->commentaire)."',";
3053  $sql .= " fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
3054  $sql .= " WHERE rowid=".$this->id;
3055 
3056  dol_syslog(get_class($this)."::update", LOG_DEBUG);
3057  $resql = $this->db->query($sql);
3058  if (!$resql)
3059  {
3060  $this->error = "Error ".$this->db->lasterror();
3061  $error++;
3062  }
3063 
3064  if (!$error) // For avoid conflicts if trigger used
3065  {
3066  $result = $this->insertExtraFields();
3067  if ($result < 0)
3068  {
3069  $error++;
3070  }
3071  }
3072 
3073  // If we change a planned date (start or end), sync dates for all services
3074  if (!$error && !empty($conf->global->CONTRACT_SYNC_PLANNED_DATE_OF_SERVICES))
3075  {
3076  if ($this->date_ouverture_prevue != $this->oldcopy->date_ouverture_prevue)
3077  {
3078  $sql = 'UPDATE '.MAIN_DB_PREFIX.'contratdet SET';
3079  $sql .= " date_ouverture_prevue = ".($this->date_ouverture_prevue != '' ? "'".$this->db->idate($this->date_ouverture_prevue)."'" : "null");
3080  $sql .= " WHERE fk_contrat = ".$this->fk_contrat;
3081 
3082  $resql = $this->db->query($sql);
3083  if (!$resql)
3084  {
3085  $error++;
3086  $this->error = "Error ".$this->db->lasterror();
3087  }
3088  }
3089  if ($this->date_fin_validite != $this->oldcopy->date_fin_validite)
3090  {
3091  $sql = 'UPDATE '.MAIN_DB_PREFIX.'contratdet SET';
3092  $sql .= " date_fin_validite = ".($this->date_fin_validite != '' ? "'".$this->db->idate($this->date_fin_validite)."'" : "null");
3093  $sql .= " WHERE fk_contrat = ".$this->fk_contrat;
3094 
3095  $resql = $this->db->query($sql);
3096  if (!$resql)
3097  {
3098  $error++;
3099  $this->error = "Error ".$this->db->lasterror();
3100  }
3101  }
3102  }
3103 
3104  if (!$error && !$notrigger) {
3105  // Call trigger
3106  $result = $this->call_trigger('LINECONTRACT_UPDATE', $user);
3107  if ($result < 0) {
3108  $error++;
3109  $this->db->rollback();
3110  }
3111  // End call triggers
3112  }
3113 
3114  if (!$error)
3115  {
3116  $this->db->commit();
3117  return 1;
3118  } else {
3119  $this->db->rollback();
3120  $this->errors[] = $this->error;
3121  return -1;
3122  }
3123  }
3124 
3125 
3126  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3133  public function update_total()
3134  {
3135  // phpcs:enable
3136  $this->db->begin();
3137 
3138  // Mise a jour ligne en base
3139  $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET";
3140  $sql .= " total_ht=".price2num($this->total_ht, 'MT')."";
3141  $sql .= ",total_tva=".price2num($this->total_tva, 'MT')."";
3142  $sql .= ",total_localtax1=".price2num($this->total_localtax1, 'MT')."";
3143  $sql .= ",total_localtax2=".price2num($this->total_localtax2, 'MT')."";
3144  $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT')."";
3145  $sql .= " WHERE rowid = ".$this->id;
3146 
3147  dol_syslog(get_class($this)."::update_total", LOG_DEBUG);
3148 
3149  $resql = $this->db->query($sql);
3150  if ($resql)
3151  {
3152  $this->db->commit();
3153  return 1;
3154  } else {
3155  $this->error = $this->db->error();
3156  $this->db->rollback();
3157  return -2;
3158  }
3159  }
3160 
3161 
3168  public function insert($notrigger = 0)
3169  {
3170  global $conf, $user;
3171 
3172  $error = 0;
3173 
3174  // Insertion dans la base
3175  $sql = "INSERT INTO ".MAIN_DB_PREFIX."contratdet";
3176  $sql .= " (fk_contrat, label, description, fk_product, qty, vat_src_code, tva_tx,";
3177  $sql .= " localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice,";
3178  $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc,";
3179  $sql .= " info_bits,";
3180  $sql .= " price_ht, remise, fk_product_fournisseur_price, buy_price_ht";
3181  if ($this->date_ouverture_prevue > 0) { $sql .= ",date_ouverture_prevue"; }
3182  if ($this->date_fin_validite > 0) { $sql .= ",date_fin_validite"; }
3183  $sql .= ") VALUES ($this->fk_contrat, '', '".$this->db->escape($this->description)."',";
3184  $sql .= ($this->fk_product > 0 ? $this->fk_product : "null").",";
3185  $sql .= " '".$this->db->escape($this->qty)."',";
3186  $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3187  $sql .= " '".$this->db->escape($this->tva_tx)."',";
3188  $sql .= " '".$this->db->escape($this->localtax1_tx)."',";
3189  $sql .= " '".$this->db->escape($this->localtax2_tx)."',";
3190  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3191  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3192  $sql .= " ".price2num($this->remise_percent).",".price2num($this->subprice).",";
3193  $sql .= " ".price2num($this->total_ht).",".price2num($this->total_tva).",".price2num($this->total_localtax1).",".price2num($this->total_localtax2).",".price2num($this->total_ttc).",";
3194  $sql .= " '".$this->db->escape($this->info_bits)."',";
3195  $sql .= " ".price2num($this->price_ht).",".price2num($this->remise).",";
3196  if ($this->fk_fournprice > 0) $sql .= ' '.$this->fk_fournprice.',';
3197  else $sql .= ' null,';
3198  if ($this->pa_ht > 0) $sql .= ' '.price2num($this->pa_ht);
3199  else $sql .= ' null';
3200  if ($this->date_ouverture > 0) { $sql .= ",'".$this->db->idate($this->date_ouverture)."'"; }
3201  if ($this->date_cloture > 0) { $sql .= ",'".$this->db->idate($this->date_cloture)."'"; }
3202  $sql .= ")";
3203 
3204  dol_syslog(get_class($this)."::insert", LOG_DEBUG);
3205 
3206  $resql = $this->db->query($sql);
3207  if ($resql)
3208  {
3209  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'contratdet');
3210 
3211  // Insert of extrafields
3212  if (!$error)
3213  {
3214  $result = $this->insertExtraFields();
3215  if ($result < 0)
3216  {
3217  $this->db->rollback();
3218  return -1;
3219  }
3220  }
3221 
3222  if (!$notrigger)
3223  {
3224  // Call trigger
3225  $result = $this->call_trigger('LINECONTRACT_INSERT', $user);
3226  if ($result < 0) {
3227  $this->db->rollback();
3228  return -1;
3229  }
3230  // End call triggers
3231  }
3232 
3233  $this->db->commit();
3234  return 1;
3235  } else {
3236  $this->db->rollback();
3237  $this->error = $this->db->error()." sql=".$sql;
3238  return -1;
3239  }
3240  }
3241 
3242  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3252  public function active_line($user, $date, $date_end = '', $comment = '')
3253  {
3254  // phpcs:enable
3255  global $langs, $conf;
3256 
3257  $error = 0;
3258 
3259  $this->db->begin();
3260 
3261  $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET statut = ".ContratLigne::STATUS_OPEN.",";
3262  $sql .= " date_ouverture = ".(dol_strlen($date) != 0 ? "'".$this->db->idate($date)."'" : "null").",";
3263  if ($date_end >= 0) $sql .= " date_fin_validite = ".(dol_strlen($date_end) != 0 ? "'".$this->db->idate($date_end)."'" : "null").",";
3264  $sql .= " fk_user_ouverture = ".$user->id.",";
3265  $sql .= " date_cloture = null,";
3266  $sql .= " commentaire = '".$this->db->escape($comment)."'";
3267  $sql .= " WHERE rowid = ".$this->id." AND (statut = ".ContratLigne::STATUS_INITIAL." OR statut = ".ContratLigne::STATUS_CLOSED.")";
3268 
3269  dol_syslog(get_class($this)."::active_line", LOG_DEBUG);
3270  $resql = $this->db->query($sql);
3271  if ($resql) {
3272  // Call trigger
3273  $result = $this->call_trigger('LINECONTRACT_ACTIVATE', $user);
3274  if ($result < 0) $error++;
3275  // End call triggers
3276 
3277  if (!$error)
3278  {
3279  $this->statut = ContratLigne::STATUS_OPEN;
3280  $this->date_ouverture = $date;
3281  $this->date_fin_validite = $date_end;
3282  $this->fk_user_ouverture = $user->id;
3283  $this->date_cloture = null;
3284  $this->commentaire = $comment;
3285 
3286  $this->db->commit();
3287  return 1;
3288  } else {
3289  $this->db->rollback();
3290  return -1;
3291  }
3292  } else {
3293  $this->error = $this->db->lasterror();
3294  $this->db->rollback();
3295  return -1;
3296  }
3297  }
3298 
3299  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3309  public function close_line($user, $date_end, $comment = '', $notrigger = 0)
3310  {
3311  // phpcs:enable
3312  global $langs, $conf;
3313 
3314  // Update object
3315  $this->date_cloture = $date_end;
3316  $this->fk_user_cloture = $user->id;
3317  $this->commentaire = $comment;
3318 
3319  $error = 0;
3320 
3321  // statut actif : 4
3322 
3323  $this->db->begin();
3324 
3325  $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET statut = ".ContratLigne::STATUS_CLOSED.",";
3326  $sql .= " date_cloture = '".$this->db->idate($date_end)."',";
3327  $sql .= " fk_user_cloture = ".$user->id.",";
3328  $sql .= " commentaire = '".$this->db->escape($comment)."'";
3329  $sql .= " WHERE rowid = ".$this->id." AND statut = ".ContratLigne::STATUS_OPEN;
3330 
3331  $resql = $this->db->query($sql);
3332  if ($resql)
3333  {
3334  if (!$notrigger)
3335  {
3336  // Call trigger
3337  $result = $this->call_trigger('LINECONTRACT_CLOSE', $user);
3338  if ($result < 0) {
3339  $error++;
3340  $this->db->rollback();
3341  return -1;
3342  }
3343  // End call triggers
3344  }
3345 
3346  $this->db->commit();
3347  return 1;
3348  } else {
3349  $this->error = $this->db->lasterror();
3350  $this->db->rollback();
3351  return -1;
3352  }
3353  }
3354 }
static replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
Function used to replace a thirdparty id with another one.
if(!function_exists('dol_getprefix')) dol_include_once($relpath, $classname= '')
Make an include_once using default root and alternate root if it fails.
initAsSpecimen()
Initialise an instance with random values.
getIdContact($source, $code, $status=0)
Return id of contacts for a source and a contact code.
array_detail($status=-1)
Return list of line rowid.
info($id)
Charge les informations d&#39;ordre info dans l&#39;objet contrat.
close_line($user, $line_id, $date_end, $comment= '')
Close a contract line.
if(!empty($arrayfields['u.datec']['checked'])) print_liste_field_titre("DateCreationShort"u if(!empty($arrayfields['u.tms']['checked'])) print_liste_field_titre("DateModificationShort"u if(!empty($arrayfields['u.statut']['checked'])) print_liste_field_titre("Status"u statut
Definition: list.php:632
getMarginInfos($pvht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $paht)
Return an array with margins information of a line.
activateAll($user, $date_start= '', $notrigger=0, $comment= '')
Open all lines of a contract.
validate(User $user, $force_number= '', $notrigger=0)
Validate a contract.
closeAll(User $user, $notrigger=0, $comment= '')
Close all lines of a contract.
active_line($user, $line_id, $date, $date_end= '', $comment= '')
Activate a contract line.
generateDocument($modele, $outputlangs, $hidedetails=0, $hidedesc=0, $hideref=0, $moreparams=null)
Create a document onto disk according to template module.
LibStatut($status, $mode)
Renvoi label of a given contrat status.
</td > param sortfield sortorder printFieldListOption< tdclass="liste_titremaxwidthsearchright"></td ></tr >< trclass="liste_titre">< inputtype="checkbox"onClick="toggle(this)"/> Ref p ref Label p label Duration p duration center DesiredStock p desiredstock right StockLimitShort p seuil_stock_alerte right stock_physique right stock_real_warehouse right Ordered right StockToBuy right SupplierRef right param sortfield sortorder printFieldListTitle warehouseinternal SELECT description FROM product_lang WHERE qty< br > qty qty qty StockTooLow StockTooLow help help help< trclass="oddeven">< td >< inputtype="checkbox"class="check"name="choose'.$i.'"></td >< tdclass="nowrap"> stock</td >< td >< inputtype="hidden"name="desc'.$i.'"value="'.dol_escape_htmltag($objp-> description
Only used if Module[ID]Desc translation string is not found.
Definition: replenish.php:750
getIdServiceContact()
Return id des contacts clients de prestation.
Class to manage products or services.
dol_now($mode= 'auto')
Return date for now.
getLinesArray()
Create an array of order lines.
fetch($id, $ref= '', $ref_customer= '', $ref_supplier= '')
Load a contract from database.
update_total()
Mise a jour en base des champs total_xxx de ligne Used by migration process.
delete_linked_contact($source= '', $code= '')
Delete all links between an object $this and all its contacts.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $remise_percent_global, $price_base_type, $info_bits, $type, $seller= '', $localtaxes_array= '', $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code= '')
Calculate totals (net, vat, ...) of a line.
Definition: price.lib.php:86
Class to manage Dolibarr users.
Definition: user.class.php:44
createFromClone(User $user, $socid=0, $notrigger=0)
Load an object from its id and create a new one in database.
Class to manage Dolibarr database access.
add_contact($fk_socpeople, $type_contact, $source= 'external', $notrigger=0)
Add a link between element $this-&gt;element and a contact.
Class to manage contracts.
getIdBillingContact()
Return id des contacts clients de facturation.
commonGenerateDocument($modelspath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams=null)
Common function for all objects extending CommonObject for generating documents.
create($user)
Create a contract into database.
Class to manage lines of contracts.
update($user, $notrigger=0)
Update object into database.
fetch($id, $ref= '')
Load object in memory from database.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this-&gt;socid or $this-&gt;fk_soc, into this-&gt;thirdparty.
update_statut($user)
Update statut of contract according to services.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
$conf db
API class for accounts.
Definition: inc.php:54
getNomUrl($withpicto=0, $maxlength=0)
Return clicable name (with picto eventually)
close_line($user, $date_end, $comment= '', $notrigger=0)
Close a contract line.
price($amount, $form=0, $outlangs= '', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code= '')
Function to format a value into an amount for visual output Function used into PDF and HTML pages...
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
insertExtraFields($trigger= '', $userused=null)
Add/Update all extra fields values for the current object.
Class to manage standard extra fields.
__construct($db)
Constructor.
load_state_board()
Charge indicateurs this-&gt;nb de tableau de bord.
Class to manage third parties objects (customers, suppliers, prospects...)
fetch_lines($only_product=0, $loadalsotranslation=0)
Load lines array into this-&gt;lines.
dol_strlen($string, $stringencoding= 'UTF-8')
Make a strlen call.
price2num($amount, $rounding= '', $option=0)
Function that return a number with universal decimal format (decimal separator is &#39;...
getListOfContracts($option= 'all')
Return list of other contracts for same company than current contract.
Class to manage shipments.
static commonReplaceThirdparty(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a thirdparty id with another one.
active_line($user, $date, $date_end= '', $comment= '')
Activate a contract line.
img_picto($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt= '', $morecss= '', $marginleftonlyshort=2)
Show picto whatever it&#39;s its name (generic function)
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename= '', $restricttologhandler= '', $logcontext=null)
Write log message into outputs.
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories) ...
Definition: files.lib.php:1286
static LibStatut($status, $mode, $expired=-1, $moreatt= '')
Return label of a contract line status.
img_object($titlealt, $picto, $moreatt= '', $pictoisfullpath=false, $srconly=0, $notitle=0)
Show a picto called object_picto (generic function)
deleteExtraFields()
Delete all extra fields values for the current object.
dol_sanitizeFileName($str, $newstr= '_', $unaccent=1)
Clean a string to use it as a file name.
dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefilter=null, $sortcriteria="name", $sortorder=SORT_ASC, $mode=0, $nohook=0, $relativename="", $donotfollowsymlinks=0)
Scan a directory and return a list of files/directories.
Definition: files.lib.php:60
deleteline($idline, User $user)
Delete a contract line.
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this-&gt;array_options This method is in most cases call...
reopen($user, $notrigger=0)
Unvalidate a contract.
deleteObjectLinked($sourceid=null, $sourcetype= '', $targetid=null, $targettype= '', $rowid= '')
Delete all links between an object $this.
update($user, $notrigger=0)
Update database for contract line.
getNextNumRef($soc)
Return next contract ref.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
print $_SERVER["PHP_SELF"]
Edit parameters.
getLibStatut($mode)
Return label of a contract status.
getLibStatut($mode)
Return label of this contract line status.
print
Draft customers invoices.
Definition: index.php:89
call_trigger($triggerName, $user)
Call trigger based on this instance.
if(!empty($conf->facture->enabled)&&$user->rights->facture->lire) if((!empty($conf->fournisseur->enabled)&&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)||!empty($conf->supplier_invoice->enabled))&&$user->rights->fournisseur->facture->lire) if(!empty($conf->don->enabled)&&$user->rights->don->lire) if(!empty($conf->tax->enabled)&&$user->rights->tax->charges->lire) if(!empty($conf->facture->enabled)&&!empty($conf->commande->enabled)&&$user->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) if(!empty($conf->facture->enabled)&&$user->rights->facture->lire) if((!empty($conf->fournisseur->enabled)&&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)||!empty($conf->supplier_invoice->enabled))&&$user->rights->fournisseur->facture->lire) $resql
Social contributions to pay.
Definition: index.php:1232
dol_print_error($db= '', $error= '', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
insert($notrigger=0)
Inserts a contrat line into database.
dolGetStatus($statusLabel= '', $statusLabelShort= '', $html= '', $statusType= 'status0', $displayMode=0, $url= '', $params=array())
Output the badge of a status.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
updateline($rowid, $desc, $pu, $qty, $remise_percent, $date_start, $date_end, $tvatx, $localtax1tx=0.0, $localtax2tx=0.0, $date_debut_reel= '', $date_fin_reel= '', $price_base_type= 'HT', $info_bits=0, $fk_fournprice=null, $pa_ht=0, $array_options=0, $fk_unit=null)
Mets a jour une ligne de contrat.
add_object_linked($origin=null, $origin_id=null)
Add objects linked in llx_element_element.
$table_ref_field
{}
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition: repair.php:105
__construct($db)
Constructor.
load_board($user, $mode)
Load indicators for dashboard (this-&gt;nbtodo and this-&gt;nbtodolate)
Parent class of all other business classes (invoices, contracts, proposals, orders, ...)
addline($desc, $pu_ht, $qty, $txtva, $txlocaltax1, $txlocaltax2, $fk_product, $remise_percent, $date_start, $date_end, $price_base_type= 'HT', $pu_ttc=0.0, $info_bits=0, $fk_fournprice=null, $pa_ht=0, $array_options=0, $fk_unit=null, $rang=0)
Ajoute une ligne de contrat en base.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $keepmoretags= '', $escapeonlyhtmltags=0)
Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields...
getNomUrl($withpicto=0, $maxlength=0, $notooltip=0, $save_lastsearch_value=-1)
Return clicable name (with picto eventually)