dolibarr 24.0.0-beta
discount.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2005 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3 * Copyright (C) 2004-2018 Laurent Destailleur <eldy@users.sourceforge.net>
4 * Copyright (C) 2024 Alexandre Janniaux <alexandre.janniaux@gmail.com>
5 * Copyright (C) 2024 Frédéric France <frederic.france@free.fr>
6 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
7 * Copyright (C) 2024 Noé Cendrier <noe.cendrier@altairis.fr>
8 * Copyright (C) 2026 Vincent de Grandpré <vincent@de-grandpre.quebec>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 */
23
30require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
31
36{
41 public $fk_soc;
45 public $socid;
46
50 public $discount_type; // 0 => customer discount, 1 => supplier discount
51
55 public $total_ht;
59 public $total_tva;
63 public $total_ttc;
68 public $amount_ht; // deprecated
73 public $amount_tva; // deprecated
78 public $amount_ttc; // deprecated
79
83 public $multicurrency_total_ht;
87 public $multicurrency_total_tva;
91 public $multicurrency_total_ttc;
96 public $multicurrency_amount_ht; // deprecated
101 public $multicurrency_amount_tva; // deprecated
106 public $multicurrency_amount_ttc; // deprecated
107
111 public $multicurrency_subprice;
112
116 public $fk_invoice_supplier;
117
121 public $fk_invoice_supplier_line;
122
126 public $tva_tx;
130 public $localtax1_tx;
134 public $localtax2_tx;
138 public $localtax1_type;
142 public $localtax2_type;
146 public $vat_src_code;
147
151 public $fk_user;
152
156 public $description;
157
163 public $datec;
164
168 public $fk_facture_line;
169
173 public $fk_facture;
174
178 public $fk_facture_source;
182 public $ref_facture_source; // Ref credit note or deposit used to create the discount
186 public $type_facture_source;
187
191 public $fk_invoice_supplier_source;
195 public $ref_invoice_supplier_source; // Ref credit note or deposit used to create the discount
199 public $type_invoice_supplier_source;
200
201 /* Customer Discount */
202 const TYPE_CUSTOMER = 0;
203
204 /* Supplier Discount */
205 const TYPE_SUPPLIER = 1;
206
212 public function __construct($db)
213 {
214 $this->db = $db;
215 }
216
217
226 public function fetch($rowid, $fk_facture_source = 0, $fk_invoice_supplier_source = 0)
227 {
228 // Check parameters
229 if (!$rowid && !$fk_facture_source && !$fk_invoice_supplier_source) {
230 $this->error = 'ErrorBadParameters';
231 return -1;
232 }
233
234 $sql = "SELECT sr.rowid, sr.fk_soc, sr.discount_type,";
235 $sql .= " sr.fk_user,";
236 $sql .= " sr.amount_ht, sr.amount_tva, sr.amount_localtax1, sr.amount_localtax2, sr.amount_ttc, sr.tva_tx, sr.localtax1_tx, sr.localtax1_type, sr.localtax2_tx, sr.localtax2_type, sr.vat_src_code,";
237 $sql .= " sr.multicurrency_amount_ht, sr.multicurrency_amount_tva, sr.multicurrency_amount_ttc,";
238 $sql .= " sr.fk_facture_line, sr.fk_facture, sr.fk_facture_source, sr.fk_invoice_supplier_line, sr.fk_invoice_supplier, sr.fk_invoice_supplier_source, sr.description,";
239 $sql .= " sr.datec,";
240 $sql .= " f.ref as ref_facture_source, f.type as type_facture_source,";
241 $sql .= " fsup.ref as ref_invoice_supplier_source, fsup.type as type_invoice_supplier_source";
242 $sql .= " FROM ".$this->db->prefix()."societe_remise_except as sr";
243 $sql .= " LEFT JOIN ".$this->db->prefix()."facture as f ON sr.fk_facture_source = f.rowid";
244 $sql .= " LEFT JOIN ".$this->db->prefix()."facture_fourn as fsup ON sr.fk_invoice_supplier_source = fsup.rowid";
245 $sql .= " WHERE sr.entity IN (".getEntity('invoice').")";
246 if ($rowid) {
247 $sql .= " AND sr.rowid = ".((int) $rowid);
248 }
249 if ($fk_facture_source) {
250 $sql .= " AND sr.fk_facture_source = ".((int) $fk_facture_source);
251 }
252 if ($fk_invoice_supplier_source) {
253 $sql .= " AND sr.fk_invoice_supplier_source = ".((int) $fk_invoice_supplier_source);
254 }
255
256 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
257 $resql = $this->db->query($sql);
258 if ($resql) {
259 if ($this->db->num_rows($resql)) {
260 $obj = $this->db->fetch_object($resql);
261
262 $this->id = $obj->rowid;
263 $this->fk_soc = $obj->fk_soc;
264 $this->socid = $obj->fk_soc;
265 $this->discount_type = $obj->discount_type;
266
267 $this->total_ht = $obj->amount_ht;
268 $this->total_tva = $obj->amount_tva;
269 $this->total_localtax1 = $obj->amount_localtax1;
270 $this->total_localtax2 = $obj->amount_localtax2;
271 $this->total_ttc = $obj->amount_ttc;
272 // For backward compatibility
273 $this->amount_ht = $this->total_ht;
274 $this->amount_tva = $this->total_tva;
275 $this->amount_ttc = $this->total_ttc;
276
277 $this->multicurrency_total_ht = $this->multicurrency_subprice = $obj->multicurrency_amount_ht;
278 $this->multicurrency_total_tva = $obj->multicurrency_amount_tva;
279 $this->multicurrency_total_ttc = $obj->multicurrency_amount_ttc;
280 // For backward compatibility
281 $this->multicurrency_amount_ht = $this->multicurrency_total_ht;
282 $this->multicurrency_amount_tva = $this->multicurrency_total_tva;
283 $this->multicurrency_amount_ttc = $this->multicurrency_total_ttc;
284
285 $this->tva_tx = $obj->tva_tx;
286 $this->localtax1_tx = $obj->localtax1_tx;
287 $this->localtax1_type = $obj->localtax1_type;
288 $this->localtax2_tx = $obj->localtax2_tx;
289 $this->localtax2_type = $obj->localtax2_type;
290 $this->vat_src_code = $obj->vat_src_code;
291
292 $this->fk_user = $obj->fk_user;
293 $this->fk_facture_line = $obj->fk_facture_line;
294 $this->fk_facture = $obj->fk_facture;
295 $this->fk_facture_source = $obj->fk_facture_source; // Id credit note or deposit source
296 $this->ref_facture_source = $obj->ref_facture_source; // Ref credit note or deposit source
297 $this->type_facture_source = $obj->type_facture_source; // Type credit note or deposit source
298 $this->fk_invoice_supplier_line = $obj->fk_invoice_supplier_line;
299 $this->fk_invoice_supplier = $obj->fk_invoice_supplier;
300 $this->fk_invoice_supplier_source = $obj->fk_invoice_supplier_source; // Id credit note or deposit source
301 $this->ref_invoice_supplier_source = $obj->ref_invoice_supplier_source; // Ref credit note or deposit source
302 $this->type_invoice_supplier_source = $obj->type_invoice_supplier_source; // Type credit note or deposit source
303 $this->description = $obj->description;
304 $this->datec = $this->db->jdate($obj->datec);
305
306 $this->db->free($resql);
307 return 1;
308 } else {
309 $this->db->free($resql);
310 return 0;
311 }
312 } else {
313 $this->error = $this->db->error();
314 return -1;
315 }
316 }
317
318
325 public function create($user)
326 {
327 global $conf;
328
329 // Clean parameters
330 $this->amount_ht = price2num($this->amount_ht);
331 $this->amount_tva = price2num($this->amount_tva);
332 $this->amount_ttc = price2num($this->amount_ttc);
333
334 $this->tva_tx = price2num($this->tva_tx);
335 $this->localtax1_tx = price2num($this->localtax1_tx);
336 $this->localtax1_type = price2num($this->localtax1_type);
337 $this->localtax2_tx = price2num($this->localtax2_tx);
338 $this->localtax2_type = price2num($this->localtax2_type);
339
340 $this->multicurrency_amount_ht = price2num($this->multicurrency_amount_ht);
341 $this->multicurrency_amount_tva = price2num($this->multicurrency_amount_tva);
342 $this->multicurrency_amount_ttc = price2num($this->multicurrency_amount_ttc);
343
344 if (empty($this->multicurrency_amount_ht)) {
345 $this->multicurrency_amount_ht = 0;
346 }
347 if (empty($this->multicurrency_amount_tva)) {
348 $this->multicurrency_amount_tva = 0;
349 }
350 if (empty($this->multicurrency_amount_ttc)) {
351 $this->multicurrency_amount_ttc = 0;
352 }
353 if (empty($this->tva_tx)) {
354 $this->tva_tx = 0;
355 }
356 if (empty($this->localtax1_tx)) {
357 $this->localtax1_tx = 0;
358 $this->localtax1_type = 0;
359 }
360 if (empty($this->localtax2_tx)) {
361 $this->localtax2_tx = 0;
362 $this->localtax2_type = 0;
363 }
364
365 // Check parameters
366 if (empty($this->description)) {
367 $this->error = 'BadValueForPropertyDescriptionOfDiscount';
368 dol_syslog(get_class($this)."::create ".$this->error, LOG_ERR);
369 return -1;
370 }
371
372 $userid = $user->id;
373 if (!($userid > 0)) { // For example when record is saved into an anonymous context with a not loaded object $user.
374 include_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
375 $tmpinvoice = new Facture($this->db);
376 $tmpinvoice->fetch($this->fk_facture_source);
377 $userid = $tmpinvoice->user_creation_id; // We use the author of invoice
378 }
379
380 // Insert request
381 $sql = "INSERT INTO ".$this->db->prefix()."societe_remise_except";
382 $sql .= " (entity, datec, fk_soc, discount_type, fk_user, description,";
383 $sql .= " amount_ht, amount_tva, amount_localtax1, amount_localtax2, amount_ttc, tva_tx, localtax1_tx, localtax1_type, localtax2_tx, localtax2_type, vat_src_code,";
384 $sql .= " multicurrency_amount_ht, multicurrency_amount_tva, multicurrency_amount_ttc,";
385 $sql .= " fk_facture_source, fk_invoice_supplier_source, multicurrency_code, multicurrency_tx";
386 $sql .= ")";
387 $sql .= " VALUES (".$conf->entity.", '".$this->db->idate($this->datec != '' ? $this->datec : dol_now())."', ".((int) $this->socid).", ".(empty($this->discount_type) ? 0 : intval($this->discount_type)).", ".((int) $userid).", '".$this->db->escape($this->description)."',";
388 $sql .= " ".price2num($this->amount_ht).", ".price2num($this->amount_tva).", ";
389 $sql .= " ".($this->total_localtax1 ? price2num($this->total_localtax1) : 0).", ".($this->total_localtax2 ? price2num($this->total_localtax2) : 0).", ".price2num($this->amount_ttc).", ".price2num($this->tva_tx).",";
390 $sql .= " ".price2num($this->localtax1_tx).", ".price2num($this->localtax1_type).", ";
391 $sql .= " ".($this->localtax2_tx ? price2num($this->localtax2_tx) : 0).", ".($this->localtax2_type ? price2num($this->localtax2_type) : 0).", '".$this->db->escape($this->vat_src_code)."',";
392 $sql .= " ".price2num($this->multicurrency_amount_ht).", ".price2num($this->multicurrency_amount_tva).", ";
393 $sql .= " ".price2num($this->multicurrency_amount_ttc).", ";
394 $sql .= " ".($this->fk_facture_source ? ((int) $this->fk_facture_source) : "null").",";
395 $sql .= " ".($this->fk_invoice_supplier_source ? ((int) $this->fk_invoice_supplier_source) : "null").",";
396 $sql .= " ".($this->multicurrency_code ? "'".$this->db->escape($this->multicurrency_code)."'" : "null").",";
397 $sql .= " ".($this->multicurrency_tx ? price2num($this->multicurrency_tx) : "null");
398 $sql .= ")";
399
400 dol_syslog(get_class($this)."::create", LOG_DEBUG);
401 $resql = $this->db->query($sql);
402 if ($resql) {
403 $this->id = $this->db->last_insert_id($this->db->prefix()."societe_remise_except");
404 return $this->id;
405 } else {
406 $this->error = $this->db->lasterror().' - sql='.$sql;
407 return -1;
408 }
409 }
410
411
418 public function delete($user)
419 {
420 // Check if we can remove the discount
421 if ($this->fk_facture_source) {
422 $sql = "SELECT COUNT(rowid) as nb";
423 $sql .= " FROM ".$this->db->prefix()."societe_remise_except";
424 $sql .= " WHERE (fk_facture_line IS NOT NULL"; // Not used as absolute simple discount
425 $sql .= " OR fk_facture IS NOT NULL)"; // Not used as credit note and not used as deposit
426 $sql .= " AND fk_facture_source = ".((int) $this->fk_facture_source);
427 //$sql.=" AND rowid != ".$this->id;
428
429 dol_syslog(get_class($this)."::delete Check if we can remove discount", LOG_DEBUG);
430 $resql = $this->db->query($sql);
431 if ($resql) {
432 $obj = $this->db->fetch_object($resql);
433 if ($obj->nb > 0) {
434 $this->error = 'ErrorThisPartOrAnotherIsAlreadyUsedSoDiscountSerieCantBeRemoved';
435 return -2;
436 }
437 } else {
438 dol_print_error($this->db);
439 return -1;
440 }
441 }
442
443 // Check if we can remove the discount
444 if ($this->fk_invoice_supplier_source) {
445 $sql = "SELECT COUNT(rowid) as nb";
446 $sql .= " FROM ".$this->db->prefix()."societe_remise_except";
447 $sql .= " WHERE (fk_invoice_supplier_line IS NOT NULL"; // Not used as absolute simple discount
448 $sql .= " OR fk_invoice_supplier IS NOT NULL)"; // Not used as credit note and not used as deposit
449 $sql .= " AND fk_invoice_supplier_source = ".((int) $this->fk_invoice_supplier_source);
450 //$sql.=" AND rowid != ".$this->id;
451
452 dol_syslog(get_class($this)."::delete Check if we can remove discount", LOG_DEBUG);
453 $resql = $this->db->query($sql);
454 if ($resql) {
455 $obj = $this->db->fetch_object($resql);
456 if ($obj->nb > 0) {
457 $this->error = 'ErrorThisPartOrAnotherIsAlreadyUsedSoDiscountSerieCantBeRemoved';
458 return -2;
459 }
460 } else {
461 dol_print_error($this->db);
462 return -1;
463 }
464 }
465
466 $this->db->begin();
467
468 // Delete but only if not used
469 $sql = "DELETE FROM ".$this->db->prefix()."societe_remise_except ";
470 if ($this->fk_facture_source) {
471 $sql .= " WHERE fk_facture_source = ".((int) $this->fk_facture_source); // Delete all lines of same series
472 } elseif ($this->fk_invoice_supplier_source) {
473 $sql .= " WHERE fk_invoice_supplier_source = ".((int) $this->fk_invoice_supplier_source); // Delete all lines of same series
474 } else {
475 $sql .= " WHERE rowid = ".((int) $this->id); // Delete only line
476 }
477 $sql .= " AND (fk_facture_line IS NULL"; // Not used as absolute simple discount
478 $sql .= " AND fk_facture IS NULL)"; // Not used as credit note and not used as deposit
479 $sql .= " AND (fk_invoice_supplier_line IS NULL"; // Not used as absolute simple discount
480 $sql .= " AND fk_invoice_supplier IS NULL)"; // Not used as credit note and not used as deposit
481
482 dol_syslog(get_class($this)."::delete Delete discount", LOG_DEBUG);
483
484 require_once DOL_DOCUMENT_ROOT. '/core/class/commoninvoice.class.php';
485 $result = $this->db->query($sql);
486 if ($result) {
487 // If source of discount was a credit note or deposit, we change source statut.
488 if ($this->fk_facture_source) {
489 $sql = "UPDATE ".$this->db->prefix()."facture";
490 $sql .= " set paye=0, fk_statut=1";
491 $sql .= " WHERE type IN (".$this->db->sanitize(CommonInvoice::TYPE_CREDIT_NOTE.", ".CommonInvoice::TYPE_DEPOSIT).") AND rowid = ".((int) $this->fk_facture_source);
492
493 dol_syslog(get_class($this)."::delete Update credit note or deposit invoice statut", LOG_DEBUG);
494 $result = $this->db->query($sql);
495 if ($result) {
496 $this->db->commit();
497 return 1;
498 } else {
499 $this->error = $this->db->lasterror();
500 $this->db->rollback();
501 return -1;
502 }
503 } elseif ($this->fk_invoice_supplier_source) {
504 $sql = "UPDATE ".$this->db->prefix()."facture_fourn";
505 $sql .= " set paye=0, fk_statut=1";
506 $sql .= " WHERE type IN (".$this->db->sanitize(CommonInvoice::TYPE_CREDIT_NOTE.", ".CommonInvoice::TYPE_DEPOSIT).") AND rowid = ".((int) $this->fk_invoice_supplier_source);
507
508 dol_syslog(get_class($this)."::delete Update credit note or deposit invoice statut", LOG_DEBUG);
509 $result = $this->db->query($sql);
510 if ($result) {
511 $this->db->commit();
512 return 1;
513 } else {
514 $this->error = $this->db->lasterror();
515 $this->db->rollback();
516 return -1;
517 }
518 } else {
519 $this->db->commit();
520 return 1;
521 }
522 } else {
523 $this->error = $this->db->lasterror();
524 $this->db->rollback();
525 return -1;
526 }
527 }
528
529
530
531 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
542 public function link_to_invoice($rowidline, $rowidinvoice, $notrigger = 0)
543 {
544 // phpcs:enable
545 global $user;
546
547 // Check parameters
548 if (!$rowidline && !$rowidinvoice) {
549 $this->error = 'ErrorBadParameters';
550 return -1;
551 }
552 if ($rowidline && $rowidinvoice) {
553 $this->error = 'ErrorBadParameters';
554 return -2;
555 }
556
557 $sql = "UPDATE ".$this->db->prefix()."societe_remise_except";
558 if (!empty($this->discount_type)) {
559 if ($rowidline) {
560 $sql .= " SET fk_invoice_supplier_line = ".((int) $rowidline);
561 }
562 if ($rowidinvoice) {
563 $sql .= " SET fk_invoice_supplier = ".((int) $rowidinvoice);
564 }
565 } else {
566 if ($rowidline) {
567 $sql .= " SET fk_facture_line = ".((int) $rowidline);
568 }
569 if ($rowidinvoice) {
570 $sql .= " SET fk_facture = ".((int) $rowidinvoice);
571 }
572 }
573 $sql .= " WHERE rowid = ".((int) $this->id);
574
575 dol_syslog(get_class($this)."::link_to_invoice", LOG_DEBUG);
576 $resql = $this->db->query($sql);
577 if ($resql) {
578 if (!empty($this->discount_type)) {
579 $this->fk_invoice_supplier_line = $rowidline;
580 $this->fk_invoice_supplier = $rowidinvoice;
581 } else {
582 $this->fk_facture_line = $rowidline;
583 $this->fk_facture = $rowidinvoice;
584 }
585
586 // If a discount comes from a source invoice (deposit/credit note/excess) and is applied to another invoice,
587 // also create a link between the source invoice and the target invoice.
588 if ($rowidinvoice) {
589 $sourcetype = '';
590 $targettype = '';
591 $sourceinvoiceid = 0;
592
593 if (!empty($this->discount_type)) {
594 $sourcetype = 'invoice_supplier';
595 $targettype = 'invoice_supplier';
596 $sourceinvoiceid = (int) $this->fk_invoice_supplier_source;
597 } else {
598 $sourcetype = 'facture';
599 $targettype = 'facture';
600 $sourceinvoiceid = (int) $this->fk_facture_source;
601 }
602
603 if ($sourceinvoiceid > 0 && $sourceinvoiceid !== (int) $rowidinvoice) {
604 $sqlcheck = "SELECT 1";
605 $sqlcheck .= " FROM ".$this->db->prefix()."element_element";
606 $sqlcheck .= " WHERE fk_source = ".((int) $sourceinvoiceid);
607 $sqlcheck .= " AND sourcetype = '".$this->db->escape($sourcetype)."'";
608 $sqlcheck .= " AND fk_target = ".((int) $rowidinvoice);
609 $sqlcheck .= " AND targettype = '".$this->db->escape($targettype)."'";
610 $sqlcheck .= $this->db->plimit(1);
611
612 $rescheck = $this->db->query($sqlcheck);
613 if ($rescheck && $this->db->num_rows($rescheck) === 0) {
614 $sqladd = "INSERT INTO ".$this->db->prefix()."element_element (fk_source, sourcetype, fk_target, targettype)";
615 $sqladd .= " VALUES (".((int) $sourceinvoiceid).", '".$this->db->escape($sourcetype)."', ".((int) $rowidinvoice).", '".$this->db->escape($targettype)."')";
616 $this->db->query($sqladd); // Best-effort: do not fail discount link if object link can't be created.
617 }
618 }
619 }
620
621 if (!$notrigger) {
622 // Call trigger
623 $result = $this->call_trigger('DISCOUNT_MODIFY', $user);
624 if ($result < 0) {
625 return -2;
626 }
627 // End call triggers
628 }
629 return 1;
630 } else {
631 $this->error = $this->db->error();
632 return -3;
633 }
634 }
635
636
637 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
645 public function unlink_invoice($notrigger = 0)
646 {
647 // phpcs:enable
648 global $user;
649
650 $sql = "UPDATE ".$this->db->prefix()."societe_remise_except";
651 if (!empty($this->discount_type)) {
652 $sql .= " SET fk_invoice_supplier_line = NULL, fk_invoice_supplier = NULL";
653 } else {
654 $sql .= " SET fk_facture_line = NULL, fk_facture = NULL";
655 }
656 $sql .= " WHERE rowid = ".((int) $this->id);
657
658 dol_syslog(get_class($this)."::unlink_invoice", LOG_DEBUG);
659 $resql = $this->db->query($sql);
660 if ($resql) {
661 if (!$notrigger) {
662 // Call trigger
663 $result = $this->call_trigger('DISCOUNT_MODIFY', $user);
664 if ($result < 0) {
665 return -2;
666 }
667 // End call triggers
668 }
669 return 1;
670 } else {
671 $this->error = $this->db->error();
672 return -3;
673 }
674 }
675
676
688 public function getAvailableDiscounts($company = null, $user = null, $filter = '', $maxvalue = 0, $discount_type = 0, $multicurrency = 0)
689 {
690 global $conf, $hookmanager;
691
692 dol_syslog(get_class($this)."::getAvailableDiscounts discount_type=".$discount_type, LOG_DEBUG);
693
694 $parameters = array(
695 'company' => $company,
696 'user' => $user,
697 'filter' => $filter,
698 'maxvalue' => $maxvalue,
699 'discount_type' => $discount_type,
700 'multicurrency' => $multicurrency
701 );
702
703 $reshook = $hookmanager->executeHooks('getAvailableDiscounts', $parameters);
704 if (empty($reshook)) {
705 $sql = "SELECT SUM(rc.amount_ttc) as amount, SUM(rc.multicurrency_amount_ttc) as multicurrency_amount";
706 $sql .= " FROM ".$this->db->prefix()."societe_remise_except as rc";
707 $sql .= " WHERE rc.entity = ".$conf->entity;
708 $sql .= " AND rc.discount_type=".((int) $discount_type);
709 if (!empty($discount_type)) {
710 $sql .= " AND (rc.fk_invoice_supplier IS NULL AND rc.fk_invoice_supplier_line IS NULL)"; // Available from supplier
711 } else {
712 $sql .= " AND (rc.fk_facture IS NULL AND rc.fk_facture_line IS NULL)"; // Available to customer
713 }
714 if (is_object($company)) {
715 $sql .= " AND rc.fk_soc = ".((int) $company->id);
716 }
717 if (is_object($user)) {
718 $sql .= " AND rc.fk_user = ".((int) $user->id);
719 }
720 if ($filter) {
721 $sql .= " AND (".$filter.")";
722 }
723 if ($maxvalue) {
724 $sql .= ' AND rc.amount_ttc <= '.((float) price2num($maxvalue));
725 }
726 } else {
727 $sql = $hookmanager->resArray['sql'];
728 }
729
730 $resql = $this->db->query($sql);
731 if ($resql) {
732 $obj = $this->db->fetch_object($resql);
733 //while ($obj)
734 //{
735 //print 'zz'.$obj->amount;
736 //$obj = $this->db->fetch_object($resql);
737 //}
738 if ($multicurrency) {
739 return $obj->multicurrency_amount;
740 }
741
742 return $obj->amount;
743 }
744 return -1;
745 }
746
755 public function getSumDepositsUsed($invoice, $multicurrency = 0)
756 {
757 dol_syslog(get_class($this)."::getSumDepositsUsed", LOG_DEBUG);
758
759 if ($invoice->element == 'facture' || $invoice->element == 'invoice') {
760 $sql = "SELECT sum(rc.amount_ttc) as amount, sum(rc.multicurrency_amount_ttc) as multicurrency_amount";
761 $sql .= " FROM ".$this->db->prefix()."societe_remise_except as rc, ".$this->db->prefix()."facture as f";
762 $sql .= " WHERE rc.fk_facture_source=f.rowid AND rc.fk_facture = ".((int) $invoice->id);
763 $sql .= " AND f.type = ". (int) $invoice::TYPE_DEPOSIT;
764 } elseif ($invoice->element == 'invoice_supplier') {
765 $sql = "SELECT sum(rc.amount_ttc) as amount, sum(rc.multicurrency_amount_ttc) as multicurrency_amount";
766 $sql .= " FROM ".$this->db->prefix()."societe_remise_except as rc, ".$this->db->prefix()."facture_fourn as f";
767 $sql .= " WHERE rc.fk_invoice_supplier_source=f.rowid AND rc.fk_invoice_supplier = ".((int) $invoice->id);
768 $sql .= " AND f.type = ". (int) $invoice::TYPE_DEPOSIT;
769 } else {
770 $this->error = get_class($this)."::getSumDepositsUsed was called with a bad object as a first parameter";
771 dol_print_error($this->db, $this->error);
772 return -1;
773 }
774
775 $resql = $this->db->query($sql);
776 if ($resql) {
777 $obj = $this->db->fetch_object($resql);
778 if ($multicurrency == 1) {
779 return $obj->multicurrency_amount;
780 } else {
781 return $obj->amount;
782 }
783 } else {
784 $this->error = $this->db->lasterror();
785 return -1;
786 }
787 }
788
796 public function getSumCreditNotesUsed($invoice, $multicurrency = 0)
797 {
798 dol_syslog(get_class($this)."::getSumCreditNotesUsed", LOG_DEBUG);
799
800 if ($invoice->element == 'facture' || $invoice->element == 'invoice') {
801 $sql = "SELECT sum(rc.amount_ttc) as amount, sum(rc.multicurrency_amount_ttc) as multicurrency_amount";
802 $sql .= " FROM ".$this->db->prefix()."societe_remise_except as rc, ".$this->db->prefix()."facture as f";
803 $sql .= " WHERE rc.fk_facture_source=f.rowid AND rc.fk_facture = ".((int) $invoice->id);
804 $sql .= " AND f.type IN (".$this->db->sanitize($invoice::TYPE_STANDARD.", ".$invoice::TYPE_CREDIT_NOTE.", ".$invoice::TYPE_SITUATION).")"; // Find discount coming from credit note or excess received
805 } elseif ($invoice->element == 'invoice_supplier') {
806 $sql = "SELECT sum(rc.amount_ttc) as amount, sum(rc.multicurrency_amount_ttc) as multicurrency_amount";
807 $sql .= " FROM ".$this->db->prefix()."societe_remise_except as rc, ".$this->db->prefix()."facture_fourn as f";
808 $sql .= " WHERE rc.fk_invoice_supplier_source=f.rowid AND rc.fk_invoice_supplier = ".((int) $invoice->id);
809 $sql .= " AND f.type IN (".$this->db->sanitize($invoice::TYPE_STANDARD.", ".$invoice::TYPE_CREDIT_NOTE).")"; // Find discount coming from credit note or excess paid
810 } else {
811 $this->error = get_class($this)."::getSumCreditNotesUsed was called with a bad object as a first parameter";
812 dol_print_error($this->db, $this->error);
813 return 'ErrorBadElementType';
814 }
815
816 $resql = $this->db->query($sql);
817 if ($resql) {
818 $obj = $this->db->fetch_object($resql);
819 if ($multicurrency == 1) {
820 return $obj->multicurrency_amount;
821 } else {
822 return $obj->amount;
823 }
824 } else {
825 $this->error = $this->db->lasterror();
826 return 'ErrorBadSQLquery';
827 }
828 }
836 public function getSumFromThisCreditNotesNotUsed($invoice, $multicurrency = 0)
837 {
838 dol_syslog(get_class($this)."::getSumCreditNotesUsed", LOG_DEBUG);
839
840 if ($invoice->element == 'facture' || $invoice->element == 'invoice') {
841 $sql = "SELECT sum(rc.amount_ttc) as amount, sum(rc.multicurrency_amount_ttc) as multicurrency_amount";
842 $sql .= " FROM ".$this->db->prefix()."societe_remise_except as rc";
843 $sql .= " WHERE rc.fk_facture IS NULL AND rc.fk_facture_source = ".((int) $invoice->id);
844 } elseif ($invoice->element == 'invoice_supplier') {
845 $sql = "SELECT sum(rc.amount_ttc) as amount, sum(rc.multicurrency_amount_ttc) as multicurrency_amount";
846 $sql .= " FROM ".$this->db->prefix()."societe_remise_except as rc";
847 $sql .= " WHERE rc.fk_invoice_supplier IS NULL AND rc.fk_invoice_supplier_source = ".((int) $invoice->id);
848 } else {
849 $this->error = get_class($this)."::getSumCreditNotesUsed was called with a bad object as a first parameter";
850 dol_print_error($this->db, $this->error);
851 return -1;
852 }
853
854 $resql = $this->db->query($sql);
855 if ($resql) {
856 $obj = $this->db->fetch_object($resql);
857 if ($multicurrency) {
858 return $obj->multicurrency_amount;
859 } else {
860 return $obj->amount;
861 }
862 } else {
863 $this->error = $this->db->lasterror();
864 return -1;
865 }
866 }
867
875 public function getNomUrl($withpicto = 0, $option = 'invoice')
876 {
877 global $langs;
878
879 $result = '';
880 $link = '';
881 $linkend = '';
882 $label = '';
883 $picto = '';
884 $ref = '';
885
886 if ($option == 'invoice') {
887 $facid = !empty($this->discount_type) ? $this->fk_invoice_supplier_source : $this->fk_facture_source;
888 $link = !empty($this->discount_type) ? '/fourn/facture/card.php' : '/compta/facture/card.php';
889 $label = $langs->trans("ShowSourceInvoice").': '.$this->ref_facture_source;
890 $link = '<a href="'.DOL_URL_ROOT.$link.'?facid='.$facid.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
891 $linkend = '</a>';
892 $ref = !empty($this->discount_type) ? $this->ref_invoice_supplier_source : $this->ref_facture_source;
893 $picto = 'bill';
894 }
895 if ($option == 'discount') {
896 $label = $langs->trans("Discount");
897 $link = '<a href="'.DOL_URL_ROOT.'/comm/remx.php?id='.$this->socid.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
898 $linkend = '</a>';
899 $ref = $langs->trans("Discount");
900 $picto = 'generic';
901 }
902
903
904 if ($withpicto) {
905 $result .= ($link.img_object($label, $picto, 'class="classfortooltip"').$linkend);
906 }
907 if ($withpicto && $withpicto != 2) {
908 $result .= ' ';
909 }
910 $result .= $link.$ref.$linkend;
911 return $result;
912 }
913
914
922 public function initAsSpecimen()
923 {
924 $this->socid = 1;
925 $this->amount_ht = 10;
926 $this->amount_tva = 1.96;
927 $this->amount_ttc = 11.96;
928 $this->tva_tx = 19.6;
929 $this->description = 'Specimen discount';
930
931 return 1;
932 }
933
946 public function generateFromAmount($amount, $amount_type, $tva_tx, $localtax1_tx, $localtax2_tx, $localtax1_type = 1, $localtax2_type = 1)
947 {
948 $localtax1_tx_pct = (float) ($localtax1_tx / 100);
949 $localtax2_tx_pct = (float) ($localtax2_tx / 100);
950 $tva_tx_pct = (float) ($tva_tx / 100);
951 $err = 0;
952
953 // Test if localtax type is 2, 4 or 6 (compond tax calculation)
954 $localtax1_type2 = ((int) $localtax1_type > 0 && (int) $localtax1_type % 2 == 0 ? 1 : 0);
955 $localtax2_type2 = ((int) $localtax2_type > 0 && (int) $localtax2_type % 2 == 0 ? 1 : 0);
956
957 if ($amount_type == 1) {
958 // TTC
959
960 $this->amount_ttc = price2num($amount, 'MT');
961
962 if (!$localtax1_type2 && !$localtax2_type2) {
963 $diviseur = 1 + $tva_tx_pct + $localtax1_tx_pct + $localtax2_tx_pct;
964 $this->amount_ht = price2num((float) $amount / $diviseur, 'MT');
965 $this->amount_tva = price2num((float) $this->amount_ht * $tva_tx_pct, 'MT');
966 } else {
967 $diviseur = 1 + $tva_tx_pct * ($localtax1_tx_pct > 0 ? $localtax1_tx_pct : 1) * ($localtax2_tx_pct > 0 ? $localtax2_tx_pct : 1);
968 $lt1 = 0; $lt2 = 0; $ttc = (float) $this->amount_ttc;
969 if ($localtax2_tx_pct > 0) {
970 $lt2 = $ttc - $ttc / (1 + $localtax2_tx_pct);
971 }
972 if ($localtax1_tx_pct > 0) {
973 $lt1 = $ttc - $lt2 - ($ttc - $lt2)/ (1 + $localtax1_tx_pct);
974 }
975 $tva = ($ttc - $lt2 - $lt1) - ($ttc - $lt2 - $lt1) / ( 1 + $tva_tx_pct);
976 $this->amount_tva = price2num($tva, 'MT');
977 $this->total_localtax1 = $lt1;
978 $this->total_localtax2 = $lt2;
979 $this->amount_ht = price2num((float) $this->amount_ttc- $this->total_localtax1 - $this->total_localtax2 - (float) $this->amount_tva, 'MT');
980 }
981
982 $this->multicurrency_amount_ttc = price2num($amount * (float) $this->multicurrency_tx, 'MT');
983 $this->multicurrency_amount_ht = $this->amount_ht;
984 $this->multicurrency_amount_tva = price2num((float) $this->amount_ht * $tva_tx_pct * (float) $this->multicurrency_tx, 'MT');
985 } elseif ($amount_type == 0) {
986 // HT
987 $this->amount_ht = price2num($amount, 'MT');
988 $this->amount_tva = price2num((float) $this->amount_ht * $tva_tx_pct, 'MT');
989
990 $this->multicurrency_amount_ht = price2num($amount * (float) $this->multicurrency_tx, 'MT');
991 $this->multicurrency_amount_tva = price2num(((float) $amount * $tva_tx_pct) * (float) $this->multicurrency_tx, 'MT');
992 }
993
994 $this->tva_tx = $tva_tx;
995 $this->localtax1_tx = $localtax1_tx;
996 $this->localtax1_type = $localtax1_type;
997 $this->localtax2_tx = $localtax2_tx;
998 $this->localtax2_type = $localtax2_type;
999
1000 if ($localtax1_type2 == 0) {
1001 $this->total_localtax1 = (float) $this->amount_ht * $localtax1_tx_pct;
1002 } elseif ($localtax1_type2 == 1) {
1003 $this->total_localtax1 = ((float) $this->amount_ht + (float) $this->amount_tva) * $localtax1_tx_pct;
1004 }
1005
1006 if ($localtax2_type2 == 0) {
1007 $this->total_localtax2 = (float) $this->amount_ht * $localtax2_tx_pct;
1008 } elseif ($localtax2_type2 == 1) {
1009 $this->total_localtax2 = ((float) $this->amount_ht + (float) $this->amount_tva + $this->total_localtax1) * $localtax2_tx_pct;
1010 }
1011
1012 if (empty($this->amount_ttc)) {
1013 // Dans le cas où le montant ttc vient d'une remise scindée on le prend tel quel
1014 // car de recalculer le montant ttc à partir du montant ht créé des erreurs de précision.
1015 $this->amount_ttc = price2num((float) $this->amount_ht + (float) $this->amount_tva + $this->total_localtax1 + $this->total_localtax2, 'MT');
1016 $this->multicurrency_amount_ttc = price2num(((float) $this->amount_ht + (float) $this->amount_tva + $this->total_localtax1 + $this->total_localtax2) * (float) $this->multicurrency_tx, 'MT');
1017 }
1018
1019 return $err;
1020 }
1021
1029 public function splitAmount($amount_ttc1, $amount_ttc2)
1030 {
1031 // Moved from remx.php :: domain-related operations within action confirm_split
1032 $newdiscount1 = new DiscountAbsolute($this->db);
1033 $newdiscount2 = new DiscountAbsolute($this->db);
1034 $newdiscount1->fk_facture_source = $this->fk_facture_source;
1035 $newdiscount2->fk_facture_source = $this->fk_facture_source;
1036 $newdiscount1->fk_facture = $this->fk_facture;
1037 $newdiscount2->fk_facture = $this->fk_facture;
1038 $newdiscount1->fk_facture_line = $this->fk_facture_line;
1039 $newdiscount2->fk_facture_line = $this->fk_facture_line;
1040 $newdiscount1->fk_invoice_supplier_source = $this->fk_invoice_supplier_source;
1041 $newdiscount2->fk_invoice_supplier_source = $this->fk_invoice_supplier_source;
1042 $newdiscount1->fk_invoice_supplier = $this->fk_invoice_supplier;
1043 $newdiscount2->fk_invoice_supplier = $this->fk_invoice_supplier;
1044 $newdiscount1->fk_invoice_supplier_line = $this->fk_invoice_supplier_line;
1045 $newdiscount2->fk_invoice_supplier_line = $this->fk_invoice_supplier_line;
1046 if ($this->description == '(CREDIT_NOTE)' || $this->description == '(DEPOSIT)') {
1047 $newdiscount1->description = $this->description;
1048 $newdiscount2->description = $this->description;
1049 } else {
1050 $newdiscount1->description = $this->description.' (1)';
1051 $newdiscount2->description = $this->description.' (2)';
1052 }
1053 $newdiscount1->fk_user = $this->fk_user;
1054 $newdiscount2->fk_user = $this->fk_user;
1055 $newdiscount1->fk_soc = $this->fk_soc;
1056 $newdiscount1->socid = $this->socid;
1057 $newdiscount2->fk_soc = $this->fk_soc;
1058 $newdiscount2->socid = $this->socid;
1059 $newdiscount1->discount_type = $this->discount_type;
1060 $newdiscount2->discount_type = $this->discount_type;
1061 $newdiscount1->datec = $this->datec;
1062 $newdiscount2->datec = $this->datec;
1063 $newdiscount1->tva_tx = $this->tva_tx;
1064 $newdiscount2->tva_tx = $this->tva_tx;
1065 $newdiscount1->localtax1_tx = $this->localtax1_tx;
1066 $newdiscount2->localtax1_tx = $this->localtax1_tx;
1067 $newdiscount1->localtax2_tx = $this->localtax2_tx;
1068 $newdiscount2->localtax2_tx = $this->localtax2_tx;
1069 $newdiscount1->vat_src_code = $this->vat_src_code;
1070 $newdiscount2->vat_src_code = $this->vat_src_code;
1071
1072 $newdiscount1->generateFromAmount($amount_ttc1, 1, $newdiscount1->tva_tx, $newdiscount1->localtax1_tx, $newdiscount1->localtax2_tx, $this->localtax1_type, $this->localtax2_type);
1073 $newdiscount2->generateFromAmount($amount_ttc2, 1, $newdiscount1->tva_tx, $newdiscount1->localtax1_tx, $newdiscount1->localtax2_tx, $this->localtax1_type, $this->localtax2_type);
1074
1075 return array($newdiscount1, $newdiscount2);
1076 }
1077}
const TYPE_CREDIT_NOTE
Credit note invoice.
const TYPE_DEPOSIT
Deposit invoice.
Parent class of all other business classes (invoices, contracts, proposals, orders,...
Class to manage absolute discounts.
generateFromAmount($amount, $amount_type, $tva_tx, $localtax1_tx, $localtax2_tx, $localtax1_type=1, $localtax2_type=1)
Helper method to create a discount from an amount.
getAvailableDiscounts($company=null, $user=null, $filter='', $maxvalue=0, $discount_type=0, $multicurrency=0)
Return amount (with tax) of discounts currently available for a company, user or other criteria.
getSumFromThisCreditNotesNotUsed($invoice, $multicurrency=0)
initAsSpecimen()
Initialise an instance with random values.
splitAmount($amount_ttc1, $amount_ttc2)
Helper method to create two new discounts from split amount.
fetch($rowid, $fk_facture_source=0, $fk_invoice_supplier_source=0)
Load object from database into memory.
unlink_invoice($notrigger=0)
Link the discount to a particular invoice line or a particular invoice.
getNomUrl($withpicto=0, $option='invoice')
Return clickable ref of object (with picto or not)
create($user)
Create a discount into database.
getSumDepositsUsed($invoice, $multicurrency=0)
Return amount (with tax) of all deposits invoices used by invoice as a payment.
getSumCreditNotesUsed($invoice, $multicurrency=0)
Return amount (with tax) of all credit notes invoices + excess received used by invoice as a payment.
link_to_invoice($rowidline, $rowidinvoice, $notrigger=0)
Link the discount to a particular invoice line or a particular invoice.
__construct($db)
Constructor.
Class to manage invoices.
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 '.
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.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Returns text escaped for inclusion in HTML alt or title or value tags, or into values of HTML input f...