dolibarr 21.0.3
factureligne.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2002-2007 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2013 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2004 Sebastien Di Cintio <sdicintio@ressource-toi.org>
5 * Copyright (C) 2004 Benoit Mortier <benoit.mortier@opensides.be>
6 * Copyright (C) 2005 Marc Barilley / Ocebo <marc@ocebo.com>
7 * Copyright (C) 2005-2014 Regis Houssin <regis.houssin@inodbox.com>
8 * Copyright (C) 2006 Andre Cianfarani <acianfa@free.fr>
9 * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
10 * Copyright (C) 2010-2020 Juanjo Menent <jmenent@2byte.es>
11 * Copyright (C) 2012-2014 Christophe Battarel <christophe.battarel@altairis.fr>
12 * Copyright (C) 2012-2015 Marcos García <marcosgdf@gmail.com>
13 * Copyright (C) 2012 Cédric Salvador <csalvador@gpcsolutions.fr>
14 * Copyright (C) 2012-2014 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
15 * Copyright (C) 2013 Cedric Gross <c.gross@kreiz-it.fr>
16 * Copyright (C) 2013 Florian Henry <florian.henry@open-concept.pro>
17 * Copyright (C) 2016-2022 Ferran Marcet <fmarcet@2byte.es>
18 * Copyright (C) 2018-2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
19 * Copyright (C) 2018 Nicolas ZABOURI <info@inovea-conseil.com>
20 * Copyright (C) 2022 Sylvain Legrand <contact@infras.fr>
21 * Copyright (C) 2023 Gauthier VERDOL <gauthier.verdol@atm-consulting.fr>
22 * Copyright (C) 2023 Nick Fragoulis
23 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
24 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
25 *
26 * This program is free software; you can redistribute it and/or modify
27 * it under the terms of the GNU General Public License as published by
28 * the Free Software Foundation; either version 3 of the License, or
29 * (at your option) any later version.
30 *
31 * This program is distributed in the hope that it will be useful,
32 * but WITHOUT ANY WARRANTY; without even the implied warranty of
33 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
34 * GNU General Public License for more details.
35 *
36 * You should have received a copy of the GNU General Public License
37 * along with this program. If not, see <https://www.gnu.org/licenses/>.
38 */
39
46require_once DOL_DOCUMENT_ROOT.'/core/class/commoninvoice.class.php';
47require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
48require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
49require_once DOL_DOCUMENT_ROOT.'/margin/lib/margins.lib.php';
50
56{
60 public $element = 'facturedet';
61
65 public $table_element = 'facturedet';
66
70 public $oldline;
71
73
76 public $fk_facture;
80 public $fk_parent_line;
81
85 public $desc;
89 public $ref_ext;
90
94 public $localtax1_type; // Local tax 1 type
98 public $localtax2_type; // Local tax 2 type
102 public $fk_remise_except; // Link to line into llx_remise_except
106 public $rang = 0;
110 public $fk_fournprice;
114 public $pa_ht;
118 public $marge_tx;
122 public $marque_tx;
123
127 public $tva_npr;
128
132 public $remise_percent;
133
137 public $batch;
141 public $fk_warehouse;
142
143
147 public $origin;
151 public $origin_id;
152
156 public $fk_code_ventilation = 0;
157
158
162 public $date_start;
166 public $date_end;
167
171 public $skip_update_total; // Skip update price total for special lines
172
176 public $situation_percent;
177
181 public $fk_prev_id;
182
183
189 public function __construct($db)
190 {
191 $this->db = $db;
192 }
193
200 public function fetch($rowid)
201 {
202 $sql = 'SELECT fd.rowid, fd.fk_facture, fd.fk_parent_line, fd.fk_product, fd.product_type, fd.label as custom_label, fd.description, fd.price, fd.qty, fd.vat_src_code, fd.tva_tx,';
203 $sql .= ' fd.localtax1_tx, fd. localtax2_tx, fd.remise, fd.remise_percent, fd.fk_remise_except, fd.subprice, fd.ref_ext,';
204 $sql .= ' fd.date_start as date_start, fd.date_end as date_end, fd.fk_product_fournisseur_price as fk_fournprice, fd.buy_price_ht as pa_ht,';
205 $sql .= ' fd.info_bits, fd.special_code, fd.total_ht, fd.total_tva, fd.total_ttc, fd.total_localtax1, fd.total_localtax2, fd.rang,';
206 $sql .= ' fd.fk_code_ventilation,';
207 $sql .= ' fd.batch, fd.fk_warehouse,';
208 $sql .= ' fd.fk_unit, fd.fk_user_author, fd.fk_user_modif,';
209 $sql .= ' fd.situation_percent, fd.fk_prev_id,';
210 $sql .= ' fd.multicurrency_subprice,';
211 $sql .= ' fd.multicurrency_total_ht,';
212 $sql .= ' fd.multicurrency_total_tva,';
213 $sql .= ' fd.multicurrency_total_ttc,';
214 $sql .= ' p.ref as product_ref, p.label as product_label, p.description as product_desc';
215 $sql .= ' FROM '.MAIN_DB_PREFIX.'facturedet as fd';
216 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'product as p ON fd.fk_product = p.rowid';
217 $sql .= ' WHERE fd.rowid = '.((int) $rowid);
218
219 $result = $this->db->query($sql);
220 if ($result) {
221 $objp = $this->db->fetch_object($result);
222
223 if (!$objp) {
224 $this->error = 'InvoiceLine with id '. $rowid .' not found sql='.$sql;
225 return 0;
226 }
227
228 $this->rowid = $objp->rowid;
229 $this->id = $objp->rowid;
230 $this->fk_facture = $objp->fk_facture;
231 $this->fk_parent_line = $objp->fk_parent_line;
232 $this->label = $objp->custom_label;
233 $this->desc = $objp->description;
234 $this->qty = $objp->qty;
235 $this->subprice = $objp->subprice;
236 $this->ref_ext = $objp->ref_ext;
237 $this->vat_src_code = $objp->vat_src_code;
238 $this->tva_tx = $objp->tva_tx;
239 $this->localtax1_tx = $objp->localtax1_tx;
240 $this->localtax2_tx = $objp->localtax2_tx;
241 $this->remise_percent = $objp->remise_percent;
242 $this->fk_remise_except = $objp->fk_remise_except;
243 $this->fk_product = $objp->fk_product;
244 $this->product_type = $objp->product_type;
245 $this->date_start = $this->db->jdate($objp->date_start);
246 $this->date_end = $this->db->jdate($objp->date_end);
247 $this->info_bits = $objp->info_bits;
248 $this->tva_npr = ((($objp->info_bits & 1) == 1) ? 1 : 0);
249 $this->special_code = $objp->special_code;
250 $this->total_ht = $objp->total_ht;
251 $this->total_tva = $objp->total_tva;
252 $this->total_localtax1 = $objp->total_localtax1;
253 $this->total_localtax2 = $objp->total_localtax2;
254 $this->total_ttc = $objp->total_ttc;
255 $this->fk_code_ventilation = $objp->fk_code_ventilation;
256 $this->batch = $objp->batch;
257 $this->fk_warehouse = $objp->fk_warehouse;
258 $this->rang = $objp->rang;
259 $this->fk_fournprice = $objp->fk_fournprice;
260 $marginInfos = getMarginInfos($objp->subprice, $objp->remise_percent, $objp->tva_tx, $objp->localtax1_tx, $objp->localtax2_tx, $this->fk_fournprice, $objp->pa_ht);
261 $this->pa_ht = $marginInfos[0];
262 $this->marge_tx = (string) $marginInfos[1];
263 $this->marque_tx = (string) $marginInfos[2];
264
265 $this->ref = $objp->product_ref; // deprecated
266
267 $this->product_ref = $objp->product_ref;
268 $this->product_label = $objp->product_label;
269 $this->product_desc = $objp->product_desc;
270
271 $this->fk_unit = $objp->fk_unit;
272 $this->fk_user_modif = $objp->fk_user_modif;
273 $this->fk_user_author = $objp->fk_user_author;
274
275 $this->situation_percent = $objp->situation_percent;
276 $this->fk_prev_id = $objp->fk_prev_id;
277
278 $this->multicurrency_subprice = $objp->multicurrency_subprice;
279 $this->multicurrency_total_ht = $objp->multicurrency_total_ht;
280 $this->multicurrency_total_tva = $objp->multicurrency_total_tva;
281 $this->multicurrency_total_ttc = $objp->multicurrency_total_ttc;
282
283 $this->fetch_optionals();
284
285 $this->db->free($result);
286
287 return 1;
288 } else {
289 $this->error = $this->db->lasterror();
290 return -1;
291 }
292 }
293
301 public function insert($notrigger = 0, $noerrorifdiscountalreadylinked = 0)
302 {
303 global $langs, $user;
304
305 $error = 0;
306
307 $pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
308 $this->pa_ht = (float) $this->pa_ht; // convert to float but after checking if value is empty
309
310 dol_syslog(get_class($this)."::insert rang=".$this->rang, LOG_DEBUG);
311
312 // Clean parameters
313 $this->desc = trim($this->desc);
314 if (empty($this->tva_tx)) {
315 $this->tva_tx = 0;
316 }
317 if (empty($this->localtax1_tx)) {
318 $this->localtax1_tx = 0;
319 }
320 if (empty($this->localtax2_tx)) {
321 $this->localtax2_tx = 0;
322 }
323 if (empty($this->localtax1_type)) {
324 $this->localtax1_type = 0;
325 }
326 if (empty($this->localtax2_type)) {
327 $this->localtax2_type = 0;
328 }
329 if (empty($this->total_localtax1)) {
330 $this->total_localtax1 = 0;
331 }
332 if (empty($this->total_localtax2)) {
333 $this->total_localtax2 = 0;
334 }
335 if (empty($this->rang)) {
336 $this->rang = 0;
337 }
338 if (empty($this->remise_percent)) {
339 $this->remise_percent = 0;
340 }
341 if (empty($this->info_bits)) {
342 $this->info_bits = 0;
343 }
344 if (empty($this->subprice)) {
345 $this->subprice = 0;
346 }
347 if (empty($this->ref_ext)) {
348 $this->ref_ext = '';
349 }
350 if (empty($this->special_code)) {
351 $this->special_code = 0;
352 }
353 if (empty($this->fk_parent_line)) {
354 $this->fk_parent_line = 0;
355 }
356 if (empty($this->fk_prev_id)) {
357 $this->fk_prev_id = 0;
358 }
359 if (!isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') {
360 $this->situation_percent = 100;
361 }
362
363 if (empty($this->multicurrency_subprice)) {
364 $this->multicurrency_subprice = 0;
365 }
366 if (empty($this->multicurrency_total_ht)) {
367 $this->multicurrency_total_ht = 0;
368 }
369 if (empty($this->multicurrency_total_tva)) {
370 $this->multicurrency_total_tva = 0;
371 }
372 if (empty($this->multicurrency_total_ttc)) {
373 $this->multicurrency_total_ttc = 0;
374 }
375
376 // if buy price not defined, define buyprice as configured in margin admin
377 if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
378 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
379 if ($result < 0) {
380 return $result;
381 } else {
382 $this->pa_ht = $result;
383 }
384 }
385
386 // Check parameters
387 if ($this->product_type < 0) {
388 $this->error = 'ErrorProductTypeMustBe0orMore';
389 return -1;
390 }
391 if (!empty($this->fk_product) && $this->fk_product > 0) {
392 // Check product exists
393 $result = Product::isExistingObject('product', $this->fk_product);
394 if ($result <= 0) {
395 $this->error = 'ErrorProductIdDoesNotExists';
396 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
397 return -1;
398 }
399 }
400
401 $this->db->begin();
402
403 // Update line in database
404 $sql = 'INSERT INTO '.MAIN_DB_PREFIX.'facturedet';
405 $sql .= ' (fk_facture, fk_parent_line, label, description, qty,';
406 $sql .= ' vat_src_code, tva_tx, localtax1_tx, localtax2_tx, localtax1_type, localtax2_type,';
407 $sql .= ' fk_product, product_type, remise_percent, subprice, ref_ext, fk_remise_except,';
408 $sql .= ' date_start, date_end, fk_code_ventilation,';
409 $sql .= ' rang, special_code, fk_product_fournisseur_price, buy_price_ht,';
410 $sql .= ' info_bits, total_ht, total_tva, total_ttc, total_localtax1, total_localtax2,';
411 $sql .= ' situation_percent, fk_prev_id,';
412 $sql .= ' fk_unit, fk_user_author, fk_user_modif,';
413 $sql .= ' fk_multicurrency, multicurrency_code, multicurrency_subprice, multicurrency_total_ht, multicurrency_total_tva, multicurrency_total_ttc,';
414 $sql .= ' batch, fk_warehouse';
415 $sql .= ')';
416 $sql .= " VALUES (".$this->fk_facture.",";
417 $sql .= " ".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null").",";
418 $sql .= " ".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null").",";
419 $sql .= " '".$this->db->escape($this->desc)."',";
420 $sql .= " ".price2num($this->qty).",";
421 $sql .= " ".(empty($this->vat_src_code) ? "''" : "'".$this->db->escape($this->vat_src_code)."'").",";
422 $sql .= " ".price2num($this->tva_tx).",";
423 $sql .= " ".price2num($this->localtax1_tx).",";
424 $sql .= " ".price2num($this->localtax2_tx).",";
425 $sql .= " '".$this->db->escape($this->localtax1_type)."',";
426 $sql .= " '".$this->db->escape($this->localtax2_type)."',";
427 $sql .= ' '.((!empty($this->fk_product) && $this->fk_product > 0) ? $this->fk_product : "null").',';
428 $sql .= " ".((int) $this->product_type).",";
429 $sql .= " ".price2num($this->remise_percent).",";
430 $sql .= " ".price2num($this->subprice).",";
431 $sql .= " '".$this->db->escape($this->ref_ext)."',";
432 $sql .= ' '.(!empty($this->fk_remise_except) ? $this->fk_remise_except : "null").',';
433 $sql .= " ".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null").",";
434 $sql .= " ".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null").",";
435 $sql .= ' '.((int) $this->fk_code_ventilation).',';
436 $sql .= ' '.((int) $this->rang).',';
437 $sql .= ' '.((int) $this->special_code).',';
438 $sql .= ' '.(!empty($this->fk_fournprice) ? $this->fk_fournprice : "null").',';
439 $sql .= ' '.price2num($this->pa_ht).',';
440 $sql .= " '".$this->db->escape($this->info_bits)."',";
441 $sql .= " ".price2num($this->total_ht).",";
442 $sql .= " ".price2num($this->total_tva).",";
443 $sql .= " ".price2num($this->total_ttc).",";
444 $sql .= " ".price2num($this->total_localtax1).",";
445 $sql .= " ".price2num($this->total_localtax2);
446 $sql .= ", ".((float) $this->situation_percent);
447 $sql .= ", ".(!empty($this->fk_prev_id) ? $this->fk_prev_id : "null");
448 $sql .= ", ".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
449 $sql .= ", ".((int) $user->id);
450 $sql .= ", ".((int) $user->id);
451 $sql .= ", ".(int) $this->fk_multicurrency;
452 $sql .= ", '".$this->db->escape($this->multicurrency_code)."'";
453 $sql .= ", ".price2num($this->multicurrency_subprice);
454 $sql .= ", ".price2num($this->multicurrency_total_ht);
455 $sql .= ", ".price2num($this->multicurrency_total_tva);
456 $sql .= ", ".price2num($this->multicurrency_total_ttc);
457 $sql .= ", '".$this->db->escape($this->batch)."'";
458 $sql .= ", ".((int) $this->fk_warehouse);
459 $sql .= ')';
460
461 dol_syslog(get_class($this)."::insert", LOG_DEBUG);
462 $resql = $this->db->query($sql);
463 if ($resql) {
464 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX.'facturedet');
465 $this->rowid = $this->id; // For backward compatibility
466
467 if (!$error) {
468 $result = $this->insertExtraFields();
469 if ($result < 0) {
470 $error++;
471 }
472 }
473
474 // If fk_remise_except is defined, the discount is linked to the invoice
475 // which flags it as "consumed".
476 if ($this->fk_remise_except && empty($error)) {
477 $discount = new DiscountAbsolute($this->db);
478 $result = $discount->fetch($this->fk_remise_except);
479 if ($result >= 0) {
480 // Check if discount was found
481 if ($result > 0) {
482 // Check if discount not already affected to another invoice
483 if ($discount->fk_facture_line > 0) {
484 if (empty($noerrorifdiscountalreadylinked)) {
485 $this->error = $langs->trans("ErrorDiscountAlreadyUsed", $discount->id);
486 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
487 $this->db->rollback();
488 return -3;
489 }
490 } else {
491 $result = $discount->link_to_invoice($this->rowid, 0);
492 if ($result < 0) {
493 $this->error = $discount->error;
494 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
495 $this->db->rollback();
496 return -3;
497 }
498 }
499 } else {
500 $this->error = $langs->trans("ErrorADiscountThatHasBeenRemovedIsIncluded");
501 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
502 $this->db->rollback();
503 return -3;
504 }
505 } else {
506 $this->error = $discount->error;
507 dol_syslog(get_class($this)."::insert Error ".$this->error, LOG_ERR);
508 $this->db->rollback();
509 return -3;
510 }
511 }
512
513 if (!$notrigger && empty($error)) {
514 // Call trigger
515 $result = $this->call_trigger('LINEBILL_INSERT', $user);
516 if ($result < 0) {
517 $this->db->rollback();
518 return -2;
519 }
520 // End call triggers
521 }
522
523 if (!$error) {
524 $this->db->commit();
525 return $this->id;
526 }
527
528 foreach ($this->errors as $errmsg) {
529 dol_syslog(get_class($this)."::insert ".$errmsg, LOG_ERR);
530 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
531 }
532 $this->db->rollback();
533 return -3;
534 } else {
535 $this->error = $this->db->lasterror();
536 $this->db->rollback();
537 return -2;
538 }
539 }
540
548 public function update($user = null, $notrigger = 0)
549 {
550 global $user;
551
552 $error = 0;
553
554 $pa_ht_isemptystring = (empty($this->pa_ht) && $this->pa_ht == ''); // If true, we can use a default value. If this->pa_ht = '0', we must use '0'.
555
556 // Clean parameters
557 $this->desc = trim($this->desc);
558 if (empty($this->ref_ext)) {
559 $this->ref_ext = '';
560 }
561 if (empty($this->tva_tx)) {
562 $this->tva_tx = 0;
563 }
564 if (empty($this->localtax1_tx)) {
565 $this->localtax1_tx = 0;
566 }
567 if (empty($this->localtax2_tx)) {
568 $this->localtax2_tx = 0;
569 }
570 if (empty($this->localtax1_type)) {
571 $this->localtax1_type = 0;
572 }
573 if (empty($this->localtax2_type)) {
574 $this->localtax2_type = 0;
575 }
576 if (empty($this->total_localtax1)) {
577 $this->total_localtax1 = 0;
578 }
579 if (empty($this->total_localtax2)) {
580 $this->total_localtax2 = 0;
581 }
582 if (empty($this->remise_percent)) {
583 $this->remise_percent = 0;
584 }
585 if (empty($this->info_bits)) {
586 $this->info_bits = 0;
587 }
588 if (empty($this->special_code)) {
589 $this->special_code = 0;
590 }
591 if (empty($this->product_type)) {
592 $this->product_type = 0;
593 }
594 if (empty($this->fk_parent_line)) {
595 $this->fk_parent_line = 0;
596 }
597 if (!isset($this->situation_percent) || $this->situation_percent > 100 || (string) $this->situation_percent == '') {
598 $this->situation_percent = 100;
599 }
600 if (empty($this->pa_ht)) {
601 $this->pa_ht = 0;
602 }
603
604 if (empty($this->multicurrency_subprice)) {
605 $this->multicurrency_subprice = 0;
606 }
607 if (empty($this->multicurrency_total_ht)) {
608 $this->multicurrency_total_ht = 0;
609 }
610 if (empty($this->multicurrency_total_tva)) {
611 $this->multicurrency_total_tva = 0;
612 }
613 if (empty($this->multicurrency_total_ttc)) {
614 $this->multicurrency_total_ttc = 0;
615 }
616
617 // Check parameters
618 if ($this->product_type < 0) {
619 return -1;
620 }
621
622 // if buy price not provided, define buyprice as configured in margin admin
623 if ($this->pa_ht == 0 && $pa_ht_isemptystring) {
624 // We call defineBuyPrice only if data was not provided (if input was '0', we will not go here and value will remaine '0')
625 $result = $this->defineBuyPrice($this->subprice, $this->remise_percent, $this->fk_product);
626 if ($result < 0) {
627 return $result;
628 } else {
629 $this->pa_ht = $result;
630 }
631 }
632
633 $this->db->begin();
634
635 // Update line in database
636 $sql = "UPDATE ".MAIN_DB_PREFIX."facturedet SET";
637 $sql .= " description='".$this->db->escape($this->desc)."'";
638 $sql .= ", ref_ext='".$this->db->escape($this->ref_ext)."'";
639 $sql .= ", label=".(!empty($this->label) ? "'".$this->db->escape($this->label)."'" : "null");
640 $sql .= ", subprice=".price2num($this->subprice);
641 $sql .= ", remise_percent=".price2num($this->remise_percent);
642 if ($this->fk_remise_except) {
643 $sql .= ", fk_remise_except=".$this->fk_remise_except;
644 } else {
645 $sql .= ", fk_remise_except=null";
646 }
647 $sql .= ", vat_src_code = '".(empty($this->vat_src_code) ? '' : $this->db->escape($this->vat_src_code))."'";
648 $sql .= ", tva_tx=".price2num($this->tva_tx);
649 $sql .= ", localtax1_tx=".price2num($this->localtax1_tx);
650 $sql .= ", localtax2_tx=".price2num($this->localtax2_tx);
651 $sql .= ", localtax1_type='".$this->db->escape($this->localtax1_type)."'";
652 $sql .= ", localtax2_type='".$this->db->escape($this->localtax2_type)."'";
653 $sql .= ", qty=".price2num($this->qty);
654 $sql .= ", date_start=".(!empty($this->date_start) ? "'".$this->db->idate($this->date_start)."'" : "null");
655 $sql .= ", date_end=".(!empty($this->date_end) ? "'".$this->db->idate($this->date_end)."'" : "null");
656 $sql .= ", product_type=".$this->product_type;
657 $sql .= ", info_bits='".$this->db->escape($this->info_bits)."'";
658 $sql .= ", special_code=" . (int) $this->special_code;
659 if (empty($this->skip_update_total)) {
660 $sql .= ", total_ht=".price2num($this->total_ht);
661 $sql .= ", total_tva=".price2num($this->total_tva);
662 $sql .= ", total_ttc=".price2num($this->total_ttc);
663 $sql .= ", total_localtax1=".price2num($this->total_localtax1);
664 $sql .= ", total_localtax2=".price2num($this->total_localtax2);
665 }
666 $sql .= ", fk_product_fournisseur_price=".(!empty($this->fk_fournprice) ? "'".$this->db->escape($this->fk_fournprice)."'" : "null");
667 $sql .= ", buy_price_ht=".(($this->pa_ht || (string) $this->pa_ht === '0') ? price2num($this->pa_ht) : "null"); // $this->pa_ht should always be defined (set to 0 or to sell price depending on option)
668 $sql .= ", fk_parent_line=".($this->fk_parent_line > 0 ? $this->fk_parent_line : "null");
669 if (!empty($this->rang)) {
670 $sql .= ", rang=".((int) $this->rang);
671 }
672 $sql .= ", situation_percent = ".((float) $this->situation_percent);
673 $sql .= ", fk_unit = ".(!$this->fk_unit ? 'NULL' : $this->fk_unit);
674 $sql .= ", fk_user_modif = ".((int) $user->id);
675
676 // Multicurrency
677 $sql .= ", multicurrency_subprice=".price2num($this->multicurrency_subprice);
678 $sql .= ", multicurrency_total_ht=".price2num($this->multicurrency_total_ht);
679 $sql .= ", multicurrency_total_tva=".price2num($this->multicurrency_total_tva);
680 $sql .= ", multicurrency_total_ttc=".price2num($this->multicurrency_total_ttc);
681
682 $sql .= ", batch = '".$this->db->escape($this->batch)."'";
683 $sql .= ", fk_warehouse = ".((int) $this->fk_warehouse);
684
685 $sql .= " WHERE rowid = ".((int) $this->rowid);
686
687 dol_syslog(get_class($this)."::update", LOG_DEBUG);
688 $resql = $this->db->query($sql);
689 if ($resql) {
690 if (!$error) {
691 $this->id = $this->rowid;
692 $result = $this->insertExtraFields();
693 if ($result < 0) {
694 $error++;
695 }
696 }
697
698 if (!$error && !$notrigger) {
699 // Call trigger
700 $result = $this->call_trigger('LINEBILL_MODIFY', $user);
701 if ($result < 0) {
702 $this->db->rollback();
703 return -2;
704 }
705 // End call triggers
706 }
707
708 if (!$error) {
709 $this->db->commit();
710 return 1;
711 }
712
713 foreach ($this->errors as $errmsg) {
714 dol_syslog(get_class($this)."::update ".$errmsg, LOG_ERR);
715 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
716 }
717
718 $this->db->rollback();
719 return -3;
720 } else {
721 $this->error = $this->db->error();
722 $this->db->rollback();
723 return -2;
724 }
725 }
726
734 public function delete($tmpuser = null, $notrigger = 0)
735 {
736 global $user;
737
738 $this->db->begin();
739
740 // Call trigger
741 if (empty($notrigger)) {
742 $result = $this->call_trigger('LINEBILL_DELETE', $user);
743 if ($result < 0) {
744 $this->db->rollback();
745 return -1;
746 }
747 }
748 // End call triggers
749
750 // extrafields
751 $result = $this->deleteExtraFields();
752 if ($result < 0) {
753 $this->db->rollback();
754 return -1;
755 }
756
757 // Free discount linked to invoice line
758 $sql = 'UPDATE '.MAIN_DB_PREFIX.'societe_remise_except';
759 $sql .= ' SET fk_facture_line = NULL';
760 $sql .= ' WHERE fk_facture_line = '.((int) $this->id);
761
762 dol_syslog(get_class($this)."::deleteline", LOG_DEBUG);
763 $result = $this->db->query($sql);
764 if (!$result) {
765 $this->error = $this->db->error();
766 $this->errors[] = $this->error;
767 $this->db->rollback();
768 return -1;
769 }
770
771 $sql = 'UPDATE '.MAIN_DB_PREFIX.'element_time';
772 $sql .= ' SET invoice_id = NULL, invoice_line_id = NULL';
773 $sql .= ' WHERE invoice_line_id = '.((int) $this->id);
774 if (!$this->db->query($sql)) {
775 $this->error = $this->db->error()." sql=".$sql;
776 $this->errors[] = $this->error;
777 $this->db->rollback();
778 return -1;
779 }
780
781 $sql = "DELETE FROM ".MAIN_DB_PREFIX."facturedet WHERE rowid = ".((int) $this->id);
782
783 if ($this->db->query($sql)) {
784 $this->db->commit();
785 return 1;
786 } else {
787 $this->error = $this->db->error()." sql=".$sql;
788 $this->errors[] = $this->error;
789 $this->db->rollback();
790 return -1;
791 }
792 }
793
794 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
801 public function update_total()
802 {
803 // phpcs:enable
804 $this->db->begin();
805 dol_syslog(get_class($this)."::update_total", LOG_DEBUG);
806
807 // Clean parameters
808 if (empty($this->total_localtax1)) {
809 $this->total_localtax1 = 0;
810 }
811 if (empty($this->total_localtax2)) {
812 $this->total_localtax2 = 0;
813 }
814
815 // Update line in database
816 $sql = "UPDATE ".MAIN_DB_PREFIX."facturedet SET";
817 $sql .= " total_ht=".price2num($this->total_ht);
818 $sql .= ",total_tva=".price2num($this->total_tva);
819 $sql .= ",total_localtax1=".price2num($this->total_localtax1);
820 $sql .= ",total_localtax2=".price2num($this->total_localtax2);
821 $sql .= ",total_ttc=".price2num($this->total_ttc);
822 $sql .= " WHERE rowid = ".((int) $this->rowid);
823
824 dol_syslog(get_class($this)."::update_total", LOG_DEBUG);
825
826 $resql = $this->db->query($sql);
827 if ($resql) {
828 $this->db->commit();
829 return 1;
830 } else {
831 $this->error = $this->db->error();
832 $this->db->rollback();
833 return -2;
834 }
835 }
836
837 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
847 public function get_prev_progress($invoiceid, $include_credit_note = true)
848 {
849 // phpcs:enable
850 global $invoicecache;
851
852 if (is_null($this->fk_prev_id) || empty($this->fk_prev_id) || $this->fk_prev_id == "") {
853 return 0;
854 } else {
855 // If invoice is not a situation invoice, this->fk_prev_id is used for something else
856 if (!isset($invoicecache[$invoiceid])) {
857 $invoicecache[$invoiceid] = new Facture($this->db);
858 $invoicecache[$invoiceid]->fetch($invoiceid);
859 }
860 if ($invoicecache[$invoiceid]->type != Facture::TYPE_SITUATION) {
861 return 0;
862 }
863
864 $sql = "SELECT situation_percent FROM ".MAIN_DB_PREFIX."facturedet";
865 $sql .= " WHERE rowid = ".((int) $this->fk_prev_id);
866
867 $resql = $this->db->query($sql);
868
869 if ($resql && $this->db->num_rows($resql) > 0) {
870 $returnPercent = 0;
871
872 $obj = $this->db->fetch_object($resql);
873 if ($obj) {
874 $returnPercent = (float) $obj->situation_percent;
875 }
876
877 if ($include_credit_note) {
878 $sql = 'SELECT fd.situation_percent FROM '.MAIN_DB_PREFIX.'facturedet fd';
879 $sql .= ' JOIN '.MAIN_DB_PREFIX.'facture f ON (f.rowid = fd.fk_facture) ';
880 $sql .= " WHERE fd.fk_prev_id = ".((int) $this->fk_prev_id);
881 $sql .= " AND f.situation_cycle_ref = ".((int) $invoicecache[$invoiceid]->situation_cycle_ref); // Prevent cycle outed
882 $sql .= " AND f.type = ".Facture::TYPE_CREDIT_NOTE;
883
884 $res = $this->db->query($sql);
885 if ($res) {
886 while ($obj = $this->db->fetch_object($res)) {
887 $returnPercent += (float) $obj->situation_percent;
888 }
889 } else {
890 dol_print_error($this->db);
891 }
892 }
893
894 return $returnPercent;
895 } else {
896 $this->error = $this->db->error();
897 dol_syslog(get_class($this)."::select Error ".$this->error, LOG_ERR);
898 $this->db->rollback();
899 return -1;
900 }
901 }
902 }
903
904 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
914 public function getAllPrevProgress($invoiceid, $include_credit_note = true)
915 {
916 // phpcs:enable
917 global $invoicecache;
918
919 if (is_null($this->fk_prev_id) || empty($this->fk_prev_id) || $this->fk_prev_id == "") {
920 return 0;
921 } else {
922 // If invoice is not a situation invoice, this->fk_prev_id is used for something else
923 if (!isset($invoicecache[$invoiceid])) {
924 $invoicecache[$invoiceid] = new Facture($this->db);
925 $invoicecache[$invoiceid]->fetch($invoiceid);
926 }
927 if ($invoicecache[$invoiceid]->type != Facture::TYPE_SITUATION) {
928 return 0;
929 }
930
931 $all_found = false;
932 $lastprevid = $this->fk_prev_id;
933 $cumulated_percent = 0.0;
934
935 while (!$all_found) {
936 $sql = "SELECT situation_percent, fk_prev_id FROM ".MAIN_DB_PREFIX."facturedet WHERE rowid = ".((int) $lastprevid);
937 $resql = $this->db->query($sql);
938
939 if ($resql && $this->db->num_rows($resql) > 0) {
940 $obj = $this->db->fetch_object($resql);
941 $cumulated_percent += floatval($obj->situation_percent);
942
943 if ($include_credit_note) {
944 $sql_credit_note = 'SELECT fd.situation_percent FROM '.MAIN_DB_PREFIX.'facturedet fd';
945 $sql_credit_note .= ' JOIN '.MAIN_DB_PREFIX.'facture f ON (f.rowid = fd.fk_facture) ';
946 $sql_credit_note .= " WHERE fd.fk_prev_id = ".((int) $lastprevid);
947 $sql_credit_note .= " AND f.situation_cycle_ref = ".((int) $invoicecache[$invoiceid]->situation_cycle_ref); // Prevent cycle outed
948 $sql_credit_note .= " AND f.type = ".Facture::TYPE_CREDIT_NOTE;
949
950 $res_credit_note = $this->db->query($sql_credit_note);
951 if ($res_credit_note) {
952 while ($cn = $this->db->fetch_object($res_credit_note)) {
953 $cumulated_percent += floatval($cn->situation_percent);
954 }
955 } else {
956 dol_print_error($this->db);
957 }
958 }
959
960 // Si fk_prev_id, on continue
961 if ($obj->fk_prev_id) {
962 $lastprevid = $obj->fk_prev_id;
963 } else { // Sinon on stoppe la boucle
964 $all_found = true;
965 }
966 } else {
967 $this->error = $this->db->error();
968 dol_syslog(get_class($this)."::select Error ".$this->error, LOG_ERR);
969 $this->db->rollback();
970 return -1;
971 }
972 }
973 return $cumulated_percent;
974 }
975 }
976}
$object ref
Definition info.php:89
Parent class of all other business classes for details of elements (invoices, contracts,...
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.
static isExistingObject($element, $id, $ref='', $ref_ext='')
Check if an object id or ref exists If you don't need or want to instantiate the object and just need...
deleteExtraFields()
Delete all extra fields values for the current object.
insertExtraFields($trigger='', $userused=null)
Add/Update all extra fields values for the current object.
call_trigger($triggerName, $user)
Call trigger based on this instance.
Class to manage absolute discounts.
Class to manage invoices.
const TYPE_SITUATION
Situation invoice.
Class to manage invoice lines.
insert($notrigger=0, $noerrorifdiscountalreadylinked=0)
Insert line into database.
get_prev_progress($invoiceid, $include_credit_note=true)
Returns situation_percent of the previous line.
fetch($rowid)
Load invoice line from database.
update($user=null, $notrigger=0)
Update line into database.
__construct($db)
Constructor.
getAllPrevProgress($invoiceid, $include_credit_note=true)
Returns situation_percent of all the previous line.
update_total()
Update DB line fields total_xxx Used by migration.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_error($db=null, $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
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.
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition repair.php:150