dolibarr 25.0.0-alpha
contratligne.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-2025 Frédéric France <frederic.france@free.fr>
13 * Copyright (C) 2015-2018 Ferran Marcet <fmarcet@2byte.es>
14 * Copyright (C) 2024 William Mead <william.mead@manchenumerique.fr>
15 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
16 * Copyright (C) 2026 Lionel Vessiller <lvessiller@open-dsi.fr>
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 3 of the License, or
21 * (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program. If not, see <https://www.gnu.org/licenses/>.
30 */
31
38require_once DOL_DOCUMENT_ROOT."/core/class/commonobjectline.class.php";
39require_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
40require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
41
46{
50 public $element = 'contratdet';
51
55 public $table_element = 'contratdet';
56
60 public $parent_element = 'contrat';
61
65 public $fk_parent_attribute = 'fk_contrat';
66
71 public $element_for_permission = 'contrat';
72
76 public $id;
77
81 public $ref;
82
86 public $fk_contrat;
87
91 public $fk_product;
92
96 public $statut;
97
101 public $type;
102
107 public $label;
108
113 public $libelle;
114
118 public $description;
119
123 public $product_type;
124
128 public $product_ref;
129
133 public $product_label;
134
138 public $date_commande;
139
143 public $date_start;
144
148 public $date_start_real;
149
153 public $date_end;
154
158 public $date_end_real;
159
163 public $tva_tx;
164
168 public $vat_src_code;
169
173 public $localtax1_tx;
174
178 public $localtax2_tx;
179
183 public $localtax1_type;
184
188 public $localtax2_type;
189
193 public $qty;
194
198 public $remise_percent;
199
203 public $fk_remise_except;
204
208 public $subprice;
209
213 public $total_ht;
214
218 public $total_tva;
219
223 public $total_localtax1;
224
228 public $total_localtax2;
229
233 public $total_ttc;
234
238 public $fk_fournprice;
239
243 public $pa_ht;
244
248 public $info_bits;
249
253 public $fk_user_author;
254
258 public $fk_user_ouverture;
259
263 public $fk_user_cloture;
264
268 public $commentaire;
269
270
274 public $rang = 0;
275
276
277 const STATUS_INITIAL = 0;
278 const STATUS_OPEN = 4;
279 const STATUS_CLOSED = 5;
280
281
282 // BEGIN MODULEBUILDER PROPERTIES
286 public $fields = array(
287 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
288 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 30, 'index' => 1),
289 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 35),
290 'qty' => array('type' => 'integer', 'label' => 'Quantity', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'position' => 35, 'isameasure' => 1),
291 'total_ht' => array('type' => 'integer', 'label' => 'AmountHT', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 36, 'isameasure' => 1),
292 'total_tva' => array('type' => 'integer', 'label' => 'AmountVAT', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 37, 'isameasure' => 1),
293 'total_ttc' => array('type' => 'integer', 'label' => 'AmountTTC', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 38, 'isameasure' => 1),
294 //'datec' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-1, 'position'=>40),
295 //'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>70),
296 'fk_contrat' => array('type' => 'integer:Contrat:contrat/class/contrat.class.php', 'label' => 'Contract', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 70),
297 'fk_product' => array('type' => 'integer:Product:product/class/product.class.php:1', 'label' => 'Product', 'enabled' => 1, 'visible' => -1, 'position' => 75),
298 //'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>90),
299 'note_private' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 105),
300 'note_public' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 110),
301 //'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'Model pdf', 'enabled'=>1, 'visible'=>0, 'position'=>115),
302 //'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>120),
303 //'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>125),
304 'fk_user_ouverture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserStartingService', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 135),
305 'fk_user_cloture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserClosingService', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 135),
306 'statut' => array('type' => 'smallint(6)', 'label' => 'Statut', 'enabled' => 1, 'visible' => -1, 'position' => 500, 'arrayofkeyval' => array(0 => 'Draft', 4 => 'Open', 5 => 'Closed')),
307 'rang' => array('type' => 'integer', 'label' => 'Rank', 'enabled' => 1, 'visible' => 0, 'position' => 500, 'default' => '0')
308 );
309 // END MODULEBUILDER PROPERTIES
310
311
317 public function __construct($db)
318 {
319 $this->db = $db;
320 }
321
322
329 public function getLibStatut($mode)
330 {
331 return $this->LibStatut($this->statut, $mode, ((!empty($this->date_end)) ? ($this->date_end < dol_now() ? 1 : 0) : -1));
332 }
333
334 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
345 public static function LibStatut($status, $mode, $expired = -1, $moreatt = '', $morelabel = '')
346 {
347 // phpcs:enable
348 global $langs;
349 $langs->load("contracts");
350
351 if ($status == self::STATUS_INITIAL) {
352 $labelStatus = $langs->transnoentities("ServiceStatusInitial");
353 $labelStatusShort = $langs->transnoentities("ServiceStatusInitial");
354 } elseif ($status == self::STATUS_OPEN && $expired == -1) {
355 $labelStatus = $langs->transnoentities("ServiceStatusRunning");
356 $labelStatusShort = $langs->transnoentities("ServiceStatusRunning");
357 } elseif ($status == self::STATUS_OPEN && $expired == 0) {
358 $labelStatus = $langs->transnoentities("ServiceStatusNotLate");
359 $labelStatusShort = $langs->transnoentities("ServiceStatusNotLateShort");
360 } elseif ($status == self::STATUS_OPEN && $expired == 1) {
361 $labelStatus = $langs->transnoentities("ServiceStatusLate");
362 $labelStatusShort = $langs->transnoentities("ServiceStatusLateShort");
363 } elseif ($status == self::STATUS_CLOSED) {
364 $labelStatus = $langs->transnoentities("ServiceStatusClosed");
365 $labelStatusShort = $langs->transnoentities("ServiceStatusClosed");
366 } else {
367 $labelStatus = '';
368 $labelStatusShort = '';
369 }
370
371 $statusType = 'status'.$status;
372 if ($status == self::STATUS_OPEN && $expired == 1) {
373 $statusType = 'status1';
374 }
375 if ($status == self::STATUS_CLOSED) {
376 $statusType = 'status6';
377 }
378
379 $params = array();
380 $reg = array();
381 if (preg_match('/class="(.*)"/', $moreatt, $reg)) {
382 $params = array('badgeParams' => array('css' => $reg[1]));
383 }
384 return dolGetStatus($labelStatus.($morelabel ? ' '.$morelabel : ''), $labelStatusShort.($morelabel ? ' '.$morelabel : ''), '', $statusType, $mode, '', $params);
385 }
386
393 public function getTooltipContentArray($params)
394 {
395 global $conf, $langs, $user;
396
397 $datas = [];
398 $datas['label'] = $langs->trans("ShowContractOfService").': '.$this->label;
399 if (empty($this->label)) {
400 $datas['label'] = $this->description;
401 }
402
403 return $datas;
404 }
405
413 public function getNomUrl($withpicto = 0, $maxlength = 0)
414 {
415 global $langs;
416
417 $result = '';
418 $label = $langs->trans("ShowContractOfService").': '.$this->label;
419 if (empty($this->label)) {
420 $label = $this->description;
421 }
422 $classfortooltip = 'classfortooltip';
423 $dataparams = '';
424 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
425 $params = [
426 'id' => $this->fk_contrat,
427 'objecttype' => $this->element,
428 ];
429 $classfortooltip = 'classforajaxtooltip';
430 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
431 $label = '';
432 }
433
434 $link = '<a href="'.DOL_URL_ROOT.'/contrat/card.php?id='.$this->fk_contrat.'"';
435 $link .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
436 $link .= $dataparams.' class="'.$classfortooltip.'">';
437 $linkend = '</a>';
438
439 $picto = 'service';
440 if ($this->type == 0) {
441 $picto = 'product';
442 }
443
444 if ($withpicto) {
445 $result .= ($link.img_object($label, $picto, $dataparams.' class="'.$classfortooltip.'"').$linkend);
446 }
447 if ($withpicto && $withpicto != 2) {
448 $result .= ' ';
449 }
450 if ($withpicto != 2) {
451 $result .= $link.($this->product_ref ? $this->product_ref.' ' : '').($this->label ? $this->label : $this->description).$linkend;
452 }
453 return $result;
454 }
455
463 public function fetch($id, $ref = '')
464 {
465 // Check parameters
466 if (empty($id) && empty($ref)) {
467 return -1;
468 }
469
470 $sql = "SELECT";
471 $sql .= " t.rowid,";
472 $sql .= " t.tms,";
473 $sql .= " t.fk_contrat,";
474 $sql .= " t.fk_product,";
475 $sql .= " t.statut,";
476 $sql .= " t.label,"; // This field is not used. Only label of product
477 $sql .= " p.ref as product_ref,";
478 $sql .= " p.label as product_label,";
479 $sql .= " p.description as product_description,";
480 $sql .= " p.fk_product_type as product_type,";
481 $sql .= " t.description,";
482 $sql .= " t.date_commande,";
483 $sql .= " t.date_ouverture_prevue as date_start,";
484 $sql .= " t.date_ouverture as date_start_real,";
485 $sql .= " t.date_fin_validite as date_end,";
486 $sql .= " t.date_cloture as date_end_real,";
487 $sql .= " t.tva_tx,";
488 $sql .= " t.vat_src_code,";
489 $sql .= " t.localtax1_tx,";
490 $sql .= " t.localtax2_tx,";
491 $sql .= " t.localtax1_type,";
492 $sql .= " t.localtax2_type,";
493 $sql .= " t.qty,";
494 $sql .= " t.remise_percent,";
495 $sql .= " t.fk_remise_except,";
496 $sql .= " t.subprice,";
497 $sql .= " t.subprice_ttc,";
498 $sql .= " t.total_ht,";
499 $sql .= " t.total_tva,";
500 $sql .= " t.total_localtax1,";
501 $sql .= " t.total_localtax2,";
502 $sql .= " t.total_ttc,";
503 $sql .= " t.fk_product_fournisseur_price as fk_fournprice,";
504 $sql .= " t.buy_price_ht as pa_ht,";
505 $sql .= " t.info_bits,";
506 $sql .= " t.fk_user_author,";
507 $sql .= " t.fk_user_ouverture,";
508 $sql .= " t.fk_user_cloture,";
509 $sql .= " t.commentaire,";
510 $sql .= " t.fk_unit,";
511 $sql .= " t.extraparams,";
512 $sql .= " t.rang";
513 $sql .= " FROM ".MAIN_DB_PREFIX."contratdet as t LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = t.fk_product";
514 if ($id) {
515 $sql .= " WHERE t.rowid = ".((int) $id);
516 }
517 if ($ref) {
518 $sql .= " WHERE t.rowid = '".$this->db->escape($ref)."'";
519 }
520
521 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
522 $resql = $this->db->query($sql);
523 if ($resql) {
524 if ($this->db->num_rows($resql)) {
525 $obj = $this->db->fetch_object($resql);
526
527 $this->id = $obj->rowid;
528 $this->ref = $obj->rowid;
529
530 $this->tms = $this->db->jdate($obj->tms);
531 $this->fk_contrat = (int) $obj->fk_contrat;
532 $this->fk_product = (int) $obj->fk_product;
533 $this->statut = $obj->statut;
534 $this->product_ref = $obj->product_ref;
535 $this->product_label = $obj->product_label;
536 $this->product_type = $obj->product_type;
537 $this->label = $obj->label; // deprecated. We do not use this field. Only ref and label of product, and description of contract line
538 $this->description = $obj->description;
539 $this->date_commande = $this->db->jdate($obj->date_commande);
540
541 $this->date_start = $this->db->jdate($obj->date_start);
542 $this->date_start_real = $this->db->jdate($obj->date_start_real);
543 $this->date_end = $this->db->jdate($obj->date_end);
544 $this->date_end_real = $this->db->jdate($obj->date_end_real);
545
546 $this->tva_tx = $obj->tva_tx;
547 $this->vat_src_code = $obj->vat_src_code;
548 $this->localtax1_tx = $obj->localtax1_tx;
549 $this->localtax2_tx = $obj->localtax2_tx;
550 $this->localtax1_type = $obj->localtax1_type;
551 $this->localtax2_type = $obj->localtax2_type;
552 $this->qty = $obj->qty;
553 $this->remise_percent = $obj->remise_percent;
554 $this->fk_remise_except = $obj->fk_remise_except;
555 $this->subprice = $obj->subprice;
556 $this->subprice_ttc = $obj->subprice_ttc;
557 $this->total_ht = $obj->total_ht;
558 $this->total_tva = $obj->total_tva;
559 $this->total_localtax1 = $obj->total_localtax1;
560 $this->total_localtax2 = $obj->total_localtax2;
561 $this->total_ttc = $obj->total_ttc;
562 $this->info_bits = $obj->info_bits;
563 $this->fk_user_author = $obj->fk_user_author;
564 $this->fk_user_ouverture = $obj->fk_user_ouverture;
565 $this->fk_user_cloture = $obj->fk_user_cloture;
566 $this->commentaire = $obj->commentaire;
567 $this->fk_fournprice = $obj->fk_fournprice;
568
569 $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->fk_fournprice, $obj->pa_ht);
570 $this->pa_ht = $marginInfos[0];
571 $this->fk_unit = $obj->fk_unit;
572
573 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
574
575 $this->rang = $obj->rang;
576
577 $this->fetch_optionals();
578 }
579
580 $this->db->free($resql);
581
582 return 1;
583 } else {
584 $this->error = "Error ".$this->db->lasterror();
585 return -1;
586 }
587 }
588
589
597 public function update($user, $notrigger = 0)
598 {
599 global $mysoc;
600
601 $error = 0;
602
603 // Clean parameters
604 $this->fk_contrat = (int) $this->fk_contrat;
605 $this->fk_product = (int) $this->fk_product;
606 $this->statut = (int) $this->statut;
607 $this->label = trim($this->label);
608 $this->description = trim($this->description);
609 $this->vat_src_code = trim($this->vat_src_code);
610 $this->tva_tx = trim((string) $this->tva_tx);
611 $this->localtax1_tx = trim($this->localtax1_tx);
612 $this->localtax2_tx = trim($this->localtax2_tx);
613 $this->qty = (float) $this->qty;
614 $this->remise_percent = trim((string) $this->remise_percent);
615 $this->fk_remise_except = (int) $this->fk_remise_except;
616 $this->subprice = (float) price2num($this->subprice);
617 $this->info_bits = (int) $this->info_bits;
618 $this->fk_user_author = (int) $this->fk_user_author;
619 $this->fk_user_ouverture = (int) $this->fk_user_ouverture;
620 $this->fk_user_cloture = (int) $this->fk_user_cloture;
621 $this->commentaire = trim($this->commentaire);
622 $this->rang = (int) $this->rang;
623 if (empty($this->subprice)) {
624 $this->subprice = 0;
625 }
626 if (empty($this->total_ht)) {
627 $this->total_ht = 0;
628 }
629 if (empty($this->total_tva)) {
630 $this->total_tva = 0;
631 }
632 if (empty($this->total_ttc)) {
633 $this->total_ttc = 0;
634 }
635 if (empty($this->localtax1_tx)) {
636 $this->localtax1_tx = 0;
637 }
638 if (empty($this->localtax2_tx)) {
639 $this->localtax2_tx = 0;
640 }
641 if (empty($this->remise_percent)) {
642 $this->remise_percent = 0;
643 }
644
645 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
646 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
647 // and this is done at the line level, which has its own VAT rate
648 $localtaxes_type = getLocalTaxesFromRate($this->tva_tx, 0, $this->thirdparty, $mysoc);
649
650 // Compute the total from the value the user actually entered, to avoid a rounding drift.
651 $line_price_base_type = $this->getPriceBaseType();
652 $pu_for_calc = $this->wasEnteredIncludingTax() ? (float) $this->subprice_ttc : (float) $this->subprice;
653
654 $tabprice = calcul_price_total($this->qty, $pu_for_calc, $this->remise_percent, (float) $this->tva_tx, $this->localtax1_tx, $this->localtax2_tx, 0, $line_price_base_type, 0, 1, $mysoc, $localtaxes_type);
655 $this->total_ht = (float) $tabprice[0];
656 $this->total_tva = (float) $tabprice[1];
657 $this->total_ttc = (float) $tabprice[2];
658 $this->total_localtax1 = (float) $tabprice[9];
659 $this->total_localtax2 = (float) $tabprice[10];
660
661 if (empty($this->pa_ht)) {
662 $this->pa_ht = 0;
663 }
664
665 // if buy price not defined, define buyprice as configured in margin admin
666 if ($this->pa_ht == 0) {
667 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
668 if ($result < 0) {
669 return -1;
670 } else {
671 $this->pa_ht = $result;
672 }
673 }
674
675 // $this->oldcopy should have been set by the caller of update (here properties were already modified)
676 if (empty($this->oldcopy)) {
677 dol_syslog("this->oldcopy should have been set by the caller of update (here properties were already modified)", LOG_WARNING);
678 $this->oldcopy = dol_clone($this, 2);
679 }
680
681 $this->db->begin();
682
683 // Update request
684 $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET";
685 $sql .= " fk_contrat = ".((int) $this->fk_contrat).",";
686 $sql .= " fk_product = ".($this->fk_product ? ((int) $this->fk_product) : 'null').",";
687 $sql .= " statut = ".((int) $this->statut).",";
688 $sql .= " label = '".$this->db->escape($this->label)."',";
689 $sql .= " description = '".$this->db->escape($this->description)."',";
690 $sql .= " date_commande = ".($this->date_commande != '' ? "'".$this->db->idate($this->date_commande)."'" : "null").",";
691 $sql .= " date_ouverture_prevue = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : "null").",";
692 $sql .= " date_ouverture = ".($this->date_start_real != '' ? "'".$this->db->idate($this->date_start_real)."'" : "null").",";
693 $sql .= " date_fin_validite = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : "null").",";
694 $sql .= " date_cloture = ".($this->date_end_real != '' ? "'".$this->db->idate($this->date_end_real)."'" : "null").",";
695 $sql .= " vat_src_code = '".$this->db->escape($this->vat_src_code)."',";
696 $sql .= " tva_tx = ".price2num($this->tva_tx).",";
697 $sql .= " localtax1_tx = ".price2num($this->localtax1_tx).",";
698 $sql .= " localtax2_tx = ".price2num($this->localtax2_tx).",";
699 $sql .= " qty = ".price2num($this->qty).",";
700 $sql .= " remise_percent = ".price2num($this->remise_percent).",";
701 $sql .= " fk_remise_except = ".($this->fk_remise_except > 0 ? ((int) $this->fk_remise_except) : "null").",";
702 $sql .= " subprice = ".($this->subprice != '' ? ((float) $this->subprice) : "null").",";
703 $sql .= " subprice_ttc = ".($this->subprice_ttc != '' ? ((float) $this->subprice_ttc) : "0").",";
704 $sql .= " total_ht = ".((float) $this->total_ht).",";
705 $sql .= " total_tva = ".((float) $this->total_tva).",";
706 $sql .= " total_localtax1 = ".((float) $this->total_localtax1).",";
707 $sql .= " total_localtax2 = ".((float) $this->total_localtax2).",";
708 $sql .= " total_ttc = ".((float) $this->total_ttc).",";
709 $sql .= " fk_product_fournisseur_price = ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "NULL").",";
710 $sql .= " buy_price_ht = '".price2num($this->pa_ht)."',";
711 $sql .= " info_bits = '".$this->db->escape((string) $this->info_bits)."',";
712 $sql .= " fk_user_author = ".($this->fk_user_author >= 0 ? ((int) $this->fk_user_author) : "NULL").",";
713 $sql .= " fk_user_ouverture = ".($this->fk_user_ouverture > 0 ? ((int) $this->fk_user_ouverture) : "NULL").",";
714 $sql .= " fk_user_cloture = ".($this->fk_user_cloture > 0 ? ((int) $this->fk_user_cloture) : "NULL").",";
715 $sql .= " commentaire = '".$this->db->escape($this->commentaire)."',";
716 $sql .= " fk_unit = ".(!$this->fk_unit ? 'NULL' : ((int) $this->fk_unit)).",";
717 $sql .= " rang = ".(empty($this->rang) ? '0' : ((int) $this->rang));
718 $sql .= " WHERE rowid = ".((int) $this->id);
719
720 dol_syslog(get_class($this)."::update", LOG_DEBUG);
721 $resql = $this->db->query($sql);
722 if (!$resql) {
723 $this->error = "Error ".$this->db->lasterror();
724 $error++;
725 }
726
727 if (!$error) { // For avoid conflicts if trigger used
728 $result = $this->insertExtraFields();
729 if ($result < 0) {
730 $error++;
731 }
732 }
733
734 // If we change a planned date (start or end) of one contract line, sync dates for all other services too
735 if (!$error && getDolGlobalString('CONTRACT_SYNC_PLANNED_DATE_OF_SERVICES')) {
736 dol_syslog(get_class($this)."::update CONTRACT_SYNC_PLANNED_DATE_OF_SERVICES is on so we update date for all lines", LOG_DEBUG);
737
738 if ($this->date_start != $this->oldcopy->date_start) {
739 $sql = 'UPDATE '.MAIN_DB_PREFIX.'contratdet SET';
740 $sql .= " date_ouverture_prevue = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : "null");
741 $sql .= " WHERE fk_contrat = ".((int) $this->fk_contrat);
742
743 $resql = $this->db->query($sql);
744 if (!$resql) {
745 $error++;
746 $this->error = "Error ".$this->db->lasterror();
747 }
748 }
749 if ($this->date_end != $this->oldcopy->date_end) {
750 $sql = 'UPDATE '.MAIN_DB_PREFIX.'contratdet SET';
751 $sql .= " date_fin_validite = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : "null");
752 $sql .= " WHERE fk_contrat = ".((int) $this->fk_contrat);
753
754 $resql = $this->db->query($sql);
755 if (!$resql) {
756 $error++;
757 $this->error = "Error ".$this->db->lasterror();
758 }
759 }
760 }
761
762 if (!$error && !$notrigger) {
763 // Call trigger
764 $result = $this->call_trigger('LINECONTRACT_MODIFY', $user);
765 if ($result < 0) {
766 $error++;
767 $this->db->rollback();
768 }
769 // End call triggers
770 }
771
772 if (!$error) {
773 $this->db->commit();
774 return 1;
775 } else {
776 $this->db->rollback();
777 $this->errors[] = $this->error;
778 return -1;
779 }
780 }
781
782
783 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
790 public function update_total()
791 {
792 // phpcs:enable
793 $this->db->begin();
794
795 // Update line in database
796 $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET";
797 $sql .= " total_ht=".price2num($this->total_ht, 'MT');
798 $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
799 $sql .= ",total_localtax1=".price2num($this->total_localtax1, 'MT');
800 $sql .= ",total_localtax2=".price2num($this->total_localtax2, 'MT');
801 $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
802 $sql .= " WHERE rowid = ".((int) $this->id);
803
804 dol_syslog(get_class($this)."::update_total", LOG_DEBUG);
805
806 $resql = $this->db->query($sql);
807 if ($resql) {
808 $this->db->commit();
809 return 1;
810 } else {
811 $this->error = $this->db->error();
812 $this->db->rollback();
813 return -2;
814 }
815 }
816
817
824 public function insert($notrigger = 0)
825 {
826 global $user;
827
828 $error = 0;
829
830 // Insert into database
831 $sql = "INSERT INTO ".MAIN_DB_PREFIX."contratdet";
832 $sql .= " (fk_contrat, label, description, fk_product, qty, vat_src_code, tva_tx,";
833 $sql .= " localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice, subprice_ttc,";
834 $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc,";
835 $sql .= " info_bits,";
836 $sql .= " rang,";
837 $sql .= " fk_product_fournisseur_price, buy_price_ht";
838 if ($this->date_start > 0) {
839 $sql .= ",date_ouverture_prevue";
840 }
841 if ($this->date_end > 0) {
842 $sql .= ",date_fin_validite";
843 }
844 $sql .= ") VALUES (".((int) $this->fk_contrat).", '', '".$this->db->escape($this->description)."',";
845 $sql .= ($this->fk_product > 0 ? ((int) $this->fk_product) : "null").",";
846 $sql .= " '".$this->db->escape((string) $this->qty)."',";
847 $sql .= " '".$this->db->escape($this->vat_src_code)."',";
848 $sql .= " '".$this->db->escape($this->tva_tx)."',";
849 $sql .= " '".$this->db->escape($this->localtax1_tx)."',";
850 $sql .= " '".$this->db->escape($this->localtax2_tx)."',";
851 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
852 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
853 $sql .= " ".price2num($this->remise_percent).",".price2num($this->subprice).",".((float) price2num($this->subprice_ttc)).",";
854 $sql .= " ".price2num($this->total_ht).",".price2num($this->total_tva).",".price2num($this->total_localtax1).",".price2num($this->total_localtax2).",".price2num($this->total_ttc).",";
855 $sql .= " '".$this->db->escape((string) $this->info_bits)."',";
856 $sql .= " ".(empty($this->rang) ? '0' : (int) $this->rang).",";
857 if ($this->fk_fournprice > 0) {
858 $sql .= ' '.((int) $this->fk_fournprice).',';
859 } else {
860 $sql .= ' null,';
861 }
862 if ($this->pa_ht > 0) {
863 $sql .= ' '.((float) price2num($this->pa_ht));
864 } else {
865 $sql .= ' null';
866 }
867 if ($this->date_start > 0) {
868 $sql .= ",'".$this->db->idate($this->date_start)."'";
869 }
870 if ($this->date_end > 0) {
871 $sql .= ",'".$this->db->idate($this->date_end)."'";
872 }
873 $sql .= ")";
874
875 dol_syslog(get_class($this)."::insert", LOG_DEBUG);
876
877 $resql = $this->db->query($sql);
878 if ($resql) {
879 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'contratdet');
880
881 // Insert of extrafields
882 $result = $this->insertExtraFields();
883 if ($result < 0) {
884 $this->db->rollback();
885 return -1;
886 }
887
888 if (!$notrigger) {
889 // Call trigger
890 $result = $this->call_trigger('LINECONTRACT_INSERT', $user);
891 if ($result < 0) {
892 $this->db->rollback();
893 return -1;
894 }
895 // End call triggers
896 }
897
898 $this->db->commit();
899 return 1;
900 } else {
901 $this->db->rollback();
902 $this->error = $this->db->error()." sql=".$sql;
903 return -1;
904 }
905 }
906
907 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
917 public function active_line($user, $date, $date_end = '', $comment = '')
918 {
919 // phpcs:enable
920 $error = 0;
921
922 $this->db->begin();
923
924 $this->statut = ContratLigne::STATUS_OPEN;
925 $this->status = ContratLigne::STATUS_OPEN;
926 $this->date_start_real = $date;
927 $this->date_end = $date_end;
928 $this->fk_user_ouverture = $user->id;
929 $this->date_end_real = null;
930 $this->commentaire = $comment;
931
932 $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET statut = ".((int) $this->status).",";
933 $sql .= " date_ouverture = ".(dol_strlen((string) $this->date_start_real) != 0 ? "'".$this->db->idate($this->date_start_real)."'" : "null").",";
934 if ($date_end >= 0) {
935 $sql .= " date_fin_validite = ".(dol_strlen($this->date_end) != 0 ? "'".$this->db->idate($this->date_end)."'" : "null").",";
936 }
937 $sql .= " fk_user_ouverture = ".((int) $this->fk_user_ouverture).",";
938 $sql .= " date_cloture = null,";
939 $sql .= " commentaire = '".$this->db->escape($comment)."'";
940 $sql .= " WHERE rowid = ".((int) $this->id)." AND (statut = ".ContratLigne::STATUS_INITIAL." OR statut = ".ContratLigne::STATUS_CLOSED.")";
941
942 dol_syslog(get_class($this)."::active_line", LOG_DEBUG);
943 $resql = $this->db->query($sql);
944 if ($resql) {
945 if ($date_end >= 0) {
946 // Update column llx_contrat.denormalized_lower_panned_end_date with next expiration date of an open contract
947 $sqltoupdatecontract = "UPDATE ".MAIN_DB_PREFIX."contrat";
948 $sqltoupdatecontract .= " SET denormalized_lower_planned_end_date = (SELECT MIN(date_fin_validite) FROM ".MAIN_DB_PREFIX."contratdet as cd WHERE cd.fk_contrat = ".((int) $this->fk_contrat)." AND cd.statut = ".ContratLigne::STATUS_OPEN.")";
949 $sqltoupdatecontract .= " WHERE rowid = ".((int) $this->fk_contrat);
950 $resqltoupdatecontract = $this->db->query($sqltoupdatecontract);
951 if (!$resqltoupdatecontract) {
952 $this->error = $this->db->lasterror();
953 $this->db->rollback();
954 return -1;
955 }
956 }
957
958 // Call trigger
959 $result = $this->call_trigger('LINECONTRACT_ACTIVATE', $user);
960 if ($result < 0) {
961 $error++;
962 }
963 // End call triggers
964
965 if (!$error) {
966 $this->db->commit();
967 return 1;
968 } else {
969 $this->db->rollback();
970 return -1;
971 }
972 } else {
973 $this->error = $this->db->lasterror();
974 $this->db->rollback();
975 return -1;
976 }
977 }
978
979 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
989 public function close_line($user, $date_end_real, $comment = '', $notrigger = 0)
990 {
991 // phpcs:enable
992 $this->date_cloture = $date_end_real;
993 $this->date_end_real = $date_end_real;
994 $this->user_closing_id = $user->id;
995 $this->commentaire = $comment;
996
997 $error = 0;
998
999 // statut actif : 4
1000
1001 $this->db->begin();
1002
1003 $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET statut = ".((int) ContratLigne::STATUS_CLOSED).",";
1004 $sql .= " date_cloture = '".$this->db->idate($date_end_real)."',";
1005 $sql .= " fk_user_cloture = ".((int) $user->id).",";
1006 $sql .= " commentaire = '".$this->db->escape($comment)."'";
1007 $sql .= " WHERE rowid = ".((int) $this->id)." AND statut <> ".((int) ContratLigne::STATUS_CLOSED);
1008
1009 $resql = $this->db->query($sql);
1010 if ($resql) {
1011 // Update column llx_contrat.denormalized_lower_panned_end_date with next expiration date of an open contract
1012 $sqltoupdatecontract = "UPDATE ".MAIN_DB_PREFIX."contrat";
1013 $sqltoupdatecontract .= " SET denormalized_lower_planned_end_date = (SELECT MIN(date_fin_validite) FROM ".MAIN_DB_PREFIX."contratdet as cd WHERE cd.fk_contrat = ".((int) $this->fk_contrat)." AND cd.statut = ".ContratLigne::STATUS_OPEN.")";
1014 $sqltoupdatecontract .= " WHERE rowid = ".((int) $this->fk_contrat);
1015 $resqltoupdatecontract = $this->db->query($sqltoupdatecontract);
1016 if (!$resqltoupdatecontract) {
1017 $this->error = $this->db->lasterror();
1018 $this->db->rollback();
1019 return -1;
1020 }
1021
1022 if (!$notrigger) {
1023 // Call trigger
1024 $result = $this->call_trigger('LINECONTRACT_CLOSE', $user);
1025 if ($result < 0) {
1026 $error++;
1027 $this->db->rollback();
1028 return -1;
1029 }
1030 // End call triggers
1031 }
1032
1033 $this->db->commit();
1034 return 1;
1035 } else {
1036 $this->error = $this->db->lasterror();
1037 $this->db->rollback();
1038 return -1;
1039 }
1040 }
1041}
$object ref
Definition info.php:90
fetch_optionals($rowid=null, $optionsArray=null)
Function to get extra fields of an object into $this->array_options This method is in most cases call...
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0)
Get buy price to use for margin calculation.
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
wasEnteredIncludingTax()
Return true if the unit price was originally entered including tax (TTC mode).
getPriceBaseType()
Return the price base type ('TTC' or 'HT') matching how the unit price was entered.
Class to manage lines of contracts.
active_line($user, $date, $date_end='', $comment='')
Activate a contract line.
getLibStatut($mode)
Return label of this contract line status.
insert($notrigger=0)
Inserts a contrat line into database.
fetch($id, $ref='')
Load object in memory from database.
close_line($user, $date_end_real, $comment='', $notrigger=0)
Close a contract line.
static LibStatut($status, $mode, $expired=-1, $moreatt='', $morelabel='')
Return label of a contract line status.
update($user, $notrigger=0)
Update database for contract line.
update_total()
Update in database the fields total_xxx of lines Used by migration process.
__construct($db)
Constructor.
getTooltipContentArray($params)
getTooltipContentArray
getNomUrl($withpicto=0, $maxlength=0)
Return clickable name (with picto eventually) for ContratLigne.
print $langs trans("Ref").' m titre as m m statut as status
Or an array listing all the potential status of the object: array: int of the status => translated la...
Definition index.php:169
global $mysoc
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as description
Only used if Module[ID]Desc translation string is not found.
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
dol_now($mode='gmt')
Return date for now.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
getLocalTaxesFromRate($vatrate, $local, $buyer, $seller, $firstparamisid=0)
Get type and rate of localtaxes for a particular vat rate/country of a thirdparty.
dol_clone($srcobject, $native=2)
Create a clone of instance of object (new instance with same value for each properties) With native =...
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.
dolGetStatus($statusLabel='', $statusLabelShort='', $html='', $statusType='status0', $displayMode=0, $url='', $params=array())
Output the badge of a status.
getMarginInfos($pv_ht, $remise_percent, $tva_tx, $localtax1_tx, $localtax2_tx, $fk_pa, $pa_ht)
Return an array with margins information of a line.
calcul_price_total($qty, $pu, $remise_percent_ligne, $txtva, $uselocaltax1_rate, $uselocaltax2_rate, $notused, $price_base_type, $info_bits, $type, $seller=null, $localtaxes_array=[], $progress=100, $multicurrency_tx=1, $pu_devise=0, $multicurrency_code='')
Calculate totals (net, vat, ...) of a line.
Definition price.lib.php:90
if(preg_match('/(crypted|dolcrypt):/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition repair.php:130