dolibarr  16.0.1
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-2021 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 
184  public $nbofservices;
185  public $nbofserviceswait;
186  public $nbofservicesopened;
187  public $nbofservicesexpired;
188  //public $lower_planned_end_date;
189  //public $higher_planner_end_date;
190 
195  protected $lines_id_index_mapper = array();
196 
197 
222  // BEGIN MODULEBUILDER PROPERTIES
226  public $fields = array(
227  'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
228  'ref' =>array('type'=>'varchar(50)', 'label'=>'Ref', 'enabled'=>1, 'visible'=>-1, 'showoncombobox'=>1, 'position'=>15),
229  'ref_ext' =>array('type'=>'varchar(255)', 'label'=>'Ref ext', 'enabled'=>1, 'visible'=>0, 'position'=>20),
230  'ref_supplier' =>array('type'=>'varchar(50)', 'label'=>'Ref supplier', 'enabled'=>1, 'visible'=>-1, 'position'=>25),
231  'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>30, 'index'=>1),
232  'tms' =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>35),
233  'datec' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-1, 'position'=>40),
234  'date_contrat' =>array('type'=>'datetime', 'label'=>'Date contrat', 'enabled'=>1, 'visible'=>-1, 'position'=>45),
235  'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>'$conf->societe->enabled', 'visible'=>-1, 'notnull'=>1, 'position'=>70),
236  'fk_projet' =>array('type'=>'integer:Project:projet/class/project.class.php:1:fk_statut=1', 'label'=>'Project', 'enabled'=>'$conf->project->enabled', 'visible'=>-1, 'position'=>75),
237  'fk_commercial_signature' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'SaleRepresentative Signature', 'enabled'=>1, 'visible'=>-1, 'position'=>80),
238  'fk_commercial_suivi' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'SaleRepresentative follower', 'enabled'=>1, 'visible'=>-1, 'position'=>85),
239  'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>90),
240  'note_public' =>array('type'=>'text', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>105),
241  'note_private' =>array('type'=>'text', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>110),
242  'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'Model pdf', 'enabled'=>1, 'visible'=>0, 'position'=>115),
243  'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>120),
244  'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>125),
245  'ref_customer' =>array('type'=>'varchar(50)', 'label'=>'Ref customer', 'enabled'=>1, 'visible'=>-1, 'position'=>130),
246  'fk_user_modif' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>135),
247  'last_main_doc' =>array('type'=>'varchar(255)', 'label'=>'Last main doc', 'enabled'=>1, 'visible'=>-1, 'position'=>140),
248  'statut' =>array('type'=>'smallint(6)', 'label'=>'Statut', 'enabled'=>1, 'visible'=>-1, 'position'=>500, 'notnull'=>1, 'arrayofkeyval'=>array(0=>'Draft', 1=>'Validated', 2=>'Closed'))
249  );
250  // END MODULEBUILDER PROPERTIES
251 
252  const STATUS_DRAFT = 0;
253  const STATUS_VALIDATED = 1;
254  const STATUS_CLOSED = 2;
255 
256 
257 
263  public function __construct($db)
264  {
265  $this->db = $db;
266  }
267 
274  public function getNextNumRef($soc)
275  {
276  global $db, $langs, $conf;
277  $langs->load("contracts");
278 
279  if (!empty($conf->global->CONTRACT_ADDON)) {
280  $mybool = false;
281 
282  $file = $conf->global->CONTRACT_ADDON.".php";
283  $classname = $conf->global->CONTRACT_ADDON;
284 
285  // Include file with class
286  $dirmodels = array_merge(array('/'), (array) $conf->modules_parts['models']);
287 
288  foreach ($dirmodels as $reldir) {
289  $dir = dol_buildpath($reldir."core/modules/contract/");
290 
291  // Load file with numbering class (if found)
292  $mybool |= @include_once $dir.$file;
293  }
294 
295  if (!$mybool) {
296  dol_print_error('', "Failed to include file ".$file);
297  return '';
298  }
299 
300  $obj = new $classname();
301  $numref = $obj->getNextValue($soc, $this);
302 
303  if ($numref != "") {
304  return $numref;
305  } else {
306  $this->error = $obj->error;
307  dol_print_error($db, get_class($this)."::getNextValue ".$obj->error);
308  return "";
309  }
310  } else {
311  $langs->load("errors");
312  print $langs->trans("Error")." ".$langs->trans("ErrorModuleSetupNotComplete", $langs->transnoentitiesnoconv("Contract"));
313  return "";
314  }
315  }
316 
317  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
328  public function active_line($user, $line_id, $date, $date_end = '', $comment = '')
329  {
330  // phpcs:enable
331  $result = $this->lines[$this->lines_id_index_mapper[$line_id]]->active_line($user, $date, $date_end, $comment);
332  if ($result < 0) {
333  $this->error = $this->lines[$this->lines_id_index_mapper[$line_id]]->error;
334  $this->errors = $this->lines[$this->lines_id_index_mapper[$line_id]]->errors;
335  }
336  return $result;
337  }
338 
339 
340  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
350  public function close_line($user, $line_id, $date_end, $comment = '')
351  {
352  // phpcs:enable
353  $result = $this->lines[$this->lines_id_index_mapper[$line_id]]->close_line($user, $date_end, $comment);
354  if ($result < 0) {
355  $this->error = $this->lines[$this->lines_id_index_mapper[$line_id]]->error;
356  $this->errors = $this->lines[$this->lines_id_index_mapper[$line_id]]->errors;
357  }
358  return $result;
359  }
360 
361 
372  public function activateAll($user, $date_start = '', $notrigger = 0, $comment = '')
373  {
374  if (empty($date_start)) {
375  $date_start = dol_now();
376  }
377 
378  $this->db->begin();
379 
380  $error = 0;
381 
382  // Load lines
383  $this->fetch_lines();
384 
385  foreach ($this->lines as $contratline) {
386  // Open lines not already open
387  if ($contratline->statut != ContratLigne::STATUS_OPEN) {
388  $contratline->context = $this->context;
389 
390  $result = $contratline->active_line($user, $date_start, -1, $comment); // This call trigger LINECONTRACT_ACTIVATE
391  if ($result < 0) {
392  $error++;
393  $this->error = $contratline->error;
394  $this->errors = $contratline->errors;
395  break;
396  }
397  }
398  }
399 
400  if (!$error && $this->statut == 0) {
401  $result = $this->validate($user, '', $notrigger);
402  if ($result < 0) {
403  $error++;
404  }
405  }
406 
407  if (!$error) {
408  $this->db->commit();
409  return 1;
410  } else {
411  $this->db->rollback();
412  return -1;
413  }
414  }
415 
425  public function closeAll(User $user, $notrigger = 0, $comment = '')
426  {
427  $this->db->begin();
428 
429  // Load lines
430  $this->fetch_lines();
431 
432  $now = dol_now();
433 
434  $error = 0;
435 
436  foreach ($this->lines as $contratline) {
437  // Close lines not already closed
438  if ($contratline->statut != ContratLigne::STATUS_CLOSED) {
439  $contratline->date_end_real = $now;
440  $contratline->date_cloture = $now; // For backward compatibility
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  $error++;
446  $this->error = $contratline->error;
447  $this->errors = $contratline->errors;
448  break;
449  }
450  }
451  }
452 
453  if (!$error && $this->statut == 0) {
454  $result = $this->validate($user, '', $notrigger);
455  if ($result < 0) {
456  $error++;
457  }
458  }
459 
460  if (!$error) {
461  $this->db->commit();
462  return 1;
463  } else {
464  $this->db->rollback();
465  return -1;
466  }
467  }
468 
477  public function validate(User $user, $force_number = '', $notrigger = 0)
478  {
479  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
480  global $langs, $conf;
481 
482  $now = dol_now();
483 
484  $error = 0;
485  dol_syslog(get_class($this).'::validate user='.$user->id.', force_number='.$force_number);
486 
487 
488  $this->db->begin();
489 
490  $this->fetch_thirdparty();
491 
492  // A contract is validated so we can move thirdparty to status customer
493  if (empty($conf->global->CONTRACT_DISABLE_AUTOSET_AS_CLIENT_ON_CONTRACT_VALIDATION)) {
494  $result = $this->thirdparty->set_as_client();
495  }
496 
497  // Define new ref
498  if ($force_number) {
499  $num = $force_number;
500  } elseif (!$error && (preg_match('/^[\(]?PROV/i', $this->ref) || empty($this->ref))) { // empty should not happened, but when it occurs, the test save life
501  $num = $this->getNextNumRef($this->thirdparty);
502  } else {
503  $num = $this->ref;
504  }
505  $this->newref = dol_sanitizeFileName($num);
506 
507  if ($num) {
508  $sql = "UPDATE ".MAIN_DB_PREFIX."contrat SET ref = '".$this->db->escape($num)."', statut = 1";
509  //$sql.= ", fk_user_valid = ".$user->id.", date_valid = '".$this->db->idate($now)."'";
510  $sql .= " WHERE rowid = ".((int) $this->id)." AND statut = 0";
511 
512  dol_syslog(get_class($this)."::validate", LOG_DEBUG);
513  $resql = $this->db->query($sql);
514  if (!$resql) {
515  dol_print_error($this->db);
516  $error++;
517  $this->error = $this->db->lasterror();
518  }
519 
520  // Trigger calls
521  if (!$error && !$notrigger) {
522  // Call trigger
523  $result = $this->call_trigger('CONTRACT_VALIDATE', $user);
524  if ($result < 0) {
525  $error++;
526  }
527  // End call triggers
528  }
529 
530  if (!$error) {
531  $this->oldref = $this->ref;
532 
533  // Rename directory if dir was a temporary ref
534  if (preg_match('/^[\(]?PROV/i', $this->ref)) {
535  // Now we rename also files into index
536  $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)."'";
537  $sql .= " WHERE filename LIKE '".$this->db->escape($this->ref)."%' AND filepath = 'contract/".$this->db->escape($this->ref)."' and entity = ".$conf->entity;
538  $resql = $this->db->query($sql);
539  if (!$resql) {
540  $error++; $this->error = $this->db->lasterror();
541  }
542 
543  // We rename directory ($this->ref = old ref, $num = new ref) in order not to lose the attachments
544  $oldref = dol_sanitizeFileName($this->ref);
545  $newref = dol_sanitizeFileName($num);
546  $dirsource = $conf->contract->dir_output.'/'.$oldref;
547  $dirdest = $conf->contract->dir_output.'/'.$newref;
548  if (!$error && file_exists($dirsource)) {
549  dol_syslog(get_class($this)."::validate rename dir ".$dirsource." into ".$dirdest);
550 
551  if (@rename($dirsource, $dirdest)) {
552  dol_syslog("Rename ok");
553  // Rename docs starting with $oldref with $newref
554  $listoffiles = dol_dir_list($conf->contract->dir_output.'/'.$newref, 'files', 1, '^'.preg_quote($oldref, '/'));
555  foreach ($listoffiles as $fileentry) {
556  $dirsource = $fileentry['name'];
557  $dirdest = preg_replace('/^'.preg_quote($oldref, '/').'/', $newref, $dirsource);
558  $dirsource = $fileentry['path'].'/'.$dirsource;
559  $dirdest = $fileentry['path'].'/'.$dirdest;
560  @rename($dirsource, $dirdest);
561  }
562  }
563  }
564  }
565  }
566 
567  // Set new ref and define current statut
568  if (!$error) {
569  $this->ref = $num;
570  $this->statut = 1;
571  $this->brouillon = 0;
572  $this->date_validation = $now;
573  }
574  } else {
575  $error++;
576  }
577 
578  if (!$error) {
579  $this->db->commit();
580  return 1;
581  } else {
582  $this->db->rollback();
583  return -1;
584  }
585  }
586 
594  public function reopen($user, $notrigger = 0)
595  {
596  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
597  global $langs, $conf;
598 
599  $now = dol_now();
600 
601  $error = 0;
602  dol_syslog(get_class($this).'::reopen user='.$user->id);
603 
604  $this->db->begin();
605 
606  $this->fetch_thirdparty();
607 
608  $sql = "UPDATE ".MAIN_DB_PREFIX."contrat SET statut = 0";
609  //$sql.= ", fk_user_valid = null, date_valid = null";
610  $sql .= " WHERE rowid = ".((int) $this->id)." AND statut = 1";
611 
612  dol_syslog(get_class($this)."::validate", LOG_DEBUG);
613  $resql = $this->db->query($sql);
614  if (!$resql) {
615  dol_print_error($this->db);
616  $error++;
617  $this->error = $this->db->lasterror();
618  }
619 
620  // Trigger calls
621  if (!$error && !$notrigger) {
622  // Call trigger
623  $result = $this->call_trigger('CONTRACT_REOPEN', $user);
624  if ($result < 0) {
625  $error++;
626  }
627  // End call triggers
628  }
629 
630  // Set new ref and define current status
631  if (!$error) {
632  $this->statut = 0;
633  $this->brouillon = 1;
634  $this->date_validation = $now;
635  }
636 
637  if (!$error) {
638  $this->db->commit();
639  return 1;
640  } else {
641  $this->db->rollback();
642  return -1;
643  }
644  }
645 
655  public function fetch($id, $ref = '', $ref_customer = '', $ref_supplier = '')
656  {
657  $sql = "SELECT rowid, statut, ref, fk_soc,";
658  $sql .= " ref_supplier, ref_customer,";
659  $sql .= " ref_ext,";
660  $sql .= " entity,";
661  $sql .= " date_contrat as datecontrat,";
662  $sql .= " fk_user_author,";
663  $sql .= " fk_projet as fk_project,";
664  $sql .= " fk_commercial_signature, fk_commercial_suivi,";
665  $sql .= " note_private, note_public, model_pdf, extraparams";
666  $sql .= " FROM ".MAIN_DB_PREFIX."contrat";
667  if (!$id) {
668  $sql .= " WHERE entity IN (".getEntity('contract').")";
669  } else {
670  $sql .= " WHERE rowid=".(int) $id;
671  }
672  if ($ref_customer) {
673  $sql .= " AND ref_customer = '".$this->db->escape($ref_customer)."'";
674  }
675  if ($ref_supplier) {
676  $sql .= " AND ref_supplier = '".$this->db->escape($ref_supplier)."'";
677  }
678  if ($ref) {
679  $sql .= " AND ref='".$this->db->escape($ref)."'";
680  }
681 
682  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
683  $resql = $this->db->query($sql);
684  if ($resql) {
685  $num = $this->db->num_rows($resql);
686  if ($num > 1) {
687  $this->error = 'Fetch found several records.';
688  dol_syslog($this->error, LOG_ERR);
689  $result = -2;
690  } elseif ($num) { // $num = 1
691  $obj = $this->db->fetch_object($resql);
692  if ($obj) {
693  $this->id = $obj->rowid;
694  $this->ref = (!isset($obj->ref) || !$obj->ref) ? $obj->rowid : $obj->ref;
695  $this->ref_customer = $obj->ref_customer;
696  $this->ref_supplier = $obj->ref_supplier;
697  $this->ref_ext = $obj->ref_ext;
698  $this->entity = $obj->entity;
699  $this->statut = $obj->statut;
700 
701  $this->date_contrat = $this->db->jdate($obj->datecontrat);
702  $this->date_creation = $this->db->jdate($obj->datecontrat);
703 
704  $this->user_author_id = $obj->fk_user_author;
705 
706  $this->commercial_signature_id = $obj->fk_commercial_signature;
707  $this->commercial_suivi_id = $obj->fk_commercial_suivi;
708 
709  $this->note_private = $obj->note_private;
710  $this->note_public = $obj->note_public;
711  $this->model_pdf = $obj->model_pdf;
712  $this->modelpdf = $obj->model_pdf; // deprecated
713 
714  $this->fk_projet = $obj->fk_project; // deprecated
715  $this->fk_project = $obj->fk_project;
716 
717  $this->socid = $obj->fk_soc;
718  $this->fk_soc = $obj->fk_soc;
719 
720  $this->extraparams = (array) json_decode($obj->extraparams, true);
721 
722  $this->db->free($resql);
723 
724  // Retrieve all extrafields
725  // fetch optionals attributes and labels
726  $this->fetch_optionals();
727 
728  // Lines
729  $result = $this->fetch_lines();
730  if ($result < 0) {
731  $this->error = $this->db->lasterror();
732  return -3;
733  }
734 
735  return $this->id;
736  }
737  } else {
738  dol_syslog(get_class($this)."::fetch Contract not found");
739  $this->error = "Contract not found";
740  return 0;
741  }
742  } else {
743  dol_syslog(get_class($this)."::fetch Error searching contract");
744  $this->error = $this->db->error();
745  return -1;
746  }
747  }
748 
749  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
758  public function fetch_lines($only_services = 0, $loadalsotranslation = 0)
759  {
760  // phpcs:enable
761  global $langs, $conf;
762 
763  $this->nbofservices = 0;
764  $this->nbofserviceswait = 0;
765  $this->nbofservicesopened = 0;
766  $this->nbofservicesexpired = 0;
767  $this->nbofservicesclosed = 0;
768 
769  $total_ttc = 0;
770  $total_vat = 0;
771  $total_ht = 0;
772 
773  $now = dol_now();
774 
775  $this->lines = array();
776  $pos = 0;
777 
778  // Selects contract lines related to a product
779  $sql = "SELECT p.label as product_label, p.description as product_desc, p.ref as product_ref, p.fk_product_type as product_type,";
780  $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,";
781  $sql .= " d.total_ht,";
782  $sql .= " d.total_tva,";
783  $sql .= " d.total_localtax1,";
784  $sql .= " d.total_localtax2,";
785  $sql .= " d.total_ttc,";
786  $sql .= " d.info_bits, d.fk_product,";
787  $sql .= " d.date_ouverture_prevue, d.date_ouverture,";
788  $sql .= " d.date_fin_validite, d.date_cloture,";
789  $sql .= " d.fk_user_author,";
790  $sql .= " d.fk_user_ouverture,";
791  $sql .= " d.fk_user_cloture,";
792  $sql .= " d.fk_unit,";
793  $sql .= " d.product_type as type";
794  $sql .= " FROM ".MAIN_DB_PREFIX."contratdet as d LEFT JOIN ".MAIN_DB_PREFIX."product as p ON d.fk_product = p.rowid";
795  $sql .= " WHERE d.fk_contrat = ".((int) $this->id);
796  if ($only_services == 1) {
797  $sql .= " AND d.product_type = 1";
798  }
799  $sql .= " ORDER by d.rowid ASC";
800 
801  dol_syslog(get_class($this)."::fetch_lines", LOG_DEBUG);
802  $result = $this->db->query($sql);
803  if ($result) {
804  $num = $this->db->num_rows($result);
805  $i = 0;
806 
807  while ($i < $num) {
808  $objp = $this->db->fetch_object($result);
809 
810  $line = new ContratLigne($this->db);
811 
812  $line->id = $objp->rowid;
813  $line->ref = $objp->rowid;
814  $line->fk_contrat = $objp->fk_contrat;
815  $line->desc = $objp->description; // Description line
816  $line->qty = $objp->qty;
817  $line->vat_src_code = $objp->vat_src_code;
818  $line->tva_tx = $objp->tva_tx;
819  $line->localtax1_tx = $objp->localtax1_tx;
820  $line->localtax2_tx = $objp->localtax2_tx;
821  $line->localtax1_type = $objp->localtax1_type;
822  $line->localtax2_type = $objp->localtax2_type;
823  $line->subprice = $objp->subprice;
824  $line->statut = $objp->statut;
825  $line->remise_percent = $objp->remise_percent;
826  $line->price_ht = $objp->price_ht;
827  $line->price = $objp->price_ht; // For backward compatibility
828  $line->total_ht = $objp->total_ht;
829  $line->total_tva = $objp->total_tva;
830  $line->total_localtax1 = $objp->total_localtax1;
831  $line->total_localtax2 = $objp->total_localtax2;
832  $line->total_ttc = $objp->total_ttc;
833  $line->fk_product = (($objp->fk_product > 0) ? $objp->fk_product : 0);
834  $line->info_bits = $objp->info_bits;
835  $line->type = $objp->type;
836 
837  $line->fk_fournprice = $objp->fk_fournprice;
838  $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $objp->fk_fournprice, $objp->pa_ht);
839  $line->pa_ht = $marginInfos[0];
840 
841  $line->fk_user_author = $objp->fk_user_author;
842  $line->fk_user_ouverture = $objp->fk_user_ouverture;
843  $line->fk_user_cloture = $objp->fk_user_cloture;
844  $line->fk_unit = $objp->fk_unit;
845 
846  $line->ref = $objp->product_ref; // deprecated
847  $line->product_ref = $objp->product_ref; // Product Ref
848  $line->product_type = $objp->product_type; // Product Type
849  $line->product_desc = $objp->product_desc; // Product Description
850  $line->product_label = $objp->product_label; // Product Label
851 
852  $line->description = $objp->description;
853 
854  $line->date_start = $this->db->jdate($objp->date_ouverture_prevue);
855  $line->date_start_real = $this->db->jdate($objp->date_ouverture);
856  $line->date_end = $this->db->jdate($objp->date_fin_validite);
857  $line->date_end_real = $this->db->jdate($objp->date_cloture);
858  // For backward compatibility
859  $line->date_ouverture_prevue = $this->db->jdate($objp->date_ouverture_prevue);
860  $line->date_ouverture = $this->db->jdate($objp->date_ouverture);
861  $line->date_fin_validite = $this->db->jdate($objp->date_fin_validite);
862  $line->date_cloture = $this->db->jdate($objp->date_cloture);
863  $line->date_debut_prevue = $this->db->jdate($objp->date_ouverture_prevue);
864  $line->date_debut_reel = $this->db->jdate($objp->date_ouverture);
865  $line->date_fin_prevue = $this->db->jdate($objp->date_fin_validite);
866  $line->date_fin_reel = $this->db->jdate($objp->date_cloture);
867 
868  // Retrieve all extrafields for contract line
869  // fetch optionals attributes and labels
870  $line->fetch_optionals();
871 
872  // multilangs
873  if (!empty($conf->global->MAIN_MULTILANGS) && !empty($objp->fk_product) && !empty($loadalsotranslation)) {
874  $tmpproduct = new Product($this->db);
875  $tmpproduct->fetch($objp->fk_product);
876  $tmpproduct->getMultiLangs();
877 
878  $line->multilangs = $tmpproduct->multilangs;
879  }
880 
881  $this->lines[$pos] = $line;
882 
883  $this->lines_id_index_mapper[$line->id] = $pos;
884 
885  //dol_syslog("1 ".$line->desc);
886  //dol_syslog("2 ".$line->product_desc);
887 
888  if ($line->statut == ContratLigne::STATUS_INITIAL) {
889  $this->nbofserviceswait++;
890  }
891  if ($line->statut == ContratLigne::STATUS_OPEN && (empty($line->date_fin_prevue) || $line->date_fin_prevue >= $now)) {
892  $this->nbofservicesopened++;
893  }
894  if ($line->statut == ContratLigne::STATUS_OPEN && (!empty($line->date_fin_prevue) && $line->date_fin_prevue < $now)) {
895  $this->nbofservicesexpired++;
896  }
897  if ($line->statut == ContratLigne::STATUS_CLOSED) {
898  $this->nbofservicesclosed++;
899  }
900 
901  $total_ttc += $objp->total_ttc; // TODO Not saved into database
902  $total_vat += $objp->total_tva;
903  $total_ht += $objp->total_ht;
904 
905  $i++;
906  $pos++;
907  }
908  $this->db->free($result);
909  } else {
910  dol_syslog(get_class($this)."::Fetch Error when reading lines of contracts linked to products");
911  return -3;
912  }
913 
914  // Now set the global properties on contract not stored into database.
915  $this->nbofservices = count($this->lines);
916  $this->total_ttc = price2num($total_ttc);
917  $this->total_tva = price2num($total_vat);
918  $this->total_ht = price2num($total_ht);
919 
920  return $this->lines;
921  }
922 
929  public function create($user)
930  {
931  global $conf, $langs, $mysoc;
932 
933  // Check parameters
934  $paramsok = 1;
935  if ($this->commercial_signature_id <= 0) {
936  $langs->load("commercial");
937  $this->error .= $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("SalesRepresentativeSignature"));
938  $paramsok = 0;
939  }
940  if ($this->commercial_suivi_id <= 0) {
941  $langs->load("commercial");
942  $this->error .= ($this->error ? "<br>" : '');
943  $this->error .= $langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("SalesRepresentativeFollowUp"));
944  $paramsok = 0;
945  }
946  if (!$paramsok) {
947  return -1;
948  }
949 
950 
951  $this->db->begin();
952 
953  $now = dol_now();
954 
955  // Insert contract
956  $sql = "INSERT INTO ".MAIN_DB_PREFIX."contrat (datec, fk_soc, fk_user_author, date_contrat,";
957  $sql .= " fk_commercial_signature, fk_commercial_suivi, fk_projet,";
958  $sql .= " ref, entity, note_private, note_public, ref_customer, ref_supplier, ref_ext)";
959  $sql .= " VALUES ('".$this->db->idate($now)."', ".((int) $this->socid).", ".((int) $user->id);
960  $sql .= ", ".(dol_strlen($this->date_contrat) != 0 ? "'".$this->db->idate($this->date_contrat)."'" : "NULL");
961  $sql .= ",".($this->commercial_signature_id > 0 ? ((int) $this->commercial_signature_id) : "NULL");
962  $sql .= ",".($this->commercial_suivi_id > 0 ? ((int) $this->commercial_suivi_id) : "NULL");
963  $sql .= ",".($this->fk_project > 0 ? ((int) $this->fk_project) : "NULL");
964  $sql .= ", ".(dol_strlen($this->ref) <= 0 ? "null" : "'".$this->db->escape($this->ref)."'");
965  $sql .= ", ".((int) $conf->entity);
966  $sql .= ", ".(!empty($this->note_private) ? ("'".$this->db->escape($this->note_private)."'") : "NULL");
967  $sql .= ", ".(!empty($this->note_public) ? ("'".$this->db->escape($this->note_public)."'") : "NULL");
968  $sql .= ", ".(!empty($this->ref_customer) ? ("'".$this->db->escape($this->ref_customer)."'") : "NULL");
969  $sql .= ", ".(!empty($this->ref_supplier) ? ("'".$this->db->escape($this->ref_supplier)."'") : "NULL");
970  $sql .= ", ".(!empty($this->ref_ext) ? ("'".$this->db->escape($this->ref_ext)."'") : "NULL");
971  $sql .= ")";
972  $resql = $this->db->query($sql);
973 
974  if ($resql) {
975  $error = 0;
976 
977  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."contrat");
978 
979  // Load object modContract
980  $module = (!empty($conf->global->CONTRACT_ADDON) ? $conf->global->CONTRACT_ADDON : 'mod_contract_serpis');
981  if (substr($module, 0, 13) == 'mod_contract_' && substr($module, -3) == 'php') {
982  $module = substr($module, 0, dol_strlen($module) - 4);
983  }
984  $result = dol_include_once('/core/modules/contract/'.$module.'.php');
985  if ($result > 0) {
986  $modCodeContract = new $module();
987 
988  if (!empty($modCodeContract->code_auto)) {
989  // Force the ref to a draft value if numbering module is an automatic numbering
990  $sql = 'UPDATE '.MAIN_DB_PREFIX."contrat SET ref='(PROV".$this->id.")' WHERE rowid=".((int) $this->id);
991  if ($this->db->query($sql)) {
992  if ($this->id) {
993  $this->ref = "(PROV".$this->id.")";
994  }
995  }
996  }
997  }
998 
999  if (!$error) {
1000  $result = $this->insertExtraFields();
1001  if ($result < 0) {
1002  $error++;
1003  }
1004  }
1005 
1006  // Insert business contacts ('SALESREPSIGN','contrat')
1007  if (!$error) {
1008  $result = $this->add_contact($this->commercial_signature_id, 'SALESREPSIGN', 'internal');
1009  if ($result < 0) {
1010  $error++;
1011  }
1012  }
1013 
1014  // Insert business contacts ('SALESREPFOLL','contrat')
1015  if (!$error) {
1016  $result = $this->add_contact($this->commercial_suivi_id, 'SALESREPFOLL', 'internal');
1017  if ($result < 0) {
1018  $error++;
1019  }
1020  }
1021 
1022  if (!$error) {
1023  if (!empty($this->linkedObjectsIds) && empty($this->linked_objects)) { // To use new linkedObjectsIds instead of old linked_objects
1024  $this->linked_objects = $this->linkedObjectsIds; // TODO Replace linked_objects with linkedObjectsIds
1025  }
1026 
1027  // Add object linked
1028  if (!$error && $this->id && !empty($this->linked_objects) && is_array($this->linked_objects)) {
1029  foreach ($this->linked_objects as $origin => $tmp_origin_id) {
1030  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, ...))
1031  foreach ($tmp_origin_id as $origin_id) {
1032  $ret = $this->add_object_linked($origin, $origin_id);
1033  if (!$ret) {
1034  $this->error = $this->db->lasterror();
1035  $error++;
1036  }
1037  }
1038  } else // Old behaviour, if linked_object has only one link per type, so is something like array('contract'=>id1))
1039  {
1040  $origin_id = $tmp_origin_id;
1041  $ret = $this->add_object_linked($origin, $origin_id);
1042  if (!$ret) {
1043  $this->error = $this->db->lasterror();
1044  $error++;
1045  }
1046  }
1047  }
1048  }
1049 
1050  if (!$error && $this->id && !empty($conf->global->MAIN_PROPAGATE_CONTACTS_FROM_ORIGIN) && !empty($this->origin) && !empty($this->origin_id)) { // Get contact from origin object
1051  $originforcontact = $this->origin;
1052  $originidforcontact = $this->origin_id;
1053  if ($originforcontact == 'shipping') { // shipment and order share the same contacts. If creating from shipment we take data of order
1054  require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
1055  $exp = new Expedition($this->db);
1056  $exp->fetch($this->origin_id);
1057  $exp->fetchObjectLinked();
1058  if (count($exp->linkedObjectsIds['commande']) > 0) {
1059  foreach ($exp->linkedObjectsIds['commande'] as $key => $value) {
1060  $originforcontact = 'commande';
1061  $originidforcontact = $value;
1062  break; // We take first one
1063  }
1064  }
1065  }
1066 
1067  $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";
1068  $sqlcontact .= " WHERE element_id = ".((int) $originidforcontact)." AND ec.fk_c_type_contact = ctc.rowid AND ctc.element = '".$this->db->escape($originforcontact)."'";
1069 
1070  $resqlcontact = $this->db->query($sqlcontact);
1071  if ($resqlcontact) {
1072  while ($objcontact = $this->db->fetch_object($resqlcontact)) {
1073  if ($objcontact->source == 'internal' && in_array($objcontact->code, array('SALESREPSIGN', 'SALESREPFOLL'))) {
1074  continue; // ignore this, already forced previously
1075  }
1076 
1077  //print $objcontact->code.'-'.$objcontact->source.'-'.$objcontact->fk_socpeople."\n";
1078  $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
1079  }
1080  } else {
1081  dol_print_error($resqlcontact);
1082  }
1083  }
1084  }
1085 
1086  if (!$error) {
1087  // Call trigger
1088  $result = $this->call_trigger('CONTRACT_CREATE', $user);
1089  if ($result < 0) {
1090  $error++;
1091  }
1092  // End call triggers
1093 
1094  if (!$error) {
1095  $this->db->commit();
1096  return $this->id;
1097  } else {
1098  dol_syslog(get_class($this)."::create - 30 - ".$this->error, LOG_ERR);
1099  $this->db->rollback();
1100  return -3;
1101  }
1102  } else {
1103  $this->error = "Failed to add contract";
1104  dol_syslog(get_class($this)."::create - 20 - ".$this->error, LOG_ERR);
1105  $this->db->rollback();
1106  return -2;
1107  }
1108  } else {
1109  $this->error = $langs->trans("UnknownError: ".$this->db->error()." -", LOG_DEBUG);
1110 
1111  $this->db->rollback();
1112  return -1;
1113  }
1114  }
1115 
1116 
1123  public function delete($user)
1124  {
1125  global $conf, $langs;
1126  require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1127 
1128  $error = 0;
1129 
1130  $this->db->begin();
1131 
1132  // Call trigger
1133  $result = $this->call_trigger('CONTRACT_DELETE', $user);
1134  if ($result < 0) {
1135  $error++;
1136  }
1137  // End call triggers
1138 
1139  if (!$error) {
1140  // Delete linked contacts
1141  $res = $this->delete_linked_contact();
1142  if ($res < 0) {
1143  dol_syslog(get_class($this)."::delete error", LOG_ERR);
1144  $error++;
1145  }
1146  }
1147 
1148  if (!$error) {
1149  // Delete linked object
1150  $res = $this->deleteObjectLinked();
1151  if ($res < 0) {
1152  $error++;
1153  }
1154  }
1155 
1156  if (!$error) {
1157  // Delete contratdet_log
1158  /*
1159  $sql = "DELETE cdl";
1160  $sql.= " FROM ".MAIN_DB_PREFIX."contratdet_log as cdl, ".MAIN_DB_PREFIX."contratdet as cd";
1161  $sql.= " WHERE cdl.fk_contratdet=cd.rowid AND cd.fk_contrat=".((int) $this->id);
1162  */
1163  $sql = "SELECT cdl.rowid as cdlrowid ";
1164  $sql .= " FROM ".MAIN_DB_PREFIX."contratdet_log as cdl, ".MAIN_DB_PREFIX."contratdet as cd";
1165  $sql .= " WHERE cdl.fk_contratdet=cd.rowid AND cd.fk_contrat=".((int) $this->id);
1166 
1167  dol_syslog(get_class($this)."::delete contratdet_log", LOG_DEBUG);
1168  $resql = $this->db->query($sql);
1169  if (!$resql) {
1170  $this->error = $this->db->error();
1171  $error++;
1172  }
1173  $numressql = $this->db->num_rows($resql);
1174  if (!$error && $numressql) {
1175  $tab_resql = array();
1176  for ($i = 0; $i < $numressql; $i++) {
1177  $objresql = $this->db->fetch_object($resql);
1178  $tab_resql[] = $objresql->cdlrowid;
1179  }
1180  $this->db->free($resql);
1181 
1182  $sql = "DELETE FROM ".MAIN_DB_PREFIX."contratdet_log ";
1183  $sql .= " WHERE ".MAIN_DB_PREFIX."contratdet_log.rowid IN (".$this->db->sanitize(implode(",", $tab_resql)).")";
1184 
1185  dol_syslog(get_class($this)."::delete contratdet_log", LOG_DEBUG);
1186  $resql = $this->db->query($sql);
1187  if (!$resql) {
1188  $this->error = $this->db->error();
1189  $error++;
1190  }
1191  }
1192  }
1193 
1194  // Delete lines
1195  if (!$error) {
1196  // Delete contratdet extrafields
1197  $main = MAIN_DB_PREFIX.'contratdet';
1198  $ef = $main."_extrafields";
1199  $sql = "DELETE FROM ".$ef." WHERE fk_object IN (SELECT rowid FROM ".$main." WHERE fk_contrat = ".((int) $this->id).")";
1200 
1201  dol_syslog(get_class($this)."::delete contratdet_extrafields", LOG_DEBUG);
1202  $resql = $this->db->query($sql);
1203  if (!$resql) {
1204  $this->error = $this->db->error();
1205  $error++;
1206  }
1207  }
1208 
1209  if (!$error) {
1210  // Delete contratdet
1211  $sql = "DELETE FROM ".MAIN_DB_PREFIX."contratdet";
1212  $sql .= " WHERE fk_contrat=".((int) $this->id);
1213 
1214  dol_syslog(get_class($this)."::delete contratdet", LOG_DEBUG);
1215  $resql = $this->db->query($sql);
1216  if (!$resql) {
1217  $this->error = $this->db->error();
1218  $error++;
1219  }
1220  }
1221 
1222  // Delete llx_ecm_files
1223  if (!$error) {
1224  $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 = ".((int) $this->id);
1225  $resql = $this->db->query($sql);
1226  if (!$resql) {
1227  $this->error = $this->db->lasterror();
1228  $this->errors[] = $this->error;
1229  $error++;
1230  }
1231  }
1232 
1233  // Delete contract
1234  if (!$error) {
1235  $sql = "DELETE FROM ".MAIN_DB_PREFIX."contrat";
1236  $sql .= " WHERE rowid=".((int) $this->id);
1237 
1238  dol_syslog(get_class($this)."::delete contrat", LOG_DEBUG);
1239  $resql = $this->db->query($sql);
1240  if (!$resql) {
1241  $this->error = $this->db->error();
1242  $error++;
1243  }
1244  }
1245 
1246  // Removed extrafields
1247  if (!$error) {
1248  $result = $this->deleteExtraFields();
1249  if ($result < 0) {
1250  $error++;
1251  dol_syslog(get_class($this)."::delete error -3 ".$this->error, LOG_ERR);
1252  }
1253  }
1254 
1255  if (!$error) {
1256  // We remove directory
1257  $ref = dol_sanitizeFileName($this->ref);
1258  if ($conf->contrat->dir_output) {
1259  $dir = $conf->contrat->multidir_output[$this->entity]."/".$ref;
1260  if (file_exists($dir)) {
1261  $res = @dol_delete_dir_recursive($dir);
1262  if (!$res) {
1263  $this->error = 'ErrorFailToDeleteDir';
1264  $error++;
1265  }
1266  }
1267  }
1268  }
1269 
1270  if (!$error) {
1271  $this->db->commit();
1272  return 1;
1273  } else {
1274  $this->error = $this->db->lasterror();
1275  $this->db->rollback();
1276  return -1;
1277  }
1278  }
1279 
1287  public function update($user, $notrigger = 0)
1288  {
1289  global $conf, $langs;
1290  $error = 0;
1291 
1292  // Clean parameters
1293  if (empty($this->fk_commercial_signature) && $this->commercial_signature_id > 0) {
1294  $this->fk_commercial_signature = $this->commercial_signature_id;
1295  }
1296  if (empty($this->fk_commercial_suivi) && $this->commercial_suivi_id > 0) {
1297  $this->fk_commercial_suivi = $this->commercial_suivi_id;
1298  }
1299  if (empty($this->fk_soc) && $this->socid > 0) {
1300  $this->fk_soc = (int) $this->socid;
1301  }
1302  if (empty($this->fk_project) && $this->projet > 0) {
1303  $this->fk_project = (int) $this->projet;
1304  }
1305 
1306  if (isset($this->ref)) {
1307  $this->ref = trim($this->ref);
1308  }
1309  if (isset($this->ref_customer)) {
1310  $this->ref_customer = trim($this->ref_customer);
1311  }
1312  if (isset($this->ref_supplier)) {
1313  $this->ref_supplier = trim($this->ref_supplier);
1314  }
1315  if (isset($this->ref_ext)) {
1316  $this->ref_ext = trim($this->ref_ext);
1317  }
1318  if (isset($this->entity)) {
1319  $this->entity = (int) $this->entity;
1320  }
1321  if (isset($this->statut)) {
1322  $this->statut = (int) $this->statut;
1323  }
1324  if (isset($this->fk_soc)) {
1325  $this->fk_soc = (int) $this->fk_soc;
1326  }
1327  if (isset($this->fk_commercial_signature)) {
1328  $this->fk_commercial_signature = trim($this->fk_commercial_signature);
1329  }
1330  if (isset($this->fk_commercial_suivi)) {
1331  $this->fk_commercial_suivi = trim($this->fk_commercial_suivi);
1332  }
1333  if (isset($this->note_private)) {
1334  $this->note_private = trim($this->note_private);
1335  }
1336  if (isset($this->note_public)) {
1337  $this->note_public = trim($this->note_public);
1338  }
1339  if (isset($this->import_key)) {
1340  $this->import_key = trim($this->import_key);
1341  }
1342  //if (isset($this->extraparams)) $this->extraparams=trim($this->extraparams);
1343 
1344  // Check parameters
1345  // Put here code to add a control on parameters values
1346 
1347  // Update request
1348  $sql = "UPDATE ".MAIN_DB_PREFIX."contrat SET";
1349  $sql .= " ref=".(isset($this->ref) ? "'".$this->db->escape($this->ref)."'" : "null").",";
1350  $sql .= " ref_customer=".(isset($this->ref_customer) ? "'".$this->db->escape($this->ref_customer)."'" : "null").",";
1351  $sql .= " ref_supplier=".(isset($this->ref_supplier) ? "'".$this->db->escape($this->ref_supplier)."'" : "null").",";
1352  $sql .= " ref_ext=".(isset($this->ref_ext) ? "'".$this->db->escape($this->ref_ext)."'" : "null").",";
1353  $sql .= " entity=".$conf->entity.",";
1354  $sql .= " date_contrat=".(dol_strlen($this->date_contrat) != 0 ? "'".$this->db->idate($this->date_contrat)."'" : 'null').",";
1355  $sql .= " statut=".(isset($this->statut) ? $this->statut : "null").",";
1356  $sql .= " fk_soc=".($this->fk_soc > 0 ? $this->fk_soc : "null").",";
1357  $sql .= " fk_projet=".($this->fk_project > 0 ? $this->fk_project : "null").",";
1358  $sql .= " fk_commercial_signature=".(isset($this->fk_commercial_signature) ? $this->fk_commercial_signature : "null").",";
1359  $sql .= " fk_commercial_suivi=".(isset($this->fk_commercial_suivi) ? $this->fk_commercial_suivi : "null").",";
1360  $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
1361  $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
1362  $sql .= " import_key=".(isset($this->import_key) ? "'".$this->db->escape($this->import_key)."'" : "null")."";
1363  //$sql.= " extraparams=".(isset($this->extraparams)?"'".$this->db->escape($this->extraparams)."'":"null")."";
1364  $sql .= " WHERE rowid=".((int) $this->id);
1365 
1366  $this->db->begin();
1367 
1368  $resql = $this->db->query($sql);
1369  if (!$resql) {
1370  $error++; $this->errors[] = "Error ".$this->db->lasterror();
1371  }
1372 
1373  if (!$error) {
1374  $result = $this->insertExtraFields();
1375  if ($result < 0) {
1376  $error++;
1377  }
1378  }
1379 
1380  if (!$error && !$notrigger) {
1381  // Call triggers
1382  $result = $this->call_trigger('CONTRACT_MODIFY', $user);
1383  if ($result < 0) {
1384  $error++;
1385  }
1386  // End call triggers
1387  }
1388 
1389  // Commit or rollback
1390  if ($error) {
1391  foreach ($this->errors as $errmsg) {
1392  dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
1393  $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
1394  }
1395  $this->db->rollback();
1396  return -1 * $error;
1397  } else {
1398  $this->db->commit();
1399  return 1;
1400  }
1401  }
1402 
1403 
1427  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)
1428  {
1429  global $user, $langs, $conf, $mysoc;
1430  $error = 0;
1431 
1432  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");
1433 
1434  // Check parameters
1435  if ($fk_product <= 0 && empty($desc)) {
1436  $this->error = "ErrorDescRequiredForFreeProductLines";
1437  return -1;
1438  }
1439 
1440  if ($this->statut >= 0) {
1441  // Clean parameters
1442  $pu_ht = price2num($pu_ht);
1443  $pu_ttc = price2num($pu_ttc);
1444  $pa_ht = price2num($pa_ht);
1445 
1446  // Clean vat code
1447  $reg = array();
1448  $vat_src_code = '';
1449  if (preg_match('/\((.*)\)/', $txtva, $reg)) {
1450  $vat_src_code = $reg[1];
1451  $txtva = preg_replace('/\s*\(.*\)/', '', $txtva); // Remove code into vatrate.
1452  }
1453  $txtva = price2num($txtva);
1454  $txlocaltax1 = price2num($txlocaltax1);
1455  $txlocaltax2 = price2num($txlocaltax2);
1456 
1457  $remise_percent = price2num($remise_percent);
1458  $qty = price2num($qty);
1459  if (empty($qty)) {
1460  $qty = 1;
1461  }
1462  if (empty($info_bits)) {
1463  $info_bits = 0;
1464  }
1465  if (empty($pu_ht) || !is_numeric($pu_ht)) {
1466  $pu_ht = 0;
1467  }
1468  if (empty($pu_ttc)) {
1469  $pu_ttc = 0;
1470  }
1471  if (empty($txtva) || !is_numeric($txtva)) {
1472  $txtva = 0;
1473  }
1474  if (empty($txlocaltax1) || !is_numeric($txlocaltax1)) {
1475  $txlocaltax1 = 0;
1476  }
1477  if (empty($txlocaltax2) || !is_numeric($txlocaltax2)) {
1478  $txlocaltax2 = 0;
1479  }
1480 
1481  if ($price_base_type == 'HT') {
1482  $pu = $pu_ht;
1483  } else {
1484  $pu = $pu_ttc;
1485  }
1486 
1487  // Check parameters
1488  if (empty($remise_percent)) {
1489  $remise_percent = 0;
1490  }
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  $remise = round(($pu_ht * $remise_percent / 100), 2);
1523  $price = $pu_ht - $remise;
1524  }
1525 
1526  if (empty($pa_ht)) {
1527  $pa_ht = 0;
1528  }
1529 
1530 
1531  // if buy price not defined, define buyprice as configured in margin admin
1532  if ($this->pa_ht == 0) {
1533  if (($result = $this->defineBuyPrice($pu_ht, $remise_percent, $fk_product)) < 0) {
1534  return $result;
1535  } else {
1536  $pa_ht = $result;
1537  }
1538  }
1539 
1540  // Insertion dans la base
1541  $sql = "INSERT INTO ".MAIN_DB_PREFIX."contratdet";
1542  $sql .= " (fk_contrat, label, description, fk_product, qty, tva_tx, vat_src_code,";
1543  $sql .= " localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice,";
1544  $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc,";
1545  $sql .= " info_bits,";
1546  $sql .= " price_ht, remise, fk_product_fournisseur_price, buy_price_ht";
1547  if ($date_start > 0) {
1548  $sql .= ",date_ouverture_prevue";
1549  }
1550  if ($date_end > 0) {
1551  $sql .= ",date_fin_validite";
1552  }
1553  $sql .= ", fk_unit";
1554  $sql .= ") VALUES (";
1555  $sql .= $this->id.", '', '".$this->db->escape($desc)."',";
1556  $sql .= ($fk_product > 0 ? $fk_product : "null").",";
1557  $sql .= " ".((float) $qty).",";
1558  $sql .= " ".((float) $txtva).",";
1559  $sql .= " ".($vat_src_code ? "'".$this->db->escape($vat_src_code)."'" : "null").",";
1560  $sql .= " ".((float) $txlocaltax1).",";
1561  $sql .= " ".((float) $txlocaltax2).",";
1562  $sql .= " '".$this->db->escape($localtax1_type)."',";
1563  $sql .= " '".$this->db->escape($localtax2_type)."',";
1564  $sql .= " ".price2num($remise_percent).",";
1565  $sql .= " ".price2num($pu_ht).",";
1566  $sql .= " ".price2num($total_ht).",".price2num($total_tva).",".price2num($total_localtax1).",".price2num($total_localtax2).",".price2num($total_ttc).",";
1567  $sql .= " '".$this->db->escape($info_bits)."',";
1568  $sql .= " ".price2num($price).",".price2num($remise).",";
1569  if (isset($fk_fournprice)) {
1570  $sql .= ' '.((int) $fk_fournprice).',';
1571  } else {
1572  $sql .= ' null,';
1573  }
1574  if (isset($pa_ht)) {
1575  $sql .= ' '.price2num($pa_ht);
1576  } else {
1577  $sql .= ' null';
1578  }
1579  if ($date_start > 0) {
1580  $sql .= ",'".$this->db->idate($date_start)."'";
1581  }
1582  if ($date_end > 0) {
1583  $sql .= ",'".$this->db->idate($date_end)."'";
1584  }
1585  $sql .= ", ".($fk_unit ? "'".$this->db->escape($fk_unit)."'" : "null");
1586  $sql .= ")";
1587 
1588  $resql = $this->db->query($sql);
1589  if ($resql) {
1590  $contractlineid = $this->db->last_insert_id(MAIN_DB_PREFIX."contratdet");
1591 
1592  if (!$error) {
1593  $contractline = new ContratLigne($this->db);
1594  $contractline->array_options = $array_options;
1595  $contractline->id = $contractlineid;
1596  $result = $contractline->insertExtraFields();
1597  if ($result < 0) {
1598  $this->error[] = $contractline->error;
1599  $error++;
1600  }
1601  }
1602 
1603  if (empty($error)) {
1604  // Call trigger
1605  $result = $this->call_trigger('LINECONTRACT_INSERT', $user);
1606  if ($result < 0) {
1607  $error++;
1608  }
1609  // End call triggers
1610  }
1611 
1612  if ($error) {
1613  $this->db->rollback();
1614  return -1;
1615  } else {
1616  $this->db->commit();
1617  return $contractlineid;
1618  }
1619  } else {
1620  $this->db->rollback();
1621  $this->error = $this->db->error()." sql=".$sql;
1622  return -1;
1623  }
1624  } else {
1625  dol_syslog(get_class($this)."::addline ErrorTryToAddLineOnValidatedContract", LOG_ERR);
1626  return -2;
1627  }
1628  }
1629 
1653  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)
1654  {
1655  global $user, $conf, $langs, $mysoc;
1656 
1657  $error = 0;
1658 
1659  // Clean parameters
1660  $qty = trim($qty);
1661  $desc = trim($desc);
1662  $desc = trim($desc);
1663  $price = price2num($pu);
1664  $tvatx = price2num($tvatx);
1665  $localtax1tx = price2num($localtax1tx);
1666  $localtax2tx = price2num($localtax2tx);
1667  $pa_ht = price2num($pa_ht);
1668  if (empty($fk_fournprice)) {
1669  $fk_fournprice = 0;
1670  }
1671 
1672  $subprice = $price;
1673  $remise = 0;
1674  if (dol_strlen($remise_percent) > 0) {
1675  $remise = round(($pu * $remise_percent / 100), 2);
1676  $price = $pu - $remise;
1677  } else {
1678  $remise_percent = 0;
1679  }
1680 
1681  if ($date_start && $date_end && $date_start > $date_end) {
1682  $langs->load("errors");
1683  $this->error = $langs->trans('ErrorStartDateGreaterEnd');
1684  return -1;
1685  }
1686 
1687  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");
1688 
1689  $this->db->begin();
1690 
1691  // Calcul du total TTC et de la TVA pour la ligne a partir de
1692  // qty, pu, remise_percent et tvatx
1693  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
1694  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
1695 
1696  $localtaxes_type = getLocalTaxesFromRate($tvatx, 0, $this->societe, $mysoc);
1697  $tvatx = preg_replace('/\s*\(.*\)/', '', $tvatx); // Remove code into vatrate.
1698 
1699  $tabprice = calcul_price_total($qty, $pu, $remise_percent, $tvatx, $localtax1tx, $localtax2tx, 0, $price_base_type, $info_bits, 1, $mysoc, $localtaxes_type);
1700  $total_ht = $tabprice[0];
1701  $total_tva = $tabprice[1];
1702  $total_ttc = $tabprice[2];
1703  $total_localtax1 = $tabprice[9];
1704  $total_localtax2 = $tabprice[10];
1705 
1706  $localtax1_type = $localtaxes_type[0];
1707  $localtax2_type = $localtaxes_type[2];
1708 
1709  // TODO A virer
1710  // Anciens indicateurs: $price, $remise (a ne plus utiliser)
1711  $remise = 0;
1712  $price = price2num(round($pu, 2));
1713  if (dol_strlen($remise_percent) > 0) {
1714  $remise = round(($pu * $remise_percent / 100), 2);
1715  $price = $pu - $remise;
1716  }
1717 
1718  if (empty($pa_ht)) {
1719  $pa_ht = 0;
1720  }
1721 
1722  // if buy price not defined, define buyprice as configured in margin admin
1723  if ($this->pa_ht == 0) {
1724  if (($result = $this->defineBuyPrice($pu, $remise_percent)) < 0) {
1725  return $result;
1726  } else {
1727  $pa_ht = $result;
1728  }
1729  }
1730 
1731  $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet set description = '".$this->db->escape($desc)."'";
1732  $sql .= ",price_ht = ".((float) price2num($price));
1733  $sql .= ",subprice = ".((float) price2num($subprice));
1734  $sql .= ",remise = ".((float) price2num($remise));
1735  $sql .= ",remise_percent = ".((float) price2num($remise_percent));
1736  $sql .= ",qty = ".((float) $qty);
1737  $sql .= ",tva_tx = ".((float) price2num($tvatx));
1738  $sql .= ",localtax1_tx = ".((float) price2num($localtax1tx));
1739  $sql .= ",localtax2_tx = ".((float) price2num($localtax2tx));
1740  $sql .= ",localtax1_type='".$this->db->escape($localtax1_type);
1741  $sql .= ",localtax2_type='".$this->db->escape($localtax2_type);
1742  $sql .= ", total_ht = ".((float) price2num($total_ht));
1743  $sql .= ", total_tva = ".((float) price2num($total_tva));
1744  $sql .= ", total_localtax1 = ".((float) price2num($total_localtax1));
1745  $sql .= ", total_localtax2 = ".((float) price2num($total_localtax2));
1746  $sql .= ", total_ttc = ".((float) price2num($total_ttc));
1747  $sql .= ", fk_product_fournisseur_price=".($fk_fournprice > 0 ? $fk_fournprice : "null");
1748  $sql .= ", buy_price_ht = ".((float) price2num($pa_ht));
1749  if ($date_start > 0) {
1750  $sql .= ",date_ouverture_prevue = '".$this->db->idate($date_start)."'";
1751  } else {
1752  $sql .= ",date_ouverture_prevue = null";
1753  }
1754  if ($date_end > 0) {
1755  $sql .= ",date_fin_validite = '".$this->db->idate($date_end)."'";
1756  } else {
1757  $sql .= ",date_fin_validite = null";
1758  }
1759  if ($date_debut_reel > 0) {
1760  $sql .= ",date_ouverture = '".$this->db->idate($date_debut_reel)."'";
1761  } else {
1762  $sql .= ",date_ouverture = null";
1763  }
1764  if ($date_fin_reel > 0) {
1765  $sql .= ",date_cloture = '".$this->db->idate($date_fin_reel)."'";
1766  } else {
1767  $sql .= ",date_cloture = null";
1768  }
1769  $sql .= ", fk_unit = ".($fk_unit > 0 ? ((int) $fk_unit) : "null");
1770  $sql .= " WHERE rowid = ".((int) $rowid);
1771 
1772  dol_syslog(get_class($this)."::updateline", LOG_DEBUG);
1773  $result = $this->db->query($sql);
1774  if ($result) {
1775  if (is_array($array_options) && count($array_options) > 0) { // For avoid conflicts if trigger used
1776  $contractline = new ContratLigne($this->db);
1777  $contractline->fetch($rowid);
1778 
1779  // We replace values in $contractline->array_options only for entries defined into $array_options
1780  foreach ($array_options as $key => $value) {
1781  $contractline->array_options[$key] = $array_options[$key];
1782  }
1783 
1784  $result = $contractline->insertExtraFields();
1785  if ($result < 0) {
1786  $this->error[] = $contractline->error;
1787  $error++;
1788  }
1789  }
1790 
1791  if (empty($error)) {
1792  // Call trigger
1793  $result = $this->call_trigger('LINECONTRACT_MODIFY', $user);
1794  if ($result < 0) {
1795  $this->db->rollback();
1796  return -3;
1797  }
1798  // End call triggers
1799 
1800  $this->db->commit();
1801  return 1;
1802  }
1803  } else {
1804  $this->db->rollback();
1805  $this->error = $this->db->error();
1806  dol_syslog(get_class($this)."::updateline Erreur -1");
1807  return -1;
1808  }
1809  }
1810 
1818  public function deleteline($idline, User $user)
1819  {
1820  global $conf, $langs;
1821 
1822  $error = 0;
1823 
1824  if ($this->statut >= 0) {
1825  // Call trigger
1826  $result = $this->call_trigger('LINECONTRACT_DELETE', $user);
1827  if ($result < 0) {
1828  return -1;
1829  }
1830  // End call triggers
1831 
1832  $this->db->begin();
1833 
1834  $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element_line;
1835  $sql .= " WHERE rowid = ".((int) $idline);
1836 
1837  dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
1838  $resql = $this->db->query($sql);
1839  if (!$resql) {
1840  $this->error = "Error ".$this->db->lasterror();
1841  $error++;
1842  }
1843 
1844  if (!$error) {
1845  // Remove extrafields
1846  $contractline = new ContratLigne($this->db);
1847  $contractline->id = $idline;
1848  $result = $contractline->deleteExtraFields();
1849  if ($result < 0) {
1850  $error++;
1851  $this->error = "Error ".get_class($this)."::deleteline deleteExtraFields error -4 ".$contractline->error;
1852  }
1853  }
1854 
1855  if (empty($error)) {
1856  $this->db->commit();
1857  return 1;
1858  } else {
1859  dol_syslog(get_class($this)."::deleteline ERROR:".$this->error, LOG_ERR);
1860  $this->db->rollback();
1861  return -1;
1862  }
1863  } else {
1864  $this->error = 'ErrorDeleteLineNotAllowedByObjectStatus';
1865  return -2;
1866  }
1867  }
1868 
1869 
1870  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1878  public function update_statut($user)
1879  {
1880  // phpcs:enable
1881  dol_syslog(__METHOD__." is deprecated", LOG_WARNING);
1882 
1883  // If draft, we keep it (should not happen)
1884  if ($this->statut == 0) {
1885  return 1;
1886  }
1887 
1888  // Load $this->lines array
1889  // $this->fetch_lines();
1890 
1891  // $newstatut=1;
1892  // foreach($this->lines as $key => $contractline)
1893  // {
1894  // // if ($contractline) // Loop on each service
1895  // }
1896 
1897  return 1;
1898  }
1899 
1900 
1907  public function getLibStatut($mode)
1908  {
1909  return $this->LibStatut($this->statut, $mode);
1910  }
1911 
1912  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1920  public function LibStatut($status, $mode)
1921  {
1922  // phpcs:enable
1923  global $langs;
1924 
1925  if (empty($this->labelStatus) || empty($this->labelStatusShort)) {
1926  global $langs;
1927  $langs->load("contracts");
1928  $this->labelStatus[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('ContractStatusDraft');
1929  $this->labelStatus[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('ContractStatusValidated');
1930  $this->labelStatus[self::STATUS_CLOSED] = $langs->transnoentitiesnoconv('ContractStatusClosed');
1931  $this->labelStatusShort[self::STATUS_DRAFT] = $langs->transnoentitiesnoconv('ContractStatusDraft');
1932  $this->labelStatusShort[self::STATUS_VALIDATED] = $langs->transnoentitiesnoconv('ContractStatusValidated');
1933  $this->labelStatusShort[self::STATUS_CLOSED] = $langs->transnoentitiesnoconv('ContractStatusClosed');
1934  }
1935 
1936  $statusType = 'status'.$status;
1937  if ($status == self::STATUS_VALIDATED) {
1938  $statusType = 'status6';
1939  }
1940 
1941  if ($mode == 4 || $mode == 6 || $mode == 7) {
1942  $text = '';
1943  if ($mode == 4) {
1944  $text = '<span class="hideonsmartphone">';
1945  $text .= ($this->nbofserviceswait + $this->nbofservicesopened + $this->nbofservicesexpired + $this->nbofservicesclosed);
1946  $text .= ' '.$langs->trans("Services");
1947  $text .= ': &nbsp; &nbsp; ';
1948  $text .= '</span>';
1949  }
1950  $text .= ($mode == 7 ? '<span class="nowraponall">' : '');
1951  $text .= ($mode != 7 || $this->nbofserviceswait > 0) ? ($this->nbofserviceswait.ContratLigne::LibStatut(0, 3, -1, 'class="marginleft2"')).(($mode != 7 || $this->nbofservicesopened || $this->nbofservicesexpired || $this->nbofservicesclosed) ? ' &nbsp; ' : '') : '';
1952  $text .= ($mode == 7 ? '</span><span class="nowraponall">' : '');
1953  $text .= ($mode != 7 || $this->nbofservicesopened > 0) ? ($this->nbofservicesopened.ContratLigne::LibStatut(4, 3, 0, 'class="marginleft2"')).(($mode != 7 || $this->nbofservicesexpired || $this->nbofservicesclosed) ? ' &nbsp; ' : '') : '';
1954  $text .= ($mode == 7 ? '</span><span class="nowraponall">' : '');
1955  $text .= ($mode != 7 || $this->nbofservicesexpired > 0) ? ($this->nbofservicesexpired.ContratLigne::LibStatut(4, 3, 1, 'class="marginleft2"')).(($mode != 7 || $this->nbofservicesclosed) ? ' &nbsp; ' : '') : '';
1956  $text .= ($mode == 7 ? '</span><span class="nowraponall">' : '');
1957  $text .= ($mode != 7 || $this->nbofservicesclosed > 0) ? ($this->nbofservicesclosed.ContratLigne::LibStatut(5, 3, -1, 'class="marginleft2"')) : '';
1958  $text .= ($mode == 7 ? '</span>' : '');
1959  return $text;
1960  } else {
1961  return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode);
1962  }
1963  }
1964 
1965 
1975  public function getNomUrl($withpicto = 0, $maxlength = 0, $notooltip = 0, $save_lastsearch_value = -1)
1976  {
1977  global $conf, $langs, $user, $hookmanager;
1978 
1979  $result = '';
1980 
1981  $url = DOL_URL_ROOT.'/contrat/card.php?id='.$this->id;
1982 
1983  //if ($option !== 'nolink')
1984  //{
1985  // Add param to save lastsearch_values or not
1986  $add_save_lastsearch_values = ($save_lastsearch_value == 1 ? 1 : 0);
1987  if ($save_lastsearch_value == -1 && preg_match('/list\.php/', $_SERVER["PHP_SELF"])) {
1988  $add_save_lastsearch_values = 1;
1989  }
1990  if ($add_save_lastsearch_values) {
1991  $url .= '&save_lastsearch_values=1';
1992  }
1993  //}
1994 
1995  $label = '';
1996 
1997  if ($user->rights->contrat->lire) {
1998  $label = img_picto('', $this->picto).' <u class="paddingrightonly">'.$langs->trans("Contract").'</u>';
1999  /* Status of a contract is status of all services, so disabled
2000  if (isset($this->statut)) {
2001  $label .= ' '.$this->getLibStatut(5);
2002  }*/
2003  $label .= '<br><b>'.$langs->trans('Ref').':</b> '.($this->ref ? $this->ref : $this->id);
2004  $ref_customer = (!empty($this->ref_customer) ? $this->ref_customer : (empty($this->ref_client) ? '' : $this->ref_client));
2005  $label .= '<br><b>'.$langs->trans('RefCustomer').':</b> '.$ref_customer;
2006  $label .= '<br><b>'.$langs->trans('RefSupplier').':</b> '.$this->ref_supplier;
2007  if (!empty($this->total_ht)) {
2008  $label .= '<br><b>'.$langs->trans('AmountHT').':</b> '.price($this->total_ht, 0, $langs, 0, -1, -1, $conf->currency);
2009  }
2010  if (!empty($this->total_tva)) {
2011  $label .= '<br><b>'.$langs->trans('VAT').':</b> '.price($this->total_tva, 0, $langs, 0, -1, -1, $conf->currency);
2012  }
2013  if (!empty($this->total_ttc)) {
2014  $label .= '<br><b>'.$langs->trans('AmountTTC').':</b> '.price($this->total_ttc, 0, $langs, 0, -1, -1, $conf->currency);
2015  }
2016  }
2017 
2018  $linkclose = '';
2019  if (empty($notooltip) && $user->rights->contrat->lire) {
2020  if (!empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER)) {
2021  $label = $langs->trans("ShowOrder");
2022  $linkclose .= ' alt="'.dol_escape_htmltag($label, 1).'"';
2023  }
2024  $linkclose .= ' title="'.dol_escape_htmltag($label, 1).'"';
2025  $linkclose .= ' class="classfortooltip"';
2026  }
2027 
2028  $linkstart = '<a href="'.$url.'"';
2029  $linkstart .= $linkclose.'>';
2030  $linkend = '</a>';
2031 
2032  $result .= $linkstart;
2033  if ($withpicto) {
2034  $result .= img_object(($notooltip ? '' : $label), $this->picto, ($notooltip ? (($withpicto != 2) ? 'class="paddingright"' : '') : 'class="'.(($withpicto != 2) ? 'paddingright ' : '').'classfortooltip"'), 0, 0, $notooltip ? 0 : 1);
2035  }
2036  if ($withpicto != 2) {
2037  $result .= ($this->ref ? $this->ref : $this->id);
2038  }
2039  $result .= $linkend;
2040 
2041  global $action;
2042  $hookmanager->initHooks(array('contractdao'));
2043  $parameters = array('id'=>$this->id, 'getnomurl' => &$result);
2044  $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
2045  if ($reshook > 0) {
2046  $result = $hookmanager->resPrint;
2047  } else {
2048  $result .= $hookmanager->resPrint;
2049  }
2050 
2051  return $result;
2052  }
2053 
2060  public function info($id)
2061  {
2062  $sql = "SELECT c.rowid, c.ref, c.datec,";
2063  $sql .= " c.tms as date_modification,";
2064  $sql .= " fk_user_author";
2065  $sql .= " FROM ".MAIN_DB_PREFIX."contrat as c";
2066  $sql .= " WHERE c.rowid = ".((int) $id);
2067 
2068  $result = $this->db->query($sql);
2069  if ($result) {
2070  if ($this->db->num_rows($result)) {
2071  $obj = $this->db->fetch_object($result);
2072 
2073  $this->id = $obj->rowid;
2074 
2075  if ($obj->fk_user_author) {
2076  $cuser = new User($this->db);
2077  $cuser->fetch($obj->fk_user_author);
2078  $this->user_creation = $cuser;
2079  }
2080 
2081  $this->ref = (!$obj->ref) ? $obj->rowid : $obj->ref;
2082  $this->date_creation = $this->db->jdate($obj->datec);
2083  $this->date_modification = $this->db->jdate($obj->date_modification);
2084  }
2085 
2086  $this->db->free($result);
2087  } else {
2088  dol_print_error($this->db);
2089  }
2090  }
2091 
2092  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2099  public function array_detail($status = -1)
2100  {
2101  // phpcs:enable
2102  $tab = array();
2103 
2104  $sql = "SELECT cd.rowid";
2105  $sql .= " FROM ".MAIN_DB_PREFIX."contratdet as cd";
2106  $sql .= " WHERE fk_contrat =".((int) $this->id);
2107  if ($status >= 0) {
2108  $sql .= " AND statut = ".((int) $status);
2109  }
2110 
2111  dol_syslog(get_class($this)."::array_detail()", LOG_DEBUG);
2112  $resql = $this->db->query($sql);
2113  if ($resql) {
2114  $num = $this->db->num_rows($resql);
2115  $i = 0;
2116  while ($i < $num) {
2117  $obj = $this->db->fetch_object($resql);
2118  $tab[$i] = $obj->rowid;
2119  $i++;
2120  }
2121  return $tab;
2122  } else {
2123  $this->error = $this->db->error();
2124  return -1;
2125  }
2126  }
2127 
2137  public function getListOfContracts($option = 'all', $status = [], $product_categories = [], $line_status = [])
2138  {
2139  $tab = array();
2140 
2141  $sql = "SELECT c.rowid";
2142  $sql .= " FROM ".MAIN_DB_PREFIX."contrat as c";
2143  if (!empty($product_categories)) {
2144  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."contratdet as cd ON cd.fk_contrat = c.rowid";
2145  $sql .= " INNER JOIN ".MAIN_DB_PREFIX."categorie_product as cp ON cp.fk_product = cd.fk_product AND cp.fk_categorie IN (".$this->db->sanitize(implode(', ', $product_categories)).")";
2146  }
2147  $sql .= " WHERE c.fk_soc =".((int) $this->socid);
2148  $sql .= ($option == 'others') ? " AND c.rowid <> ".((int) $this->id) : "";
2149  $sql .= (!empty($status)) ? " AND c.statut IN (".$this->db->sanitize(implode(', ', $status)).")" : "";
2150  $sql .= (!empty($line_status)) ? " AND cd.statut IN (".$this->db->sanitize(implode(', ', $line_status)).")" : "";
2151  $sql .= " GROUP BY c.rowid";
2152 
2153  dol_syslog(get_class($this)."::getOtherContracts()", LOG_DEBUG);
2154  $resql = $this->db->query($sql);
2155  if ($resql) {
2156  $num = $this->db->num_rows($resql);
2157  $i = 0;
2158  while ($i < $num) {
2159  $obj = $this->db->fetch_object($resql);
2160  $contrat = new Contrat($this->db);
2161  $contrat->fetch($obj->rowid);
2162  $tab[$contrat->id] = $contrat;
2163  $i++;
2164  }
2165  return $tab;
2166  } else {
2167  $this->error = $this->db->lasterror();
2168  return -1;
2169  }
2170  }
2171 
2172 
2173  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2181  public function load_board($user, $mode)
2182  {
2183  // phpcs:enable
2184  global $conf, $langs;
2185 
2186  $this->from = " FROM ".MAIN_DB_PREFIX."contrat as c";
2187  $this->from .= ", ".MAIN_DB_PREFIX."contratdet as cd";
2188  $this->from .= ", ".MAIN_DB_PREFIX."societe as s";
2189  if (empty($user->rights->societe->client->voir) && !$user->socid) {
2190  $this->from .= ", ".MAIN_DB_PREFIX."societe_commerciaux as sc";
2191  }
2192 
2193  if ($mode == 'inactive') {
2194  $sql = "SELECT cd.rowid, cd.date_ouverture_prevue as datefin";
2195  $sql .= $this->from;
2196  $sql .= " WHERE c.statut = 1";
2197  $sql .= " AND c.rowid = cd.fk_contrat";
2198  $sql .= " AND cd.statut = 0";
2199  } elseif ($mode == 'expired') {
2200  $sql = "SELECT cd.rowid, cd.date_fin_validite as datefin";
2201  $sql .= $this->from;
2202  $sql .= " WHERE c.statut = 1";
2203  $sql .= " AND c.rowid = cd.fk_contrat";
2204  $sql .= " AND cd.statut = 4";
2205  $sql .= " AND cd.date_fin_validite < '".$this->db->idate(dol_now())."'";
2206  } elseif ($mode == 'active') {
2207  $sql = "SELECT cd.rowid, cd.date_fin_validite as datefin";
2208  $sql .= $this->from;
2209  $sql .= " WHERE c.statut = 1";
2210  $sql .= " AND c.rowid = cd.fk_contrat";
2211  $sql .= " AND cd.statut = 4";
2212  //$datetouse = dol_now();
2213  //$sql.= " AND cd.date_fin_validite < '".$this->db->idate($datetouse)."'";
2214  }
2215  $sql .= " AND c.fk_soc = s.rowid";
2216  $sql .= " AND c.entity = ".((int) $conf->entity);
2217  if ($user->socid) {
2218  $sql .= " AND c.fk_soc = ".((int) $user->socid);
2219  }
2220  if (empty($user->rights->societe->client->voir) && !$user->socid) {
2221  $sql .= " AND c.fk_soc = sc.fk_soc AND sc.fk_user = ".((int) $user->id);
2222  }
2223 
2224  $resql = $this->db->query($sql);
2225  if ($resql) {
2226  $langs->load("contracts");
2227  $now = dol_now();
2228 
2229  if ($mode == 'inactive') {
2230  $warning_delay = $conf->contrat->services->inactifs->warning_delay;
2231  $label = $langs->trans("BoardNotActivatedServices");
2232  $labelShort = $langs->trans("BoardNotActivatedServicesShort");
2233  $url = DOL_URL_ROOT.'/contrat/services_list.php?mainmenu=commercial&leftmenu=contracts&mode=0&sortfield=cd.date_fin_validite&sortorder=asc';
2234  } elseif ($mode == 'expired') {
2235  $warning_delay = $conf->contrat->services->expires->warning_delay;
2236  $url = DOL_URL_ROOT.'/contrat/services_list.php?mainmenu=commercial&leftmenu=contracts&mode=4&filter=expired&sortfield=cd.date_fin_validite&sortorder=asc';
2237  $label = $langs->trans("BoardExpiredServices");
2238  $labelShort = $langs->trans("BoardExpiredServicesShort");
2239  } else {
2240  $warning_delay = $conf->contrat->services->expires->warning_delay;
2241  $url = DOL_URL_ROOT.'/contrat/services_list.php?mainmenu=commercial&leftmenu=contracts&mode=4&sortfield=cd.date_fin_validite&sortorder=asc';
2242  //$url.= '&op2day='.$arraydatetouse['mday'].'&op2month='.$arraydatetouse['mon'].'&op2year='.$arraydatetouse['year'];
2243  //if ($warning_delay >= 0) $url.='&amp;filter=expired';
2244  $label = $langs->trans("BoardRunningServices");
2245  $labelShort = $langs->trans("BoardRunningServicesShort");
2246  }
2247 
2248  $response = new WorkboardResponse();
2249  $response->warning_delay = $warning_delay / 60 / 60 / 24;
2250  $response->label = $label;
2251  $response->labelShort = $labelShort;
2252  $response->url = $url;
2253  $response->img = img_object('', "contract");
2254 
2255  while ($obj = $this->db->fetch_object($resql)) {
2256  $response->nbtodo++;
2257 
2258  if ($obj->datefin && $this->db->jdate($obj->datefin) < ($now - $warning_delay)) {
2259  $response->nbtodolate++;
2260  }
2261  }
2262 
2263  return $response;
2264  } else {
2265  dol_print_error($this->db);
2266  $this->error = $this->db->error();
2267  return -1;
2268  }
2269  }
2270 
2271  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2277  public function load_state_board()
2278  {
2279  // phpcs:enable
2280  global $conf, $user;
2281 
2282  $this->nb = array();
2283  $clause = "WHERE";
2284 
2285  $sql = "SELECT count(c.rowid) as nb";
2286  $sql .= " FROM ".MAIN_DB_PREFIX."contrat as c";
2287  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe as s ON c.fk_soc = s.rowid";
2288  if (empty($user->rights->societe->client->voir) && !$user->socid) {
2289  $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."societe_commerciaux as sc ON s.rowid = sc.fk_soc";
2290  $sql .= " WHERE sc.fk_user = ".((int) $user->id);
2291  $clause = "AND";
2292  }
2293  $sql .= " ".$clause." c.entity = ".$conf->entity;
2294 
2295  $resql = $this->db->query($sql);
2296  if ($resql) {
2297  while ($obj = $this->db->fetch_object($resql)) {
2298  $this->nb["contracts"] = $obj->nb;
2299  }
2300  $this->db->free($resql);
2301  return 1;
2302  } else {
2303  dol_print_error($this->db);
2304  $this->error = $this->db->error();
2305  return -1;
2306  }
2307  }
2308 
2309 
2310  /* gestion des contacts d'un contrat */
2311 
2317  public function getIdBillingContact()
2318  {
2319  return $this->getIdContact('external', 'BILLING');
2320  }
2321 
2327  public function getIdServiceContact()
2328  {
2329  return $this->getIdContact('external', 'SERVICE');
2330  }
2331 
2332 
2340  public function initAsSpecimen()
2341  {
2342  global $user, $langs, $conf;
2343 
2344  // Load array of products prodids
2345  $num_prods = 0;
2346  $prodids = array();
2347  $sql = "SELECT rowid";
2348  $sql .= " FROM ".MAIN_DB_PREFIX."product";
2349  $sql .= " WHERE entity IN (".getEntity('product').")";
2350  $sql .= " AND tosell = 1";
2351  $sql .= $this->db->plimit(100);
2352 
2353  $resql = $this->db->query($sql);
2354  if ($resql) {
2355  $num_prods = $this->db->num_rows($resql);
2356  $i = 0;
2357  while ($i < $num_prods) {
2358  $i++;
2359  $row = $this->db->fetch_row($resql);
2360  $prodids[$i] = $row[0];
2361  }
2362  }
2363 
2364  // Initialise parametres
2365  $this->id = 0;
2366  $this->specimen = 1;
2367 
2368  $this->ref = 'SPECIMEN';
2369  $this->ref_customer = 'SPECIMENCUST';
2370  $this->ref_supplier = 'SPECIMENSUPP';
2371  $this->socid = 1;
2372  $this->statut = 0;
2373  $this->date_creation = (dol_now() - 3600 * 24 * 7);
2374  $this->date_contrat = dol_now();
2375  $this->commercial_signature_id = 1;
2376  $this->commercial_suivi_id = 1;
2377  $this->note_private = 'This is a comment (private)';
2378  $this->note_public = 'This is a comment (public)';
2379  $this->fk_projet = 0;
2380  // Lines
2381  $nbp = 5;
2382  $xnbp = 0;
2383  while ($xnbp < $nbp) {
2384  $line = new ContratLigne($this->db);
2385  $line->qty = 1;
2386  $line->subprice = 100;
2387  $line->price = 100;
2388  $line->tva_tx = 19.6;
2389  $line->remise_percent = 10;
2390  $line->total_ht = 90;
2391  $line->total_ttc = 107.64; // 90 * 1.196
2392  $line->total_tva = 17.64;
2393  $line->date_start = dol_now() - 500000;
2394  $line->date_start_real = dol_now() - 200000;
2395  $line->date_end = dol_now() + 500000;
2396  $line->date_end_real = dol_now() - 100000;
2397  if ($num_prods > 0) {
2398  $prodid = mt_rand(1, $num_prods);
2399  $line->fk_product = $prodids[$prodid];
2400  }
2401  $this->lines[$xnbp] = $line;
2402  $xnbp++;
2403  }
2404  }
2405 
2411  public function getLinesArray()
2412  {
2413  return $this->fetch_lines();
2414  }
2415 
2416 
2428  public function generateDocument($modele, $outputlangs, $hidedetails = 0, $hidedesc = 0, $hideref = 0, $moreparams = null)
2429  {
2430  global $conf, $langs;
2431 
2432  $langs->load("contracts");
2433  $outputlangs->load("products");
2434 
2435  if (!dol_strlen($modele)) {
2436  $modele = 'strato';
2437 
2438  if (!empty($this->model_pdf)) {
2439  $modele = $this->model_pdf;
2440  } elseif (!empty($this->modelpdf)) { // deprecated
2441  $modele = $this->modelpdf;
2442  } elseif (!empty($conf->global->CONTRACT_ADDON_PDF)) {
2443  $modele = $conf->global->CONTRACT_ADDON_PDF;
2444  }
2445  }
2446 
2447  $modelpath = "core/modules/contract/doc/";
2448 
2449  return $this->commonGenerateDocument($modelpath, $modele, $outputlangs, $hidedetails, $hidedesc, $hideref, $moreparams);
2450  }
2451 
2460  public static function replaceThirdparty(DoliDB $db, $origin_id, $dest_id)
2461  {
2462  $tables = array(
2463  'contrat'
2464  );
2465 
2466  return CommonObject::commonReplaceThirdparty($db, $origin_id, $dest_id, $tables);
2467  }
2468 
2477  public static function replaceProduct(DoliDB $db, $origin_id, $dest_id)
2478  {
2479  $tables = array(
2480  'contratdet'
2481  );
2482 
2483  return CommonObject::commonReplaceProduct($db, $origin_id, $dest_id, $tables);
2484  }
2485 
2494  public function createFromClone(User $user, $socid = 0, $notrigger = 0)
2495  {
2496  global $db, $langs, $conf, $hookmanager, $extrafields;
2497 
2498  dol_include_once('/projet/class/project.class.php');
2499 
2500  $error = 0;
2501 
2502  $this->fetch($this->id);
2503 
2504  // Load dest object
2505  $clonedObj = clone $this;
2506  $clonedObj->socid = $socid;
2507 
2508  $this->db->begin();
2509 
2510  $objsoc = new Societe($this->db);
2511 
2512  $objsoc->fetch($clonedObj->socid);
2513 
2514  // Clean data
2515  $clonedObj->statut = 0;
2516  // Clean extrafields
2517  if (is_array($clonedObj->array_options) && count($clonedObj->array_options) > 0) {
2518  $extrafields->fetch_name_optionals_label($this->table_element);
2519  foreach ($clonedObj->array_options as $key => $option) {
2520  $shortkey = preg_replace('/options_/', '', $key);
2521  //var_dump($shortkey); var_dump($extrafields->attributes[$this->element]['unique'][$shortkey]);
2522  if (!empty($extrafields->attributes[$this->element]['unique'][$shortkey])) {
2523  //var_dump($key); var_dump($clonedObj->array_options[$key]); exit;
2524  unset($clonedObj->array_options[$key]);
2525  }
2526  }
2527  }
2528 
2529  if (empty($conf->global->CONTRACT_ADDON) || !is_readable(DOL_DOCUMENT_ROOT."/core/modules/contract/".$conf->global->CONTRACT_ADDON.".php")) {
2530  $this->error = 'ErrorSetupNotComplete';
2531  dol_syslog($this->error);
2532  return -1;
2533  }
2534 
2535  // Set ref
2536  require_once DOL_DOCUMENT_ROOT."/core/modules/contract/".$conf->global->CONTRACT_ADDON.'.php';
2537  $obj = $conf->global->CONTRACT_ADDON;
2538  $modContract = new $obj();
2539  $clonedObj->ref = $modContract->getNextValue($objsoc, $clonedObj);
2540 
2541  // get extrafields so they will be clone
2542  foreach ($this->lines as $line) {
2543  $line->fetch_optionals($line->id);
2544  }
2545 
2546  // Create clone
2547  $clonedObj->context['createfromclone'] = 'createfromclone';
2548  $result = $clonedObj->create($user);
2549  if ($result < 0) {
2550  $error++;
2551  $this->error = $clonedObj->error;
2552  $this->errors[] = $clonedObj->error;
2553  } else {
2554  // copy external contacts if same company
2555  if ($this->socid == $clonedObj->socid) {
2556  if ($clonedObj->copy_linked_contact($this, 'external') < 0) {
2557  $error++;
2558  }
2559  }
2560  }
2561 
2562  if (!$error) {
2563  foreach ($this->lines as $line) {
2564  $result = $clonedObj->addline($line->description, $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);
2565  if ($result < 0) {
2566  $error++;
2567  $this->error = $clonedObj->error;
2568  $this->errors[] = $clonedObj->error;
2569  }
2570  }
2571  }
2572 
2573  if (!$error) {
2574  // Hook of thirdparty module
2575  if (is_object($hookmanager)) {
2576  $parameters = array(
2577  'objFrom' => $this,
2578  'clonedObj' => $clonedObj
2579  );
2580  $action = '';
2581  $reshook = $hookmanager->executeHooks('createFrom', $parameters, $clonedObj, $action); // Note that $action and $object may have been modified by some hooks
2582  if ($reshook < 0) {
2583  $error++;
2584  }
2585  }
2586  }
2587 
2588  unset($clonedObj->context['createfromclone']);
2589 
2590  // End
2591  if (!$error) {
2592  $this->db->commit();
2593  return $clonedObj->id;
2594  } else {
2595  $this->db->rollback();
2596  return -1;
2597  }
2598  }
2599 }
2600 
2601 
2606 {
2610  public $element = 'contratdet';
2611 
2615  public $table_element = 'contratdet';
2616 
2620  public $element_for_permission = 'contrat';
2621 
2625  public $id;
2626 
2630  public $ref;
2631 
2632  public $tms;
2633 
2637  public $fk_contrat;
2638 
2642  public $fk_product;
2643 
2644  public $statut; // 0 inactive, 4 active, 5 closed
2645  public $type; // 0 for product, 1 for service
2646 
2651  public $label;
2652 
2657  public $libelle;
2658 
2662  public $description;
2663 
2664  public $product_type; // 0 for product, 1 for service
2665  public $product_ref;
2666  public $product_label;
2667 
2668  public $date_commande;
2669 
2670  public $date_start; // date start planned
2671  public $date_start_real; // date start real
2672  public $date_end; // date end planned
2673  public $date_end_real; // date end real
2674  // For backward compatibility
2678  public $date_ouverture_prevue; // date start planned
2682  public $date_ouverture; // date start real
2686  public $date_fin_validite; // date end planned
2690  public $date_cloture; // date end real
2691 
2692  public $tva_tx;
2693  public $vat_src_code;
2694  public $localtax1_tx;
2695  public $localtax2_tx;
2696  public $localtax1_type; // Local tax 1 type
2697  public $localtax2_type; // Local tax 2 type
2698  public $qty;
2699  public $remise_percent;
2700  public $remise;
2701 
2705  public $fk_remise_except;
2706 
2707  public $subprice; // Unit price HT
2708 
2714  public $price;
2715 
2716  public $price_ht;
2717 
2718  public $total_ht;
2719  public $total_tva;
2720  public $total_localtax1;
2721  public $total_localtax2;
2722  public $total_ttc;
2723 
2727  public $fk_fournprice;
2728 
2729  public $pa_ht;
2730 
2731  public $info_bits;
2732 
2736  public $fk_user_author;
2737 
2741  public $fk_user_ouverture;
2742 
2746  public $fk_user_cloture;
2747 
2748  public $commentaire;
2749 
2750 
2751  const STATUS_INITIAL = 0;
2752  const STATUS_OPEN = 4;
2753  const STATUS_CLOSED = 5;
2754 
2755 
2756  // BEGIN MODULEBUILDER PROPERTIES
2760  public $fields = array(
2761  'rowid' =>array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>10),
2762  'entity' =>array('type'=>'integer', 'label'=>'Entity', 'default'=>1, 'enabled'=>1, 'visible'=>-2, 'notnull'=>1, 'position'=>30, 'index'=>1),
2763  'tms' =>array('type'=>'timestamp', 'label'=>'DateModification', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>35),
2764  'qty' =>array('type'=>'integer', 'label'=>'Quantity', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>35, 'isameasure'=>1),
2765  'total_ht' =>array('type'=>'integer', 'label'=>'AmountHT', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>36, 'isameasure'=>1),
2766  'total_tva' =>array('type'=>'integer', 'label'=>'AmountVAT', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>37, 'isameasure'=>1),
2767  'total_ttc' =>array('type'=>'integer', 'label'=>'AmountTTC', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>38, 'isameasure'=>1),
2768  //'datec' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-1, 'position'=>40),
2769  //'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>70),
2770  'fk_contrat' =>array('type'=>'integer:Contrat:contrat/class/contrat.class.php', 'label'=>'Contract', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>70),
2771  'fk_product' =>array('type'=>'integer:Product:product/class/product.class.php:1', 'label'=>'Product', 'enabled'=>1, 'visible'=>-1, 'position'=>75),
2772  //'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'Fk user author', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>90),
2773  'note_private' =>array('type'=>'text', 'label'=>'NotePublic', 'enabled'=>1, 'visible'=>0, 'position'=>105),
2774  'note_public' =>array('type'=>'text', 'label'=>'NotePrivate', 'enabled'=>1, 'visible'=>0, 'position'=>110),
2775  //'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'Model pdf', 'enabled'=>1, 'visible'=>0, 'position'=>115),
2776  //'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>120),
2777  //'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>125),
2778  'fk_user_ouverture' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserStartingService', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>135),
2779  'fk_user_cloture' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserClosingService', 'enabled'=>1, 'visible'=>-2, 'notnull'=>-1, 'position'=>135),
2780  'statut' =>array('type'=>'smallint(6)', 'label'=>'Statut', 'enabled'=>1, 'visible'=>-1, 'position'=>500, 'arrayofkeyval'=>array(0=>'Draft', 4=>'Open', 5=>'Closed'))
2781  );
2782  // END MODULEBUILDER PROPERTIES
2783 
2784 
2790  public function __construct($db)
2791  {
2792  $this->db = $db;
2793  }
2794 
2795 
2802  public function getLibStatut($mode)
2803  {
2804  return $this->LibStatut($this->statut, $mode, ((!empty($this->date_fin_validite)) ? ($this->date_fin_validite < dol_now() ? 1 : 0) : -1));
2805  }
2806 
2807  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
2817  public static function LibStatut($status, $mode, $expired = -1, $moreatt = '')
2818  {
2819  // phpcs:enable
2820  global $langs;
2821  $langs->load("contracts");
2822 
2823  if ($status == self::STATUS_INITIAL) {
2824  $labelStatus = $langs->transnoentities("ServiceStatusInitial");
2825  $labelStatusShort = $langs->transnoentities("ServiceStatusInitial");
2826  } elseif ($status == self::STATUS_OPEN && $expired == -1) {
2827  $labelStatus = $langs->transnoentities("ServiceStatusRunning");
2828  $labelStatusShort = $langs->transnoentities("ServiceStatusRunning");
2829  } elseif ($status == self::STATUS_OPEN && $expired == 0) {
2830  $labelStatus = $langs->transnoentities("ServiceStatusNotLate");
2831  $labelStatusShort = $langs->transnoentities("ServiceStatusNotLateShort");
2832  } elseif ($status == self::STATUS_OPEN && $expired == 1) {
2833  $labelStatus = $langs->transnoentities("ServiceStatusLate");
2834  $labelStatusShort = $langs->transnoentities("ServiceStatusLateShort");
2835  } elseif ($status == self::STATUS_CLOSED) {
2836  $labelStatus = $langs->transnoentities("ServiceStatusClosed");
2837  $labelStatusShort = $langs->transnoentities("ServiceStatusClosed");
2838  }
2839 
2840  $statusType = 'status'.$status;
2841  if ($status == self::STATUS_OPEN && $expired == 1) {
2842  $statusType = 'status1';
2843  }
2844  if ($status == self::STATUS_CLOSED) {
2845  $statusType = 'status6';
2846  }
2847 
2848  $params = array(); $reg = array();
2849  if (preg_match('/class="(.*)"/', $moreatt, $reg)) {
2850  $params = array('badgeParams'=>array('css' => $reg[1]));
2851  }
2852  return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode, '', $params);
2853  }
2854 
2862  public function getNomUrl($withpicto = 0, $maxlength = 0)
2863  {
2864  global $langs;
2865 
2866  $result = '';
2867  $label = $langs->trans("ShowContractOfService").': '.$this->label;
2868  if (empty($label)) {
2869  $label = $this->description;
2870  }
2871 
2872  $link = '<a href="'.DOL_URL_ROOT.'/contrat/card.php?id='.$this->fk_contrat.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
2873  $linkend = '</a>';
2874 
2875  $picto = 'service';
2876  if ($this->type == 0) {
2877  $picto = 'product';
2878  }
2879 
2880  if ($withpicto) {
2881  $result .= ($link.img_object($label, $picto, 'class="classfortooltip"').$linkend);
2882  }
2883  if ($withpicto && $withpicto != 2) {
2884  $result .= ' ';
2885  }
2886  if ($withpicto != 2) {
2887  $result .= $link.($this->product_ref ? $this->product_ref.' ' : '').($this->label ? $this->label : $this->description).$linkend;
2888  }
2889  return $result;
2890  }
2891 
2899  public function fetch($id, $ref = '')
2900  {
2901  // Check parameters
2902  if (empty($id) && empty($ref)) {
2903  return -1;
2904  }
2905 
2906  $sql = "SELECT";
2907  $sql .= " t.rowid,";
2908  $sql .= " t.tms,";
2909  $sql .= " t.fk_contrat,";
2910  $sql .= " t.fk_product,";
2911  $sql .= " t.statut,";
2912  $sql .= " t.label,"; // This field is not used. Only label of product
2913  $sql .= " p.ref as product_ref,";
2914  $sql .= " p.label as product_label,";
2915  $sql .= " p.description as product_desc,";
2916  $sql .= " p.fk_product_type as product_type,";
2917  $sql .= " t.description,";
2918  $sql .= " t.date_commande,";
2919  $sql .= " t.date_ouverture_prevue as date_ouverture_prevue,";
2920  $sql .= " t.date_ouverture as date_ouverture,";
2921  $sql .= " t.date_fin_validite as date_fin_validite,";
2922  $sql .= " t.date_cloture as date_cloture,";
2923  $sql .= " t.tva_tx,";
2924  $sql .= " t.vat_src_code,";
2925  $sql .= " t.localtax1_tx,";
2926  $sql .= " t.localtax2_tx,";
2927  $sql .= " t.localtax1_type,";
2928  $sql .= " t.localtax2_type,";
2929  $sql .= " t.qty,";
2930  $sql .= " t.remise_percent,";
2931  $sql .= " t.remise,";
2932  $sql .= " t.fk_remise_except,";
2933  $sql .= " t.subprice,";
2934  $sql .= " t.price_ht,";
2935  $sql .= " t.total_ht,";
2936  $sql .= " t.total_tva,";
2937  $sql .= " t.total_localtax1,";
2938  $sql .= " t.total_localtax2,";
2939  $sql .= " t.total_ttc,";
2940  $sql .= " t.fk_product_fournisseur_price as fk_fournprice,";
2941  $sql .= " t.buy_price_ht as pa_ht,";
2942  $sql .= " t.info_bits,";
2943  $sql .= " t.fk_user_author,";
2944  $sql .= " t.fk_user_ouverture,";
2945  $sql .= " t.fk_user_cloture,";
2946  $sql .= " t.commentaire,";
2947  $sql .= " t.fk_unit";
2948  $sql .= " FROM ".MAIN_DB_PREFIX."contratdet as t LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = t.fk_product";
2949  if ($id) {
2950  $sql .= " WHERE t.rowid = ".((int) $id);
2951  }
2952  if ($ref) {
2953  $sql .= " WHERE t.rowid = '".$this->db->escape($ref)."'";
2954  }
2955 
2956  dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
2957  $resql = $this->db->query($sql);
2958  if ($resql) {
2959  if ($this->db->num_rows($resql)) {
2960  $obj = $this->db->fetch_object($resql);
2961 
2962  $this->id = $obj->rowid;
2963  $this->ref = $obj->rowid;
2964 
2965  $this->tms = $this->db->jdate($obj->tms);
2966  $this->fk_contrat = $obj->fk_contrat;
2967  $this->fk_product = $obj->fk_product;
2968  $this->statut = $obj->statut;
2969  $this->product_ref = $obj->product_ref;
2970  $this->product_label = $obj->product_label;
2971  $this->product_description = $obj->product_description;
2972  $this->product_type = $obj->product_type;
2973  $this->label = $obj->label; // deprecated. We do not use this field. Only ref and label of product, and description of contract line
2974  $this->description = $obj->description;
2975  $this->date_commande = $this->db->jdate($obj->date_commande);
2976 
2977  $this->date_start = $this->db->jdate($obj->date_ouverture_prevue);
2978  $this->date_start_real = $this->db->jdate($obj->date_ouverture);
2979  $this->date_end = $this->db->jdate($obj->date_fin_validite);
2980  $this->date_end_real = $this->db->jdate($obj->date_cloture);
2981  // For backward compatibility
2982  $this->date_ouverture_prevue = $this->db->jdate($obj->date_ouverture_prevue);
2983  $this->date_ouverture = $this->db->jdate($obj->date_ouverture);
2984  $this->date_fin_validite = $this->db->jdate($obj->date_fin_validite);
2985  $this->date_cloture = $this->db->jdate($obj->date_cloture);
2986 
2987  $this->tva_tx = $obj->tva_tx;
2988  $this->vat_src_code = $obj->vat_src_code;
2989  $this->localtax1_tx = $obj->localtax1_tx;
2990  $this->localtax2_tx = $obj->localtax2_tx;
2991  $this->localtax1_type = $obj->localtax1_type;
2992  $this->localtax2_type = $obj->localtax2_type;
2993  $this->qty = $obj->qty;
2994  $this->remise_percent = $obj->remise_percent;
2995  $this->fk_remise_except = $obj->fk_remise_except;
2996  $this->subprice = $obj->subprice;
2997  $this->price_ht = $obj->price_ht;
2998  $this->total_ht = $obj->total_ht;
2999  $this->total_tva = $obj->total_tva;
3000  $this->total_localtax1 = $obj->total_localtax1;
3001  $this->total_localtax2 = $obj->total_localtax2;
3002  $this->total_ttc = $obj->total_ttc;
3003  $this->info_bits = $obj->info_bits;
3004  $this->fk_user_author = $obj->fk_user_author;
3005  $this->fk_user_ouverture = $obj->fk_user_ouverture;
3006  $this->fk_user_cloture = $obj->fk_user_cloture;
3007  $this->commentaire = $obj->commentaire;
3008  $this->fk_fournprice = $obj->fk_fournprice;
3009 
3010  $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->fk_fournprice, $obj->pa_ht);
3011  $this->pa_ht = $marginInfos[0];
3012  $this->fk_unit = $obj->fk_unit;
3013 
3014  $this->fetch_optionals();
3015  }
3016 
3017  $this->db->free($resql);
3018 
3019  return 1;
3020  } else {
3021  $this->error = "Error ".$this->db->lasterror();
3022  return -1;
3023  }
3024  }
3025 
3026 
3034  public function update($user, $notrigger = 0)
3035  {
3036  global $conf, $langs, $mysoc;
3037 
3038  $error = 0;
3039 
3040  // Clean parameters
3041  $this->fk_contrat = (int) $this->fk_contrat;
3042  $this->fk_product = (int) $this->fk_product;
3043  $this->statut = (int) $this->statut;
3044  $this->label = trim($this->label);
3045  $this->description = trim($this->description);
3046  $this->vat_src_code = trim($this->vat_src_code);
3047  $this->tva_tx = trim($this->tva_tx);
3048  $this->localtax1_tx = trim($this->localtax1_tx);
3049  $this->localtax2_tx = trim($this->localtax2_tx);
3050  $this->qty = trim($this->qty);
3051  $this->remise_percent = trim($this->remise_percent);
3052  $this->fk_remise_except = (int) $this->fk_remise_except;
3053  $this->subprice = price2num($this->subprice);
3054  $this->price_ht = price2num($this->price_ht);
3055  $this->total_ht = trim($this->total_ht);
3056  $this->total_tva = trim($this->total_tva);
3057  $this->total_localtax1 = trim($this->total_localtax1);
3058  $this->total_localtax2 = trim($this->total_localtax2);
3059  $this->total_ttc = trim($this->total_ttc);
3060  $this->info_bits = trim($this->info_bits);
3061  $this->fk_user_author = (int) $this->fk_user_author;
3062  $this->fk_user_ouverture = (int) $this->fk_user_ouverture;
3063  $this->fk_user_cloture = (int) $this->fk_user_cloture;
3064  $this->commentaire = trim($this->commentaire);
3065  //if (empty($this->subprice)) $this->subprice = 0;
3066  if (empty($this->price_ht)) {
3067  $this->price_ht = 0;
3068  }
3069  if (empty($this->total_ht)) {
3070  $this->total_ht = 0;
3071  }
3072  if (empty($this->total_tva)) {
3073  $this->total_tva = 0;
3074  }
3075  if (empty($this->total_ttc)) {
3076  $this->total_ttc = 0;
3077  }
3078  if (empty($this->localtax1_tx)) {
3079  $this->localtax1_tx = 0;
3080  }
3081  if (empty($this->localtax2_tx)) {
3082  $this->localtax2_tx = 0;
3083  }
3084  if (empty($this->remise_percent)) {
3085  $this->remise_percent = 0;
3086  }
3087  // For backward compatibility
3088  if (empty($this->date_start)) {
3089  $this->date_start = $this->date_ouverture_prevue;
3090  }
3091  if (empty($this->date_start_real)) {
3092  $this->date_start = $this->date_ouverture;
3093  }
3094  if (empty($this->date_end)) {
3095  $this->date_start = $this->date_fin_validite;
3096  }
3097  if (empty($this->date_end_real)) {
3098  $this->date_start = $this->date_cloture;
3099  }
3100 
3101 
3102  // Check parameters
3103  // Put here code to add control on parameters values
3104 
3105  // Calcul du total TTC et de la TVA pour la ligne a partir de
3106  // qty, pu, remise_percent et txtva
3107  // TRES IMPORTANT: C'est au moment de l'insertion ligne qu'on doit stocker
3108  // la part ht, tva et ttc, et ce au niveau de la ligne qui a son propre taux tva.
3109  $localtaxes_type = getLocalTaxesFromRate($this->txtva, 0, $this->societe, $mysoc);
3110 
3111  $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);
3112  $this->total_ht = $tabprice[0];
3113  $this->total_tva = $tabprice[1];
3114  $this->total_ttc = $tabprice[2];
3115  $this->total_localtax1 = $tabprice[9];
3116  $this->total_localtax2 = $tabprice[10];
3117 
3118  if (empty($this->pa_ht)) {
3119  $this->pa_ht = 0;
3120  }
3121 
3122  // if buy price not defined, define buyprice as configured in margin admin
3123  if ($this->pa_ht == 0) {
3124  if (($result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product)) < 0) {
3125  return $result;
3126  } else {
3127  $this->pa_ht = $result;
3128  }
3129  }
3130 
3131 
3132  $this->db->begin();
3133 
3134  $this->oldcopy = new ContratLigne($this->db);
3135  $this->oldcopy->fetch($this->id);
3136 
3137  // Update request
3138  $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET";
3139  $sql .= " fk_contrat=".((int) $this->fk_contrat).",";
3140  $sql .= " fk_product=".($this->fk_product ? "'".$this->db->escape($this->fk_product)."'" : 'null').",";
3141  $sql .= " statut=".((int) $this->statut).",";
3142  $sql .= " label='".$this->db->escape($this->label)."',";
3143  $sql .= " description='".$this->db->escape($this->description)."',";
3144  $sql .= " date_commande=".($this->date_commande != '' ? "'".$this->db->idate($this->date_commande)."'" : "null").",";
3145  $sql .= " date_ouverture_prevue=".($this->date_ouverture_prevue != '' ? "'".$this->db->idate($this->date_ouverture_prevue)."'" : "null").",";
3146  $sql .= " date_ouverture=".($this->date_ouverture != '' ? "'".$this->db->idate($this->date_ouverture)."'" : "null").",";
3147  $sql .= " date_fin_validite=".($this->date_fin_validite != '' ? "'".$this->db->idate($this->date_fin_validite)."'" : "null").",";
3148  $sql .= " date_cloture=".($this->date_cloture != '' ? "'".$this->db->idate($this->date_cloture)."'" : "null").",";
3149  $sql .= " vat_src_code='".$this->db->escape($this->vat_src_code)."',";
3150  $sql .= " tva_tx=".price2num($this->tva_tx).",";
3151  $sql .= " localtax1_tx=".price2num($this->localtax1_tx).",";
3152  $sql .= " localtax2_tx=".price2num($this->localtax2_tx).",";
3153  $sql .= " qty=".price2num($this->qty).",";
3154  $sql .= " remise_percent=".price2num($this->remise_percent).",";
3155  $sql .= " remise=".($this->remise ?price2num($this->remise) : "null").",";
3156  $sql .= " fk_remise_except=".($this->fk_remise_except > 0 ? $this->fk_remise_except : "null").",";
3157  $sql .= " subprice=".($this->subprice != '' ? $this->subprice : "null").",";
3158  $sql .= " price_ht=".($this->price_ht != '' ? $this->price_ht : "null").",";
3159  $sql .= " total_ht=".$this->total_ht.",";
3160  $sql .= " total_tva=".$this->total_tva.",";
3161  $sql .= " total_localtax1=".$this->total_localtax1.",";
3162  $sql .= " total_localtax2=".$this->total_localtax2.",";
3163  $sql .= " total_ttc=".$this->total_ttc.",";
3164  $sql .= " fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? $this->fk_fournprice : "NULL").",";
3165  $sql .= " buy_price_ht='".price2num($this->pa_ht)."',";
3166  $sql .= " info_bits='".$this->db->escape($this->info_bits)."',";
3167  $sql .= " fk_user_author=".($this->fk_user_author >= 0 ? $this->fk_user_author : "NULL").",";
3168  $sql .= " fk_user_ouverture=".($this->fk_user_ouverture > 0 ? $this->fk_user_ouverture : "NULL").",";
3169  $sql .= " fk_user_cloture=".($this->fk_user_cloture > 0 ? $this->fk_user_cloture : "NULL").",";
3170  $sql .= " commentaire='".$this->db->escape($this->commentaire)."',";
3171  $sql .= " fk_unit=".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
3172  $sql .= " WHERE rowid=".((int) $this->id);
3173 
3174  dol_syslog(get_class($this)."::update", LOG_DEBUG);
3175  $resql = $this->db->query($sql);
3176  if (!$resql) {
3177  $this->error = "Error ".$this->db->lasterror();
3178  $error++;
3179  }
3180 
3181  if (!$error) { // For avoid conflicts if trigger used
3182  $result = $this->insertExtraFields();
3183  if ($result < 0) {
3184  $error++;
3185  }
3186  }
3187 
3188  // If we change a planned date (start or end), sync dates for all services
3189  if (!$error && !empty($conf->global->CONTRACT_SYNC_PLANNED_DATE_OF_SERVICES)) {
3190  if ($this->date_ouverture_prevue != $this->oldcopy->date_ouverture_prevue) {
3191  $sql = 'UPDATE '.MAIN_DB_PREFIX.'contratdet SET';
3192  $sql .= " date_ouverture_prevue = ".($this->date_ouverture_prevue != '' ? "'".$this->db->idate($this->date_ouverture_prevue)."'" : "null");
3193  $sql .= " WHERE fk_contrat = ".((int) $this->fk_contrat);
3194 
3195  $resql = $this->db->query($sql);
3196  if (!$resql) {
3197  $error++;
3198  $this->error = "Error ".$this->db->lasterror();
3199  }
3200  }
3201  if ($this->date_fin_validite != $this->oldcopy->date_fin_validite) {
3202  $sql = 'UPDATE '.MAIN_DB_PREFIX.'contratdet SET';
3203  $sql .= " date_fin_validite = ".($this->date_fin_validite != '' ? "'".$this->db->idate($this->date_fin_validite)."'" : "null");
3204  $sql .= " WHERE fk_contrat = ".((int) $this->fk_contrat);
3205 
3206  $resql = $this->db->query($sql);
3207  if (!$resql) {
3208  $error++;
3209  $this->error = "Error ".$this->db->lasterror();
3210  }
3211  }
3212  }
3213 
3214  if (!$error && !$notrigger) {
3215  // Call trigger
3216  $result = $this->call_trigger('LINECONTRACT_MODIFY', $user);
3217  if ($result < 0) {
3218  $error++;
3219  $this->db->rollback();
3220  }
3221  // End call triggers
3222  }
3223 
3224  if (!$error) {
3225  $this->db->commit();
3226  return 1;
3227  } else {
3228  $this->db->rollback();
3229  $this->errors[] = $this->error;
3230  return -1;
3231  }
3232  }
3233 
3234 
3235  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3242  public function update_total()
3243  {
3244  // phpcs:enable
3245  $this->db->begin();
3246 
3247  // Mise a jour ligne en base
3248  $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET";
3249  $sql .= " total_ht=".price2num($this->total_ht, 'MT')."";
3250  $sql .= ",total_tva=".price2num($this->total_tva, 'MT')."";
3251  $sql .= ",total_localtax1=".price2num($this->total_localtax1, 'MT')."";
3252  $sql .= ",total_localtax2=".price2num($this->total_localtax2, 'MT')."";
3253  $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT')."";
3254  $sql .= " WHERE rowid = ".((int) $this->id);
3255 
3256  dol_syslog(get_class($this)."::update_total", LOG_DEBUG);
3257 
3258  $resql = $this->db->query($sql);
3259  if ($resql) {
3260  $this->db->commit();
3261  return 1;
3262  } else {
3263  $this->error = $this->db->error();
3264  $this->db->rollback();
3265  return -2;
3266  }
3267  }
3268 
3269 
3276  public function insert($notrigger = 0)
3277  {
3278  global $conf, $user;
3279 
3280  $error = 0;
3281 
3282  // Insertion dans la base
3283  $sql = "INSERT INTO ".MAIN_DB_PREFIX."contratdet";
3284  $sql .= " (fk_contrat, label, description, fk_product, qty, vat_src_code, tva_tx,";
3285  $sql .= " localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice,";
3286  $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc,";
3287  $sql .= " info_bits,";
3288  $sql .= " price_ht, remise, fk_product_fournisseur_price, buy_price_ht";
3289  if ($this->date_ouverture_prevue > 0) {
3290  $sql .= ",date_ouverture_prevue";
3291  }
3292  if ($this->date_fin_validite > 0) {
3293  $sql .= ",date_fin_validite";
3294  }
3295  $sql .= ") VALUES ($this->fk_contrat, '', '".$this->db->escape($this->description)."',";
3296  $sql .= ($this->fk_product > 0 ? $this->fk_product : "null").",";
3297  $sql .= " '".$this->db->escape($this->qty)."',";
3298  $sql .= " '".$this->db->escape($this->vat_src_code)."',";
3299  $sql .= " '".$this->db->escape($this->tva_tx)."',";
3300  $sql .= " '".$this->db->escape($this->localtax1_tx)."',";
3301  $sql .= " '".$this->db->escape($this->localtax2_tx)."',";
3302  $sql .= " '".$this->db->escape($this->localtax1_type)."',";
3303  $sql .= " '".$this->db->escape($this->localtax2_type)."',";
3304  $sql .= " ".price2num($this->remise_percent).",".price2num($this->subprice).",";
3305  $sql .= " ".price2num($this->total_ht).",".price2num($this->total_tva).",".price2num($this->total_localtax1).",".price2num($this->total_localtax2).",".price2num($this->total_ttc).",";
3306  $sql .= " '".$this->db->escape($this->info_bits)."',";
3307  $sql .= " ".price2num($this->price_ht).",".price2num($this->remise).",";
3308  if ($this->fk_fournprice > 0) {
3309  $sql .= ' '.((int) $this->fk_fournprice).',';
3310  } else {
3311  $sql .= ' null,';
3312  }
3313  if ($this->pa_ht > 0) {
3314  $sql .= ' '.((float) price2num($this->pa_ht));
3315  } else {
3316  $sql .= ' null';
3317  }
3318  if ($this->date_ouverture > 0) {
3319  $sql .= ",'".$this->db->idate($this->date_ouverture)."'";
3320  }
3321  if ($this->date_cloture > 0) {
3322  $sql .= ",'".$this->db->idate($this->date_cloture)."'";
3323  }
3324  $sql .= ")";
3325 
3326  dol_syslog(get_class($this)."::insert", LOG_DEBUG);
3327 
3328  $resql = $this->db->query($sql);
3329  if ($resql) {
3330  $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'contratdet');
3331 
3332  // Insert of extrafields
3333  if (!$error) {
3334  $result = $this->insertExtraFields();
3335  if ($result < 0) {
3336  $this->db->rollback();
3337  return -1;
3338  }
3339  }
3340 
3341  if (!$notrigger) {
3342  // Call trigger
3343  $result = $this->call_trigger('LINECONTRACT_INSERT', $user);
3344  if ($result < 0) {
3345  $this->db->rollback();
3346  return -1;
3347  }
3348  // End call triggers
3349  }
3350 
3351  $this->db->commit();
3352  return 1;
3353  } else {
3354  $this->db->rollback();
3355  $this->error = $this->db->error()." sql=".$sql;
3356  return -1;
3357  }
3358  }
3359 
3360  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3370  public function active_line($user, $date, $date_end = '', $comment = '')
3371  {
3372  // phpcs:enable
3373  global $langs, $conf;
3374 
3375  $error = 0;
3376 
3377  $this->db->begin();
3378 
3379  $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET statut = ".ContratLigne::STATUS_OPEN.",";
3380  $sql .= " date_ouverture = ".(dol_strlen($date) != 0 ? "'".$this->db->idate($date)."'" : "null").",";
3381  if ($date_end >= 0) {
3382  $sql .= " date_fin_validite = ".(dol_strlen($date_end) != 0 ? "'".$this->db->idate($date_end)."'" : "null").",";
3383  }
3384  $sql .= " fk_user_ouverture = ".((int) $user->id).",";
3385  $sql .= " date_cloture = null,";
3386  $sql .= " commentaire = '".$this->db->escape($comment)."'";
3387  $sql .= " WHERE rowid = ".((int) $this->id)." AND (statut = ".ContratLigne::STATUS_INITIAL." OR statut = ".ContratLigne::STATUS_CLOSED.")";
3388 
3389  dol_syslog(get_class($this)."::active_line", LOG_DEBUG);
3390  $resql = $this->db->query($sql);
3391  if ($resql) {
3392  // Call trigger
3393  $result = $this->call_trigger('LINECONTRACT_ACTIVATE', $user);
3394  if ($result < 0) {
3395  $error++;
3396  }
3397  // End call triggers
3398 
3399  if (!$error) {
3400  $this->statut = ContratLigne::STATUS_OPEN;
3401  $this->date_ouverture = $date;
3402  $this->date_fin_validite = $date_end;
3403  $this->fk_user_ouverture = $user->id;
3404  $this->date_cloture = null;
3405  $this->commentaire = $comment;
3406 
3407  $this->db->commit();
3408  return 1;
3409  } else {
3410  $this->db->rollback();
3411  return -1;
3412  }
3413  } else {
3414  $this->error = $this->db->lasterror();
3415  $this->db->rollback();
3416  return -1;
3417  }
3418  }
3419 
3420  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
3430  public function close_line($user, $date_end, $comment = '', $notrigger = 0)
3431  {
3432  // phpcs:enable
3433  global $langs, $conf;
3434 
3435  // Update object
3436  $this->date_cloture = $date_end;
3437  $this->fk_user_cloture = $user->id;
3438  $this->commentaire = $comment;
3439 
3440  $error = 0;
3441 
3442  // statut actif : 4
3443 
3444  $this->db->begin();
3445 
3446  $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET statut = ".((int) ContratLigne::STATUS_CLOSED).",";
3447  $sql .= " date_cloture = '".$this->db->idate($date_end)."',";
3448  $sql .= " fk_user_cloture = ".((int) $user->id).",";
3449  $sql .= " commentaire = '".$this->db->escape($comment)."'";
3450  $sql .= " WHERE rowid = ".((int) $this->id)." AND statut = ".((int) ContratLigne::STATUS_OPEN);
3451 
3452  $resql = $this->db->query($sql);
3453  if ($resql) {
3454  if (!$notrigger) {
3455  // Call trigger
3456  $result = $this->call_trigger('LINECONTRACT_CLOSE', $user);
3457  if ($result < 0) {
3458  $error++;
3459  $this->db->rollback();
3460  return -1;
3461  }
3462  // End call triggers
3463  }
3464 
3465  $this->db->commit();
3466  return 1;
3467  } else {
3468  $this->error = $this->db->lasterror();
3469  $this->db->rollback();
3470  return -1;
3471  }
3472  }
3473 }
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.
fetch_lines($only_services=0, $loadalsotranslation=0)
Load lines array into this-&gt;lines.
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.
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.
$conf db
API class for accounts.
Definition: inc.php:41
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.
deleteObjectLinked($sourceid=null, $sourcetype= '', $targetid=null, $targettype= '', $rowid= '', $f_user=null, $notrigger=0)
Delete all links between an object $this.
fetch($id, $ref= '', $ref_customer= '', $ref_supplier= '')
Load a contract from database.
static commonReplaceProduct(DoliDB $db, $origin_id, $dest_id, array $tables, $ignoreerrors=0)
Function used to replace a product id with another one.
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.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags= '', $escapeonlyhtmltags=0)
Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields...
update_statut($user)
Update statut of contract according to services.
dol_buildpath($path, $type=0, $returnemptyifnotfound=0)
Return path of url or filesystem.
getNomUrl($withpicto=0, $maxlength=0)
Return clicable name (with picto eventually)
getListOfContracts($option= 'all', $status=[], $product_categories=[], $line_status=[])
Return list of other contracts for the same company than current contract.
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.
__construct($db)
Constructor.
load_state_board()
Charge indicateurs this-&gt;nb de tableau de bord.
Class to manage third parties objects (customers, suppliers, prospects...)
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;...
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories) ...
Definition: files.lib.php:1382
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.
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.
static replaceProduct(DoliDB $db, $origin_id, $dest_id)
Function used to replace a product id with another one.
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.
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.
if(isModEnabled('facture')&&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur')&&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)&&$user->rights->fournisseur->facture->lire)||(isModEnabled('supplier_invoice')&&$user->rights->supplier_invoice->lire)) if(isModEnabled('don')&&!empty($user->rights->don->lire)) if(isModEnabled('tax')&&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture')&&isModEnabled('commande')&&$user->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:742
getLibStatut($mode)
Return label of a contract status.
getLibStatut($mode)
Return label of this contract line status.
print *****$script_file(".$version.") pid cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
call_trigger($triggerName, $user)
Call trigger based on this instance.
add_object_linked($origin=null, $origin_id=null, $f_user=null, $notrigger=0)
Add objects linked in llx_element_element.
$object ref
Definition: info.php:77
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.
$table_ref_field
{}
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition: repair.php:119
__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.
getNomUrl($withpicto=0, $maxlength=0, $notooltip=0, $save_lastsearch_value=-1)
Return clicable name (with picto eventually)