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 $fk_parent_line;
97
101 public $statut;
102
106 public $type;
107
112 public $label;
113
118 public $libelle;
119
123 public $description;
124
128 public $product_type;
129
133 public $product_ref;
134
138 public $product_label;
139
143 public $date_commande;
144
148 public $date_start;
149
153 public $date_start_real;
154
158 public $date_end;
159
163 public $date_end_real;
164
168 public $tva_tx;
169
173 public $vat_src_code;
174
178 public $localtax1_tx;
179
183 public $localtax2_tx;
184
188 public $localtax1_type;
189
193 public $localtax2_type;
194
198 public $qty;
199
203 public $remise_percent;
204
208 public $fk_remise_except;
209
213 public $subprice;
214
218 public $total_ht;
219
223 public $total_tva;
224
228 public $total_localtax1;
229
233 public $total_localtax2;
234
238 public $total_ttc;
239
243 public $fk_fournprice;
244
248 public $pa_ht;
249
253 public $info_bits;
254
258 public $fk_user_author;
259
263 public $fk_user_ouverture;
264
268 public $fk_user_cloture;
269
273 public $commentaire;
274
275
279 public $rang = 0;
280
281
282 const STATUS_INITIAL = 0;
283 const STATUS_OPEN = 4;
284 const STATUS_CLOSED = 5;
285
286
287 // BEGIN MODULEBUILDER PROPERTIES
291 public $fields = array(
292 'rowid' => array('type' => 'integer', 'label' => 'TechnicalID', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 10),
293 'entity' => array('type' => 'integer', 'label' => 'Entity', 'default' => '1', 'enabled' => 1, 'visible' => -2, 'notnull' => 1, 'position' => 30, 'index' => 1),
294 'tms' => array('type' => 'timestamp', 'label' => 'DateModification', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 35),
295 'qty' => array('type' => 'integer', 'label' => 'Quantity', 'enabled' => 1, 'visible' => 1, 'notnull' => 1, 'position' => 35, 'isameasure' => 1),
296 'total_ht' => array('type' => 'integer', 'label' => 'AmountHT', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 36, 'isameasure' => 1),
297 'total_tva' => array('type' => 'integer', 'label' => 'AmountVAT', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 37, 'isameasure' => 1),
298 'total_ttc' => array('type' => 'integer', 'label' => 'AmountTTC', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 38, 'isameasure' => 1),
299 //'datec' =>array('type'=>'datetime', 'label'=>'DateCreation', 'enabled'=>1, 'visible'=>-1, 'position'=>40),
300 //'fk_soc' =>array('type'=>'integer:Societe:societe/class/societe.class.php', 'label'=>'ThirdParty', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>70),
301 'fk_contrat' => array('type' => 'integer:Contrat:contrat/class/contrat.class.php', 'label' => 'Contract', 'enabled' => 1, 'visible' => -1, 'notnull' => 1, 'position' => 70),
302 'fk_product' => array('type' => 'integer:Product:product/class/product.class.php:1', 'label' => 'Product', 'enabled' => 1, 'visible' => -1, 'position' => 75),
303 //'fk_user_author' =>array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>1, 'visible'=>-1, 'notnull'=>1, 'position'=>90),
304 'note_private' => array('type' => 'html', 'label' => 'NotePublic', 'enabled' => 1, 'visible' => 0, 'position' => 105),
305 'note_public' => array('type' => 'html', 'label' => 'NotePrivate', 'enabled' => 1, 'visible' => 0, 'position' => 110),
306 //'model_pdf' =>array('type'=>'varchar(255)', 'label'=>'Model pdf', 'enabled'=>1, 'visible'=>0, 'position'=>115),
307 //'import_key' =>array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>1, 'visible'=>-2, 'position'=>120),
308 //'extraparams' =>array('type'=>'varchar(255)', 'label'=>'Extraparams', 'enabled'=>1, 'visible'=>-1, 'position'=>125),
309 'fk_user_ouverture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserStartingService', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 135),
310 'fk_user_cloture' => array('type' => 'integer:User:user/class/user.class.php', 'label' => 'UserClosingService', 'enabled' => 1, 'visible' => -2, 'notnull' => -1, 'position' => 135),
311 'statut' => array('type' => 'smallint(6)', 'label' => 'Statut', 'enabled' => 1, 'visible' => -1, 'position' => 500, 'arrayofkeyval' => array(0 => 'Draft', 4 => 'Open', 5 => 'Closed')),
312 'rang' => array('type' => 'integer', 'label' => 'Rank', 'enabled' => 1, 'visible' => 0, 'position' => 500, 'default' => '0')
313 );
314 // END MODULEBUILDER PROPERTIES
315
316
322 public function __construct($db)
323 {
324 $this->db = $db;
325 }
326
327
334 public function getLibStatut($mode)
335 {
336 return $this->LibStatut($this->statut, $mode, ((!empty($this->date_end)) ? ($this->date_end < dol_now() ? 1 : 0) : -1));
337 }
338
339 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
350 public static function LibStatut($status, $mode, $expired = -1, $moreatt = '', $morelabel = '')
351 {
352 // phpcs:enable
353 global $langs;
354 $langs->load("contracts");
355
356 if ($status == self::STATUS_INITIAL) {
357 $labelStatus = $langs->transnoentities("ServiceStatusInitial");
358 $labelStatusShort = $langs->transnoentities("ServiceStatusInitial");
359 } elseif ($status == self::STATUS_OPEN && $expired == -1) {
360 $labelStatus = $langs->transnoentities("ServiceStatusRunning");
361 $labelStatusShort = $langs->transnoentities("ServiceStatusRunning");
362 } elseif ($status == self::STATUS_OPEN && $expired == 0) {
363 $labelStatus = $langs->transnoentities("ServiceStatusNotLate");
364 $labelStatusShort = $langs->transnoentities("ServiceStatusNotLateShort");
365 } elseif ($status == self::STATUS_OPEN && $expired == 1) {
366 $labelStatus = $langs->transnoentities("ServiceStatusLate");
367 $labelStatusShort = $langs->transnoentities("ServiceStatusLateShort");
368 } elseif ($status == self::STATUS_CLOSED) {
369 $labelStatus = $langs->transnoentities("ServiceStatusClosed");
370 $labelStatusShort = $langs->transnoentities("ServiceStatusClosed");
371 } else {
372 $labelStatus = '';
373 $labelStatusShort = '';
374 }
375
376 $statusType = 'status'.$status;
377 if ($status == self::STATUS_OPEN && $expired == 1) {
378 $statusType = 'status1';
379 }
380 if ($status == self::STATUS_CLOSED) {
381 $statusType = 'status6';
382 }
383
384 $params = array();
385 $reg = array();
386 if (preg_match('/class="(.*)"/', $moreatt, $reg)) {
387 $params = array('badgeParams' => array('css' => $reg[1]));
388 }
389 return dolGetStatus($labelStatus.($morelabel ? ' '.$morelabel : ''), $labelStatusShort.($morelabel ? ' '.$morelabel : ''), '', $statusType, $mode, '', $params);
390 }
391
398 public function getTooltipContentArray($params)
399 {
400 global $conf, $langs, $user;
401
402 $datas = [];
403 $datas['label'] = $langs->trans("ShowContractOfService").': '.$this->label;
404 if (empty($this->label)) {
405 $datas['label'] = $this->description;
406 }
407
408 return $datas;
409 }
410
418 public function getNomUrl($withpicto = 0, $maxlength = 0)
419 {
420 global $langs;
421
422 $result = '';
423 $label = $langs->trans("ShowContractOfService").': '.$this->label;
424 if (empty($this->label)) {
425 $label = $this->description;
426 }
427 $classfortooltip = 'classfortooltip';
428 $dataparams = '';
429 if (getDolGlobalInt('MAIN_ENABLE_AJAX_TOOLTIP')) {
430 $params = [
431 'id' => $this->fk_contrat,
432 'objecttype' => $this->element,
433 ];
434 $classfortooltip = 'classforajaxtooltip';
435 $dataparams = ' data-params="'.dol_escape_htmltag(json_encode($params)).'"';
436 $label = '';
437 }
438
439 $link = '<a href="'.DOL_URL_ROOT.'/contrat/card.php?id='.$this->fk_contrat.'"';
440 $link .= ($label ? ' title="'.dolPrintHTMLForAttribute($label).'"' : ' title="tocomplete"');
441 $link .= $dataparams.' class="'.$classfortooltip.'">';
442 $linkend = '</a>';
443
444 $picto = 'service';
445 if ($this->type == 0) {
446 $picto = 'product';
447 }
448
449 if ($withpicto) {
450 $result .= ($link.img_object($label, $picto, $dataparams.' class="'.$classfortooltip.'"').$linkend);
451 }
452 if ($withpicto && $withpicto != 2) {
453 $result .= ' ';
454 }
455 if ($withpicto != 2) {
456 $result .= $link.($this->product_ref ? $this->product_ref.' ' : '').($this->label ? $this->label : $this->description).$linkend;
457 }
458
459 global $action, $hookmanager;
460 $hookmanager->initHooks(array($this->element . 'dao'));
461 $parameters = array('id' => $this->id, 'getnomurl' => &$result);
462 $reshook = $hookmanager->executeHooks('getNomUrl', $parameters, $this, $action); // Note that $action and $object may have been modified by some hooks
463 if ($reshook > 0) {
464 $result = $hookmanager->resPrint;
465 } else {
466 $result .= $hookmanager->resPrint;
467 }
468
469 return $result;
470 }
471
479 public function fetch($id, $ref = '')
480 {
481 // Check parameters
482 if (empty($id) && empty($ref)) {
483 return -1;
484 }
485
486 $sql = "SELECT";
487 $sql .= " t.rowid,";
488 $sql .= " t.tms,";
489 $sql .= " t.fk_contrat,";
490 $sql .= " t.fk_product,";
491 $sql .= " t.statut,";
492 $sql .= " t.label,"; // This field is not used. Only label of product
493 $sql .= " p.ref as product_ref,";
494 $sql .= " p.label as product_label,";
495 $sql .= " p.description as product_description,";
496 $sql .= " p.fk_product_type as product_type,";
497 $sql .= " t.description,";
498 $sql .= " t.date_commande,";
499 $sql .= " t.date_ouverture_prevue as date_start,";
500 $sql .= " t.date_ouverture as date_start_real,";
501 $sql .= " t.date_fin_validite as date_end,";
502 $sql .= " t.date_cloture as date_end_real,";
503 $sql .= " t.tva_tx,";
504 $sql .= " t.vat_src_code,";
505 $sql .= " t.localtax1_tx,";
506 $sql .= " t.localtax2_tx,";
507 $sql .= " t.localtax1_type,";
508 $sql .= " t.localtax2_type,";
509 $sql .= " t.qty,";
510 $sql .= " t.remise_percent,";
511 $sql .= " t.fk_remise_except,";
512 $sql .= " t.subprice,";
513 $sql .= " t.subprice_ttc,";
514 $sql .= " t.total_ht,";
515 $sql .= " t.total_tva,";
516 $sql .= " t.total_localtax1,";
517 $sql .= " t.total_localtax2,";
518 $sql .= " t.total_ttc,";
519 $sql .= " t.fk_product_fournisseur_price as fk_fournprice,";
520 $sql .= " t.buy_price_ht as pa_ht,";
521 $sql .= " t.info_bits,";
522 $sql .= " t.fk_user_author,";
523 $sql .= " t.fk_user_ouverture,";
524 $sql .= " t.fk_user_cloture,";
525 $sql .= " t.commentaire,";
526 $sql .= " t.fk_unit,";
527 $sql .= " t.extraparams,";
528 $sql .= " t.rang";
529 $sql .= " FROM ".MAIN_DB_PREFIX."contratdet as t LEFT JOIN ".MAIN_DB_PREFIX."product as p ON p.rowid = t.fk_product";
530 if ($id) {
531 $sql .= " WHERE t.rowid = ".((int) $id);
532 }
533 if ($ref) {
534 $sql .= " WHERE t.rowid = '".$this->db->escape($ref)."'";
535 }
536
537 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
538 $resql = $this->db->query($sql);
539 if ($resql) {
540 if ($this->db->num_rows($resql)) {
541 $obj = $this->db->fetch_object($resql);
542
543 $this->id = $obj->rowid;
544 $this->ref = $obj->rowid;
545
546 $this->tms = $this->db->jdate($obj->tms);
547 $this->fk_contrat = (int) $obj->fk_contrat;
548 $this->fk_product = (int) $obj->fk_product;
549 $this->statut = $obj->statut;
550 $this->product_ref = $obj->product_ref;
551 $this->product_label = $obj->product_label;
552 $this->product_type = $obj->product_type;
553 $this->label = $obj->label; // deprecated. We do not use this field. Only ref and label of product, and description of contract line
554 $this->description = $obj->description;
555 $this->date_commande = $this->db->jdate($obj->date_commande);
556
557 $this->date_start = $this->db->jdate($obj->date_start);
558 $this->date_start_real = $this->db->jdate($obj->date_start_real);
559 $this->date_end = $this->db->jdate($obj->date_end);
560 $this->date_end_real = $this->db->jdate($obj->date_end_real);
561
562 $this->tva_tx = $obj->tva_tx;
563 $this->vat_src_code = $obj->vat_src_code;
564 $this->localtax1_tx = $obj->localtax1_tx;
565 $this->localtax2_tx = $obj->localtax2_tx;
566 $this->localtax1_type = $obj->localtax1_type;
567 $this->localtax2_type = $obj->localtax2_type;
568 $this->qty = $obj->qty;
569 $this->remise_percent = $obj->remise_percent;
570 $this->fk_remise_except = $obj->fk_remise_except;
571 $this->subprice = $obj->subprice;
572 $this->subprice_ttc = $obj->subprice_ttc;
573 $this->total_ht = $obj->total_ht;
574 $this->total_tva = $obj->total_tva;
575 $this->total_localtax1 = $obj->total_localtax1;
576 $this->total_localtax2 = $obj->total_localtax2;
577 $this->total_ttc = $obj->total_ttc;
578 $this->info_bits = $obj->info_bits;
579 $this->fk_user_author = $obj->fk_user_author;
580 $this->fk_user_ouverture = $obj->fk_user_ouverture;
581 $this->fk_user_cloture = $obj->fk_user_cloture;
582 $this->commentaire = $obj->commentaire;
583 $this->fk_fournprice = $obj->fk_fournprice;
584
585 $marginInfos = getMarginInfos($obj->subprice, $obj->remise_percent, $obj->tva_tx, $obj->localtax1_tx, $obj->localtax2_tx, $this->fk_fournprice, $obj->pa_ht);
586 $this->pa_ht = $marginInfos[0];
587 $this->fk_unit = $obj->fk_unit;
588
589 $this->extraparams = !empty($obj->extraparams) ? (array) json_decode($obj->extraparams, true) : array();
590
591 $this->rang = $obj->rang;
592
593 $this->fetch_optionals();
594 }
595
596 $this->db->free($resql);
597
598 return 1;
599 } else {
600 $this->error = "Error ".$this->db->lasterror();
601 return -1;
602 }
603 }
604
605
613 public function update($user, $notrigger = 0)
614 {
615 global $mysoc;
616
617 $error = 0;
618
619 // Clean parameters
620 $this->fk_contrat = (int) $this->fk_contrat;
621 $this->fk_product = (int) $this->fk_product;
622 $this->statut = (int) $this->statut;
623 $this->label = trim((string) $this->label);
624 $this->description = trim((string) $this->description);
625 $this->vat_src_code = trim((string) $this->vat_src_code);
626 $this->tva_tx = trim((string) $this->tva_tx);
627 $this->localtax1_tx = trim((string) $this->localtax1_tx);
628 $this->localtax2_tx = trim((string) $this->localtax2_tx);
629 $this->qty = (float) $this->qty;
630 $this->remise_percent = trim((string) $this->remise_percent);
631 $this->fk_remise_except = (int) $this->fk_remise_except;
632 $this->subprice = (float) price2num($this->subprice);
633 $this->info_bits = (int) $this->info_bits;
634 $this->fk_user_author = (int) $this->fk_user_author;
635 $this->fk_user_ouverture = (int) $this->fk_user_ouverture;
636 $this->fk_user_cloture = (int) $this->fk_user_cloture;
637 $this->commentaire = trim((string) $this->commentaire);
638 $this->rang = (int) $this->rang;
639 if (empty($this->subprice)) {
640 $this->subprice = 0;
641 }
642 if (empty($this->total_ht)) {
643 $this->total_ht = 0;
644 }
645 if (empty($this->total_tva)) {
646 $this->total_tva = 0;
647 }
648 if (empty($this->total_ttc)) {
649 $this->total_ttc = 0;
650 }
651 if (empty($this->localtax1_tx)) {
652 $this->localtax1_tx = 0;
653 }
654 if (empty($this->localtax2_tx)) {
655 $this->localtax2_tx = 0;
656 }
657 if (empty($this->remise_percent)) {
658 $this->remise_percent = 0;
659 }
660
661 // Calculation of the gross total (TTC) and VAT for the line from qty, pu, remise_percent and txtva
662 // VERY IMPORTANT: It's at the time of line insertion that we must store the net, VAT, and gross amounts,
663 // and this is done at the line level, which has its own VAT rate
664 $localtaxes_type = getLocalTaxesFromRate($this->tva_tx, 0, $this->thirdparty, $mysoc);
665
666 // Compute the total from the value the user actually entered, to avoid a rounding drift.
667 $line_price_base_type = $this->getPriceBaseType();
668 $pu_for_calc = $this->wasEnteredIncludingTax() ? (float) $this->subprice_ttc : (float) $this->subprice;
669
670 $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);
671 $this->total_ht = (float) $tabprice[0];
672 $this->total_tva = (float) $tabprice[1];
673 $this->total_ttc = (float) $tabprice[2];
674 $this->total_localtax1 = (float) $tabprice[9];
675 $this->total_localtax2 = (float) $tabprice[10];
676
677 if (empty($this->pa_ht)) {
678 $this->pa_ht = 0;
679 }
680
681 // if buy price not defined, define buyprice as configured in margin admin
682 if ($this->pa_ht == 0) {
683 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product, $this->qty);
684 if ($result < 0) {
685 return -1;
686 } else {
687 $this->pa_ht = $result;
688 }
689 }
690
691 // $this->oldcopy should have been set by the caller of update (here properties were already modified)
692 if (empty($this->oldcopy)) {
693 dol_syslog("this->oldcopy should have been set by the caller of update (here properties were already modified)", LOG_WARNING);
694 $this->oldcopy = dol_clone($this, 2);
695 }
696
697 $this->db->begin();
698
699 // Update request
700 $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET";
701 $sql .= " fk_contrat = ".((int) $this->fk_contrat).",";
702 $sql .= " fk_product = ".($this->fk_product ? ((int) $this->fk_product) : 'null').",";
703 $sql .= " statut = ".((int) $this->statut).",";
704 $sql .= " label = '".$this->db->escape($this->label)."',";
705 $sql .= " description = '".$this->db->escape($this->description)."',";
706 $sql .= " date_commande = ".($this->date_commande != '' ? "'".$this->db->idate($this->date_commande)."'" : "null").",";
707 $sql .= " date_ouverture_prevue = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : "null").",";
708 $sql .= " date_ouverture = ".($this->date_start_real != '' ? "'".$this->db->idate($this->date_start_real)."'" : "null").",";
709 $sql .= " date_fin_validite = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : "null").",";
710 $sql .= " date_cloture = ".($this->date_end_real != '' ? "'".$this->db->idate($this->date_end_real)."'" : "null").",";
711 $sql .= " vat_src_code = '".$this->db->escape($this->vat_src_code)."',";
712 $sql .= " tva_tx = ".price2num($this->tva_tx).",";
713 $sql .= " localtax1_tx = ".price2num($this->localtax1_tx).",";
714 $sql .= " localtax2_tx = ".price2num($this->localtax2_tx).",";
715 $sql .= " qty = ".price2num($this->qty).",";
716 $sql .= " remise_percent = ".price2num($this->remise_percent).",";
717 $sql .= " fk_remise_except = ".($this->fk_remise_except > 0 ? ((int) $this->fk_remise_except) : "null").",";
718 $sql .= " subprice = ".($this->subprice != '' ? ((float) $this->subprice) : "null").",";
719 $sql .= " subprice_ttc = ".($this->subprice_ttc != '' ? ((float) $this->subprice_ttc) : "0").",";
720 $sql .= " total_ht = ".((float) $this->total_ht).",";
721 $sql .= " total_tva = ".((float) $this->total_tva).",";
722 $sql .= " total_localtax1 = ".((float) $this->total_localtax1).",";
723 $sql .= " total_localtax2 = ".((float) $this->total_localtax2).",";
724 $sql .= " total_ttc = ".((float) $this->total_ttc).",";
725 $sql .= " fk_product_fournisseur_price = ".(!empty($this->fk_fournprice) ? ((int) $this->fk_fournprice) : "NULL").",";
726 $sql .= " buy_price_ht = '".price2num($this->pa_ht)."',";
727 $sql .= " info_bits = '".$this->db->escape((string) $this->info_bits)."',";
728 $sql .= " fk_user_author = ".($this->fk_user_author >= 0 ? ((int) $this->fk_user_author) : "NULL").",";
729 $sql .= " fk_user_ouverture = ".($this->fk_user_ouverture > 0 ? ((int) $this->fk_user_ouverture) : "NULL").",";
730 $sql .= " fk_user_cloture = ".($this->fk_user_cloture > 0 ? ((int) $this->fk_user_cloture) : "NULL").",";
731 $sql .= " commentaire = '".$this->db->escape($this->commentaire)."',";
732 $sql .= " fk_unit = ".(!$this->fk_unit ? 'NULL' : ((int) $this->fk_unit)).",";
733 $sql .= " rang = ".(empty($this->rang) ? '0' : ((int) $this->rang));
734 $sql .= " WHERE rowid = ".((int) $this->id);
735
736 dol_syslog(get_class($this)."::update", LOG_DEBUG);
737 $resql = $this->db->query($sql);
738 if (!$resql) {
739 $this->error = "Error ".$this->db->lasterror();
740 $error++;
741 }
742
743 if (!$error) { // For avoid conflicts if trigger used
744 $result = $this->insertExtraFields();
745 if ($result < 0) {
746 $error++;
747 }
748 }
749
750 // If we change a planned date (start or end) of one contract line, sync dates for all other services too
751 if (!$error && getDolGlobalString('CONTRACT_SYNC_PLANNED_DATE_OF_SERVICES')) {
752 dol_syslog(get_class($this)."::update CONTRACT_SYNC_PLANNED_DATE_OF_SERVICES is on so we update date for all lines", LOG_DEBUG);
753
754 if ($this->date_start != $this->oldcopy->date_start) {
755 $sql = 'UPDATE '.MAIN_DB_PREFIX.'contratdet SET';
756 $sql .= " date_ouverture_prevue = ".($this->date_start != '' ? "'".$this->db->idate($this->date_start)."'" : "null");
757 $sql .= " WHERE fk_contrat = ".((int) $this->fk_contrat);
758
759 $resql = $this->db->query($sql);
760 if (!$resql) {
761 $error++;
762 $this->error = "Error ".$this->db->lasterror();
763 }
764 }
765 if ($this->date_end != $this->oldcopy->date_end) {
766 $sql = 'UPDATE '.MAIN_DB_PREFIX.'contratdet SET';
767 $sql .= " date_fin_validite = ".($this->date_end != '' ? "'".$this->db->idate($this->date_end)."'" : "null");
768 $sql .= " WHERE fk_contrat = ".((int) $this->fk_contrat);
769
770 $resql = $this->db->query($sql);
771 if (!$resql) {
772 $error++;
773 $this->error = "Error ".$this->db->lasterror();
774 }
775 }
776 }
777
778 if (!$error && !$notrigger) {
779 // Call trigger
780 $result = $this->call_trigger('LINECONTRACT_MODIFY', $user);
781 if ($result < 0) {
782 $error++;
783 $this->db->rollback();
784 }
785 // End call triggers
786 }
787
788 if (!$error) {
789 $this->db->commit();
790 return 1;
791 } else {
792 $this->db->rollback();
793 $this->errors[] = $this->error;
794 return -1;
795 }
796 }
797
798
799 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
806 public function update_total()
807 {
808 // phpcs:enable
809 $this->db->begin();
810
811 // Update line in database
812 $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET";
813 $sql .= " total_ht=".price2num($this->total_ht, 'MT');
814 $sql .= ",total_tva=".price2num($this->total_tva, 'MT');
815 $sql .= ",total_localtax1=".price2num($this->total_localtax1, 'MT');
816 $sql .= ",total_localtax2=".price2num($this->total_localtax2, 'MT');
817 $sql .= ",total_ttc=".price2num($this->total_ttc, 'MT');
818 $sql .= " WHERE rowid = ".((int) $this->id);
819
820 dol_syslog(get_class($this)."::update_total", LOG_DEBUG);
821
822 $resql = $this->db->query($sql);
823 if ($resql) {
824 $this->db->commit();
825 return 1;
826 } else {
827 $this->error = $this->db->error();
828 $this->db->rollback();
829 return -2;
830 }
831 }
832
833
840 public function insert($notrigger = 0)
841 {
842 global $user;
843
844 $error = 0;
845
846 // Insert into database
847 $sql = "INSERT INTO ".MAIN_DB_PREFIX."contratdet";
848 $sql .= " (fk_contrat, label, description, fk_product, qty, vat_src_code, tva_tx,";
849 $sql .= " localtax1_tx, localtax2_tx, localtax1_type, localtax2_type, remise_percent, subprice, subprice_ttc,";
850 $sql .= " total_ht, total_tva, total_localtax1, total_localtax2, total_ttc,";
851 $sql .= " info_bits,";
852 $sql .= " rang,";
853 $sql .= " fk_product_fournisseur_price, buy_price_ht";
854 if ($this->date_start > 0) {
855 $sql .= ",date_ouverture_prevue";
856 }
857 if ($this->date_end > 0) {
858 $sql .= ",date_fin_validite";
859 }
860 $sql .= ") VALUES (".((int) $this->fk_contrat).", '', '".$this->db->escape($this->description)."',";
861 $sql .= ($this->fk_product > 0 ? ((int) $this->fk_product) : "null").",";
862 $sql .= " '".$this->db->escape((string) $this->qty)."',";
863 $sql .= " '".$this->db->escape($this->vat_src_code)."',";
864 $sql .= " '".$this->db->escape($this->tva_tx)."',";
865 $sql .= " '".$this->db->escape($this->localtax1_tx)."',";
866 $sql .= " '".$this->db->escape($this->localtax2_tx)."',";
867 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
868 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
869 $sql .= " ".price2num($this->remise_percent).",".price2num($this->subprice).",".((float) price2num($this->subprice_ttc)).",";
870 $sql .= " ".price2num($this->total_ht).",".price2num($this->total_tva).",".price2num($this->total_localtax1).",".price2num($this->total_localtax2).",".price2num($this->total_ttc).",";
871 $sql .= " '".$this->db->escape((string) $this->info_bits)."',";
872 $sql .= " ".(empty($this->rang) ? '0' : (int) $this->rang).",";
873 if ($this->fk_fournprice > 0) {
874 $sql .= ' '.((int) $this->fk_fournprice).',';
875 } else {
876 $sql .= ' null,';
877 }
878 if ($this->pa_ht > 0) {
879 $sql .= ' '.((float) price2num($this->pa_ht));
880 } else {
881 $sql .= ' null';
882 }
883 if ($this->date_start > 0) {
884 $sql .= ",'".$this->db->idate($this->date_start)."'";
885 }
886 if ($this->date_end > 0) {
887 $sql .= ",'".$this->db->idate($this->date_end)."'";
888 }
889 $sql .= ")";
890
891 dol_syslog(get_class($this)."::insert", LOG_DEBUG);
892
893 $resql = $this->db->query($sql);
894 if ($resql) {
895 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'contratdet');
896
897 // Insert of extrafields
898 $result = $this->insertExtraFields();
899 if ($result < 0) {
900 $this->db->rollback();
901 return -1;
902 }
903
904 if (!$notrigger) {
905 // Call trigger
906 $result = $this->call_trigger('LINECONTRACT_INSERT', $user);
907 if ($result < 0) {
908 $this->db->rollback();
909 return -1;
910 }
911 // End call triggers
912 }
913
914 $this->db->commit();
915 return 1;
916 } else {
917 $this->db->rollback();
918 $this->error = $this->db->error()." sql=".$sql;
919 return -1;
920 }
921 }
922
923 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
933 public function active_line($user, $date, $date_end = '', $comment = '')
934 {
935 // phpcs:enable
936 $error = 0;
937
938 $this->db->begin();
939
940 $this->statut = ContratLigne::STATUS_OPEN;
941 $this->status = ContratLigne::STATUS_OPEN;
942 $this->date_start_real = $date;
943 $this->date_end = $date_end;
944 $this->fk_user_ouverture = $user->id;
945 $this->date_end_real = null;
946 $this->commentaire = $comment;
947
948 $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET statut = ".((int) $this->status).",";
949 $sql .= " date_ouverture = ".(dol_strlen((string) $this->date_start_real) != 0 ? "'".$this->db->idate($this->date_start_real)."'" : "null").",";
950 if ($date_end >= 0) {
951 $sql .= " date_fin_validite = ".(dol_strlen($this->date_end) != 0 ? "'".$this->db->idate($this->date_end)."'" : "null").",";
952 }
953 $sql .= " fk_user_ouverture = ".((int) $this->fk_user_ouverture).",";
954 $sql .= " date_cloture = null,";
955 $sql .= " fk_user_cloture = null,";
956 $sql .= " commentaire = '".$this->db->escape($comment)."'";
957 $sql .= " WHERE rowid = ".((int) $this->id)." AND (statut = ".ContratLigne::STATUS_INITIAL." OR statut = ".ContratLigne::STATUS_CLOSED.")";
958
959 dol_syslog(get_class($this)."::active_line", LOG_DEBUG);
960 $resql = $this->db->query($sql);
961 if ($resql) {
962 if ($date_end >= 0) {
963 // Update column llx_contrat.denormalized_lower_panned_end_date with next expiration date of an open contract
964 $sqltoupdatecontract = "UPDATE ".MAIN_DB_PREFIX."contrat";
965 $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.")";
966 $sqltoupdatecontract .= " WHERE rowid = ".((int) $this->fk_contrat);
967 $resqltoupdatecontract = $this->db->query($sqltoupdatecontract);
968 if (!$resqltoupdatecontract) {
969 $this->error = $this->db->lasterror();
970 $this->db->rollback();
971 return -1;
972 }
973 }
974
975 // Call trigger
976 $result = $this->call_trigger('LINECONTRACT_ACTIVATE', $user);
977 if ($result < 0) {
978 $error++;
979 }
980 // End call triggers
981
982 if (!$error) {
983 $this->db->commit();
984 return 1;
985 } else {
986 $this->db->rollback();
987 return -1;
988 }
989 } else {
990 $this->error = $this->db->lasterror();
991 $this->db->rollback();
992 return -1;
993 }
994 }
995
996 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
1006 public function close_line($user, $date_end_real, $comment = '', $notrigger = 0)
1007 {
1008 // phpcs:enable
1009 $this->date_cloture = $date_end_real;
1010 $this->date_end_real = $date_end_real;
1011 $this->user_closing_id = $user->id;
1012 $this->commentaire = $comment;
1013
1014 $error = 0;
1015
1016 // statut actif : 4
1017
1018 $this->db->begin();
1019
1020 $sql = "UPDATE ".MAIN_DB_PREFIX."contratdet SET statut = ".((int) ContratLigne::STATUS_CLOSED).",";
1021 $sql .= " date_cloture = '".$this->db->idate($date_end_real)."',";
1022 $sql .= " fk_user_cloture = ".((int) $user->id).",";
1023 $sql .= " commentaire = '".$this->db->escape($comment)."'";
1024 $sql .= " WHERE rowid = ".((int) $this->id)." AND statut <> ".((int) ContratLigne::STATUS_CLOSED);
1025
1026 $resql = $this->db->query($sql);
1027 if ($resql) {
1028 // Update column llx_contrat.denormalized_lower_panned_end_date with next expiration date of an open contract
1029 $sqltoupdatecontract = "UPDATE ".MAIN_DB_PREFIX."contrat";
1030 $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.")";
1031 $sqltoupdatecontract .= " WHERE rowid = ".((int) $this->fk_contrat);
1032 $resqltoupdatecontract = $this->db->query($sqltoupdatecontract);
1033 if (!$resqltoupdatecontract) {
1034 $this->error = $this->db->lasterror();
1035 $this->db->rollback();
1036 return -1;
1037 }
1038
1039 if (!$notrigger) {
1040 // Call trigger
1041 $result = $this->call_trigger('LINECONTRACT_CLOSE', $user);
1042 if ($result < 0) {
1043 $error++;
1044 $this->db->rollback();
1045 return -1;
1046 }
1047 // End call triggers
1048 }
1049
1050 $this->db->commit();
1051 return 1;
1052 } else {
1053 $this->error = $this->db->lasterror();
1054 $this->db->rollback();
1055 return -1;
1056 }
1057 }
1058}
$propal type
'integer', 'integer:ObjectClass:PathToClass[:AddCreateButtonOrNot[:Filter[:Sortfield]]]',...
Definition propal.php:280
$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...
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
defineBuyPrice($unitPrice=0.0, $discountPercent=0.0, $fk_product=0, $qty=0)
Get buy price to use for margin calculation.
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.
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