dolibarr 25.0.0-alpha
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-2026 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 (".((int) $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 .= " ".($this->localtax1_tx ? price2num($this->localtax1_tx) : 0).", ".($this->localtax1_type ? price2num($this->localtax1_type) : 0).", ";
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
405 // Call trigger
406 $result = $this->call_trigger('DISCOUNTABSOLUTE_CREATE', $user);
407 if ($result < 0) {
408 return -1;
409 }
410 // End call triggers
411
412 return $this->id;
413 } else {
414 $this->error = $this->db->lasterror().' - sql='.$sql;
415 return -1;
416 }
417 }
418
419
426 public function delete($user)
427 {
428 // Check if we can remove the discount
429 if ($this->fk_facture_source) {
430 $sql = "SELECT COUNT(rowid) as nb";
431 $sql .= " FROM ".$this->db->prefix()."societe_remise_except";
432 $sql .= " WHERE (fk_facture_line IS NOT NULL"; // Not used as absolute simple discount
433 $sql .= " OR fk_facture IS NOT NULL)"; // Not used as credit note and not used as deposit
434 $sql .= " AND fk_facture_source = ".((int) $this->fk_facture_source);
435 //$sql.=" AND rowid != ".((int) $this->id);
436
437 dol_syslog(get_class($this)."::delete Check if we can remove discount", LOG_DEBUG);
438 $resql = $this->db->query($sql);
439 if ($resql) {
440 $obj = $this->db->fetch_object($resql);
441 if ($obj->nb > 0) {
442 $this->error = 'ErrorThisPartOrAnotherIsAlreadyUsedSoDiscountSerieCantBeRemoved';
443 return -2;
444 }
445 } else {
446 dol_print_error($this->db);
447 return -1;
448 }
449 }
450
451 // Check if we can remove the discount
452 if ($this->fk_invoice_supplier_source) {
453 $sql = "SELECT COUNT(rowid) as nb";
454 $sql .= " FROM ".$this->db->prefix()."societe_remise_except";
455 $sql .= " WHERE (fk_invoice_supplier_line IS NOT NULL"; // Not used as absolute simple discount
456 $sql .= " OR fk_invoice_supplier IS NOT NULL)"; // Not used as credit note and not used as deposit
457 $sql .= " AND fk_invoice_supplier_source = ".((int) $this->fk_invoice_supplier_source);
458 //$sql.=" AND rowid != ".((int) $this->id);
459
460 dol_syslog(get_class($this)."::delete Check if we can remove discount", LOG_DEBUG);
461 $resql = $this->db->query($sql);
462 if ($resql) {
463 $obj = $this->db->fetch_object($resql);
464 if ($obj->nb > 0) {
465 $this->error = 'ErrorThisPartOrAnotherIsAlreadyUsedSoDiscountSerieCantBeRemoved';
466 return -2;
467 }
468 } else {
469 dol_print_error($this->db);
470 return -1;
471 }
472 }
473
474 $this->db->begin();
475
476 // Delete but only if not used
477 $sql = "DELETE FROM ".$this->db->prefix()."societe_remise_except ";
478 if ($this->fk_facture_source) {
479 $sql .= " WHERE fk_facture_source = ".((int) $this->fk_facture_source); // Delete all lines of same series
480 } elseif ($this->fk_invoice_supplier_source) {
481 $sql .= " WHERE fk_invoice_supplier_source = ".((int) $this->fk_invoice_supplier_source); // Delete all lines of same series
482 } else {
483 $sql .= " WHERE rowid = ".((int) $this->id); // Delete only line
484 }
485 $sql .= " AND (fk_facture_line IS NULL"; // Not used as absolute simple discount
486 $sql .= " AND fk_facture IS NULL)"; // Not used as credit note and not used as deposit
487 $sql .= " AND (fk_invoice_supplier_line IS NULL"; // Not used as absolute simple discount
488 $sql .= " AND fk_invoice_supplier IS NULL)"; // Not used as credit note and not used as deposit
489
490 dol_syslog(get_class($this)."::delete Delete discount", LOG_DEBUG);
491
492 require_once DOL_DOCUMENT_ROOT. '/core/class/commoninvoice.class.php';
493 $result = $this->db->query($sql);
494 if ($result) {
495 // If source of discount was a credit note or deposit, we change source statut.
496 if ($this->fk_facture_source) {
497 $sql = "UPDATE ".$this->db->prefix()."facture";
498 $sql .= " set paye=0, fk_statut=1";
499 $sql .= " WHERE type IN (".$this->db->sanitize(CommonInvoice::TYPE_CREDIT_NOTE.", ".CommonInvoice::TYPE_DEPOSIT).") AND rowid = ".((int) $this->fk_facture_source);
500
501 dol_syslog(get_class($this)."::delete Update credit note or deposit invoice statut", LOG_DEBUG);
502 $result = $this->db->query($sql);
503 if ($result) {
504 $this->db->commit();
505 return 1;
506 } else {
507 $this->error = $this->db->lasterror();
508 $this->db->rollback();
509 return -1;
510 }
511 } elseif ($this->fk_invoice_supplier_source) {
512 $sql = "UPDATE ".$this->db->prefix()."facture_fourn";
513 $sql .= " set paye=0, fk_statut=1";
514 $sql .= " WHERE type IN (".$this->db->sanitize(CommonInvoice::TYPE_CREDIT_NOTE.", ".CommonInvoice::TYPE_DEPOSIT).") AND rowid = ".((int) $this->fk_invoice_supplier_source);
515
516 dol_syslog(get_class($this)."::delete Update credit note or deposit invoice statut", LOG_DEBUG);
517 $result = $this->db->query($sql);
518 if ($result) {
519 $this->db->commit();
520 return 1;
521 } else {
522 $this->error = $this->db->lasterror();
523 $this->db->rollback();
524 return -1;
525 }
526 } else {
527 $this->db->commit();
528 return 1;
529 }
530 } else {
531 $this->error = $this->db->lasterror();
532 $this->db->rollback();
533 return -1;
534 }
535 }
536
537
538
539 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
550 public function link_to_invoice($rowidline, $rowidinvoice, $notrigger = 0)
551 {
552 // phpcs:enable
553 global $user;
554
555 // Check parameters
556 if (!$rowidline && !$rowidinvoice) {
557 $this->error = 'ErrorBadParameters';
558 return -1;
559 }
560 if ($rowidline && $rowidinvoice) {
561 $this->error = 'ErrorBadParameters';
562 return -2;
563 }
564
565 $sql = "UPDATE ".$this->db->prefix()."societe_remise_except";
566 if (!empty($this->discount_type)) {
567 if ($rowidline) {
568 $sql .= " SET fk_invoice_supplier_line = ".((int) $rowidline);
569 }
570 if ($rowidinvoice) {
571 $sql .= " SET fk_invoice_supplier = ".((int) $rowidinvoice);
572 }
573 } else {
574 if ($rowidline) {
575 $sql .= " SET fk_facture_line = ".((int) $rowidline);
576 }
577 if ($rowidinvoice) {
578 $sql .= " SET fk_facture = ".((int) $rowidinvoice);
579 }
580 }
581 $sql .= " WHERE rowid = ".((int) $this->id);
582
583 dol_syslog(get_class($this)."::link_to_invoice", LOG_DEBUG);
584 $resql = $this->db->query($sql);
585 if ($resql) {
586 if (!empty($this->discount_type)) {
587 $this->fk_invoice_supplier_line = $rowidline;
588 $this->fk_invoice_supplier = $rowidinvoice;
589 } else {
590 $this->fk_facture_line = $rowidline;
591 $this->fk_facture = $rowidinvoice;
592 }
593
594 // If a discount comes from a source invoice (deposit/credit note/excess) and is applied to another invoice,
595 // also create a link between the source invoice and the target invoice.
596 if ($rowidinvoice) {
597 $sourcetype = '';
598 $targettype = '';
599 $sourceinvoiceid = 0;
600
601 if (!empty($this->discount_type)) {
602 $sourcetype = 'invoice_supplier';
603 $targettype = 'invoice_supplier';
604 $sourceinvoiceid = (int) $this->fk_invoice_supplier_source;
605 } else {
606 $sourcetype = 'facture';
607 $targettype = 'facture';
608 $sourceinvoiceid = (int) $this->fk_facture_source;
609 }
610
611 if ($sourceinvoiceid > 0 && $sourceinvoiceid !== (int) $rowidinvoice) {
612 $sqlcheck = "SELECT 1";
613 $sqlcheck .= " FROM ".$this->db->prefix()."element_element";
614 $sqlcheck .= " WHERE fk_source = ".((int) $sourceinvoiceid);
615 $sqlcheck .= " AND sourcetype = '".$this->db->escape($sourcetype)."'";
616 $sqlcheck .= " AND fk_target = ".((int) $rowidinvoice);
617 $sqlcheck .= " AND targettype = '".$this->db->escape($targettype)."'";
618 $sqlcheck .= $this->db->plimit(1);
619
620 $rescheck = $this->db->query($sqlcheck);
621 if ($rescheck && $this->db->num_rows($rescheck) === 0) {
622 $sqladd = "INSERT INTO ".$this->db->prefix()."element_element (fk_source, sourcetype, fk_target, targettype)";
623 $sqladd .= " VALUES (".((int) $sourceinvoiceid).", '".$this->db->escape($sourcetype)."', ".((int) $rowidinvoice).", '".$this->db->escape($targettype)."')";
624 $this->db->query($sqladd); // Best-effort: do not fail discount link if object link can't be created.
625 }
626 }
627 }
628
629 if (!$notrigger) {
630 // Call trigger
631 $result = $this->call_trigger('DISCOUNT_MODIFY', $user);
632 if ($result < 0) {
633 return -2;
634 }
635 // End call triggers
636 }
637 return 1;
638 } else {
639 $this->error = $this->db->error();
640 return -3;
641 }
642 }
643
644
645 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
653 public function unlink_invoice($notrigger = 0)
654 {
655 // phpcs:enable
656 global $user;
657
658 $sql = "UPDATE ".$this->db->prefix()."societe_remise_except";
659 if (!empty($this->discount_type)) {
660 $sql .= " SET fk_invoice_supplier_line = NULL, fk_invoice_supplier = NULL";
661 } else {
662 $sql .= " SET fk_facture_line = NULL, fk_facture = NULL";
663 }
664 $sql .= " WHERE rowid = ".((int) $this->id);
665
666 dol_syslog(get_class($this)."::unlink_invoice", LOG_DEBUG);
667 $resql = $this->db->query($sql);
668 if ($resql) {
669 if (!$notrigger) {
670 // Call trigger
671 $result = $this->call_trigger('DISCOUNT_MODIFY', $user);
672 if ($result < 0) {
673 return -2;
674 }
675 // End call triggers
676 }
677 return 1;
678 } else {
679 $this->error = $this->db->error();
680 return -3;
681 }
682 }
683
684
696 public function getAvailableDiscounts($company = null, $user = null, $filter = '', $maxvalue = 0, $discount_type = 0, $multicurrency = 0)
697 {
698 global $conf, $hookmanager;
699
700 dol_syslog(get_class($this)."::getAvailableDiscounts discount_type=".$discount_type, LOG_DEBUG);
701
702 $parameters = array(
703 'company' => $company,
704 'user' => $user,
705 'filter' => $filter,
706 'maxvalue' => $maxvalue,
707 'discount_type' => $discount_type,
708 'multicurrency' => $multicurrency
709 );
710
711 $reshook = $hookmanager->executeHooks('getAvailableDiscounts', $parameters);
712 if (empty($reshook)) {
713 $sql = "SELECT SUM(rc.amount_ttc) as amount, SUM(rc.multicurrency_amount_ttc) as multicurrency_amount";
714 $sql .= " FROM ".$this->db->prefix()."societe_remise_except as rc";
715 $sql .= " WHERE rc.entity = ".((int) $conf->entity);
716 $sql .= " AND rc.discount_type=".((int) $discount_type);
717 if (!empty($discount_type)) {
718 $sql .= " AND (rc.fk_invoice_supplier IS NULL AND rc.fk_invoice_supplier_line IS NULL)"; // Available from supplier
719 } else {
720 $sql .= " AND (rc.fk_facture IS NULL AND rc.fk_facture_line IS NULL)"; // Available to customer
721 }
722 if (is_object($company)) {
723 $sql .= " AND rc.fk_soc = ".((int) $company->id);
724 }
725 if (is_object($user)) {
726 $sql .= " AND rc.fk_user = ".((int) $user->id);
727 }
728 if ($filter) {
729 $sql .= " AND (".$filter.")"; // @phan-suppress-current-line SqlInjection
730 }
731 if ($maxvalue) {
732 $sql .= ' AND rc.amount_ttc <= '.((float) price2num($maxvalue));
733 }
734 } else {
735 $sql = $hookmanager->resArray['sql'];
736 }
737
738 $resql = $this->db->query($sql);
739 if ($resql) {
740 $obj = $this->db->fetch_object($resql);
741 //while ($obj)
742 //{
743 //print 'zz'.$obj->amount;
744 //$obj = $this->db->fetch_object($resql);
745 //}
746 if ($multicurrency) {
747 return $obj->multicurrency_amount;
748 }
749
750 return $obj->amount;
751 }
752 return -1;
753 }
754
763 public function getSumDepositsUsed($invoice, $multicurrency = 0)
764 {
765 dol_syslog(get_class($this)."::getSumDepositsUsed", LOG_DEBUG);
766
767 if ($invoice->element == 'facture' || $invoice->element == 'invoice') {
768 $sql = "SELECT sum(rc.amount_ttc) as amount, sum(rc.multicurrency_amount_ttc) as multicurrency_amount";
769 $sql .= " FROM ".$this->db->prefix()."societe_remise_except as rc, ".$this->db->prefix()."facture as f";
770 $sql .= " WHERE rc.fk_facture_source=f.rowid AND rc.fk_facture = ".((int) $invoice->id);
771 $sql .= " AND f.type = ". (int) $invoice::TYPE_DEPOSIT;
772 } elseif ($invoice->element == 'invoice_supplier') {
773 $sql = "SELECT sum(rc.amount_ttc) as amount, sum(rc.multicurrency_amount_ttc) as multicurrency_amount";
774 $sql .= " FROM ".$this->db->prefix()."societe_remise_except as rc, ".$this->db->prefix()."facture_fourn as f";
775 $sql .= " WHERE rc.fk_invoice_supplier_source=f.rowid AND rc.fk_invoice_supplier = ".((int) $invoice->id);
776 $sql .= " AND f.type = ". (int) $invoice::TYPE_DEPOSIT;
777 } else {
778 $this->error = get_class($this)."::getSumDepositsUsed was called with a bad object as a first parameter";
779 dol_print_error($this->db, $this->error);
780 return -1;
781 }
782
783 $resql = $this->db->query($sql);
784 if ($resql) {
785 $obj = $this->db->fetch_object($resql);
786 if ($multicurrency == 1) {
787 return $obj->multicurrency_amount;
788 } else {
789 return $obj->amount;
790 }
791 } else {
792 $this->error = $this->db->lasterror();
793 return -1;
794 }
795 }
796
804 public function getSumCreditNotesUsed($invoice, $multicurrency = 0)
805 {
806 dol_syslog(get_class($this)."::getSumCreditNotesUsed", LOG_DEBUG);
807
808 if ($invoice->element == 'facture' || $invoice->element == 'invoice') {
809 $sql = "SELECT sum(rc.amount_ttc) as amount, sum(rc.multicurrency_amount_ttc) as multicurrency_amount";
810 $sql .= " FROM ".$this->db->prefix()."societe_remise_except as rc, ".$this->db->prefix()."facture as f";
811 $sql .= " WHERE rc.fk_facture_source=f.rowid AND rc.fk_facture = ".((int) $invoice->id);
812 $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
813 } elseif ($invoice->element == 'invoice_supplier') {
814 $sql = "SELECT sum(rc.amount_ttc) as amount, sum(rc.multicurrency_amount_ttc) as multicurrency_amount";
815 $sql .= " FROM ".$this->db->prefix()."societe_remise_except as rc, ".$this->db->prefix()."facture_fourn as f";
816 $sql .= " WHERE rc.fk_invoice_supplier_source=f.rowid AND rc.fk_invoice_supplier = ".((int) $invoice->id);
817 $sql .= " AND f.type IN (".$this->db->sanitize($invoice::TYPE_STANDARD.", ".$invoice::TYPE_CREDIT_NOTE).")"; // Find discount coming from credit note or excess paid
818 } else {
819 $this->error = get_class($this)."::getSumCreditNotesUsed was called with a bad object as a first parameter";
820 dol_print_error($this->db, $this->error);
821 return 'ErrorBadElementType';
822 }
823
824 $resql = $this->db->query($sql);
825 if ($resql) {
826 $obj = $this->db->fetch_object($resql);
827 if ($multicurrency == 1) {
828 return $obj->multicurrency_amount;
829 } else {
830 return $obj->amount;
831 }
832 } else {
833 $this->error = $this->db->lasterror();
834 return 'ErrorBadSQLquery';
835 }
836 }
844 public function getSumFromThisCreditNotesNotUsed($invoice, $multicurrency = 0)
845 {
846 dol_syslog(get_class($this)."::getSumCreditNotesUsed", LOG_DEBUG);
847
848 if ($invoice->element == 'facture' || $invoice->element == 'invoice') {
849 $sql = "SELECT sum(rc.amount_ttc) as amount, sum(rc.multicurrency_amount_ttc) as multicurrency_amount";
850 $sql .= " FROM ".$this->db->prefix()."societe_remise_except as rc";
851 $sql .= " WHERE rc.fk_facture IS NULL AND rc.fk_facture_source = ".((int) $invoice->id);
852 } elseif ($invoice->element == 'invoice_supplier') {
853 $sql = "SELECT sum(rc.amount_ttc) as amount, sum(rc.multicurrency_amount_ttc) as multicurrency_amount";
854 $sql .= " FROM ".$this->db->prefix()."societe_remise_except as rc";
855 $sql .= " WHERE rc.fk_invoice_supplier IS NULL AND rc.fk_invoice_supplier_source = ".((int) $invoice->id);
856 } else {
857 $this->error = get_class($this)."::getSumCreditNotesUsed was called with a bad object as a first parameter";
858 dol_print_error($this->db, $this->error);
859 return -1;
860 }
861
862 $resql = $this->db->query($sql);
863 if ($resql) {
864 $obj = $this->db->fetch_object($resql);
865 if ($multicurrency) {
866 return $obj->multicurrency_amount;
867 } else {
868 return $obj->amount;
869 }
870 } else {
871 $this->error = $this->db->lasterror();
872 return -1;
873 }
874 }
875
883 public function getNomUrl($withpicto = 0, $option = 'invoice')
884 {
885 global $langs;
886
887 $result = '';
888 $link = '';
889 $linkend = '';
890 $label = '';
891 $picto = '';
892 $ref = '';
893
894 if ($option == 'invoice') {
895 $facid = !empty($this->discount_type) ? $this->fk_invoice_supplier_source : $this->fk_facture_source;
896 $link = !empty($this->discount_type) ? '/fourn/facture/card.php' : '/compta/facture/card.php';
897 $label = $langs->trans("ShowSourceInvoice").': '.$this->ref_facture_source;
898 $link = '<a href="'.DOL_URL_ROOT.$link.'?facid='.$facid.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
899 $linkend = '</a>';
900 $ref = !empty($this->discount_type) ? $this->ref_invoice_supplier_source : $this->ref_facture_source;
901 $picto = 'bill';
902 }
903 if ($option == 'discount') {
904 $label = $langs->trans("Discount");
905 $link = '<a href="'.DOL_URL_ROOT.'/comm/remx.php?id='.$this->socid.'" title="'.dol_escape_htmltag($label, 1).'" class="classfortooltip">';
906 $linkend = '</a>';
907 $ref = $langs->trans("Discount");
908 $picto = 'generic';
909 }
910
911
912 if ($withpicto) {
913 $result .= ($link.img_object($label, $picto, 'class="classfortooltip"').$linkend);
914 }
915 if ($withpicto && $withpicto != 2) {
916 $result .= ' ';
917 }
918 $result .= $link.$ref.$linkend;
919 return $result;
920 }
921
922
930 public function initAsSpecimen()
931 {
932 $this->socid = 1;
933 $this->amount_ht = 10;
934 $this->amount_tva = 1.96;
935 $this->amount_ttc = 11.96;
936 $this->tva_tx = 19.6;
937 $this->description = 'Specimen discount';
938
939 return 1;
940 }
941
955 public function generateFromAmount($amount, $amount_type, $tva_tx, $localtax1_tx, $localtax2_tx, $localtax1_type = 1, $localtax2_type = 1)
956 {
957 $tva_tx_pct = (float) ($tva_tx / 100);
958 $localtax1_tx_pct = (float) ($localtax1_tx / 100);
959 $localtax2_tx_pct = (float) ($localtax2_tx / 100);
960
961 $err = 0;
962
963 //var_dump("amount=$amount, amount_type=$amount_type, tva_tx=$tva_tx, localtax1_tx=$localtax1_tx, localtax2_tx=$localtax2_tx, localtax1_type=$localtax1_type, localtax2_type=$localtax2_type");
964
965 // Test if localtax type is 2, 4 or 6 (loca tax are calculated on ht + vat)
966 $localtax1_type2 = ((int) $localtax1_type > 0 && (int) $localtax1_type % 2 == 0 ? 1 : 0);
967 $localtax2_type2 = ((int) $localtax2_type > 0 && (int) $localtax2_type % 2 == 0 ? 1 : 0);
968
969 if ($amount_type == 1) {
970 // TTC
971 $this->total_ttc = (float) price2num($amount, 'MT');
972
973 $ttc = (float) $this->total_ttc;
974
975 // Calculate lt1 and/or lt2 = taxes added after VAT
976 $lt1 = 0;
977 $lt2 = 0;
978 $tx_before_vat = 0;
979 $tx_before_vat_without1 = 0;
980 $tx_before_vat_without2 = 0;
981 $tx_after_vat = 0;
982 $tx_after_vat_without1 = 0;
983 $tx_after_vat_without2 = 0;
984 if ($localtax1_type2 && $localtax1_tx_pct > 0) {
985 $tx_after_vat += $localtax1_tx_pct;
986 $tx_after_vat_without2 += $localtax1_tx_pct;
987 } else {
988 $tx_before_vat += $localtax1_tx_pct;
989 $tx_before_vat_without2 += $localtax1_tx_pct;
990 }
991 if ($localtax2_type2 && $localtax2_tx_pct > 0) {
992 $tx_after_vat += $localtax2_tx_pct;
993 $tx_after_vat_without1 += $localtax2_tx_pct;
994 } else {
995 $tx_before_vat += $localtax2_tx_pct;
996 $tx_before_vat_without1 += $localtax2_tx_pct;
997 }
998 $tx_before_vat += $tva_tx_pct;
999 $tx_before_vat_without1 += $tva_tx_pct;
1000 $tx_before_vat_without2 += $tva_tx_pct;
1001 if ($localtax1_type2 && $localtax1_tx_pct > 0) {
1002 $lt1 = (float) price2num($ttc - $ttc / (1 + $tx_after_vat) * (1 + $tx_after_vat_without1), 'MT');
1003 }
1004 if ($localtax2_type2 && $localtax2_tx_pct > 0) {
1005 $lt2 = (float) price2num($ttc - $ttc / (1 + $tx_after_vat) * (1 + $tx_after_vat_without2), 'MT');
1006 }
1007
1008 // lt1 and lt2 if calculated including taxes
1009 //var_dump("if lt1 or lt2 are calculated including vat: lt1=$lt1 lt2=$lt2");
1010
1011 $htplusbeforevat = $ttc - $lt1 - $lt2; // $htplusbeforevat = amount with HT + taxes added before VAT
1012
1013 if (!$localtax1_type2 && $localtax1_tx_pct > 0) {
1014 $lt1 = (float) price2num($htplusbeforevat - $htplusbeforevat / (1 + $tx_before_vat) * (1 + $tx_before_vat_without1), 'MT');
1015 }
1016 if (!$localtax2_type2 && $localtax2_tx_pct > 0) {
1017 $lt2 = (float) price2num($htplusbeforevat - $htplusbeforevat / (1 + $tx_before_vat) * (1 + $tx_before_vat_without2), 'MT');
1018 }
1019
1020 // lt1 and lt2 are now local taxes (whatever they are calculated before of after vat)
1021 //var_dump("lt1 or lt2 whatever is calculation mode: lt1=$lt1 lt2=$lt2");
1022
1023 $tva = ($ttc - $lt2 - $lt1) - ($ttc - $lt2 - $lt1) / (1 + $tva_tx_pct);
1024 $this->total_tva = (float) price2num($tva, 'MT');
1025 $this->total_localtax1 = $lt1;
1026 $this->total_localtax2 = $lt2;
1027 $this->total_ht = (float) price2num($this->total_ttc - $this->total_localtax1 - $this->total_localtax2 - $this->total_tva, 'MT');
1028
1029
1030 $this->multicurrency_total_ttc = (float) price2num($amount * (float) $this->multicurrency_tx, 'MT');
1031 $this->multicurrency_total_ht = (float) price2num((float) $amount / (1 + $tva_tx_pct) * (float) $this->multicurrency_tx, 'MT');
1032 $this->multicurrency_total_tva = (float) price2num($this->multicurrency_total_ttc - $this->multicurrency_total_ht, 'MT');
1033 } elseif ($amount_type == 0) {
1034 // HT
1035 $this->total_ht = (float) price2num($amount, 'MT');
1036 $this->total_tva = (float) price2num((float) $this->total_ht * $tva_tx_pct, 'MT');
1037 //$this->total_ttc = price2num((float) $this->total_ht + (float) $this->total_tva + ..., 'MT'); // Is set later
1038
1039 $this->multicurrency_total_ht = (float) price2num($amount * (float) $this->multicurrency_tx, 'MT');
1040 $this->multicurrency_total_tva = (float) price2num(((float) $amount * $tva_tx_pct) * (float) $this->multicurrency_tx, 'MT');
1041 //$this->multicurrency_total_ttc = price2num(((float) $this->total_ht + (float) $this->total_tva + ...) * (float) $this->multicurrency_tx, 'MT'); // Is set later
1042
1043 if ($localtax1_type2 == 0) {
1044 $this->total_localtax1 = (float) price2num((float) $this->total_ht * $localtax1_tx_pct, 'MT');
1045 } elseif ($localtax1_type2 == 1) {
1046 $this->total_localtax1 = (float) price2num(((float) $this->total_ht + (float) $this->total_tva) * $localtax1_tx_pct, 'MT');
1047 }
1048
1049 if ($localtax2_type2 == 0) {
1050 $this->total_localtax2 = (float) price2num((float) $this->total_ht * $localtax2_tx_pct, 'MT');
1051 } elseif ($localtax2_type2 == 1) {
1052 $this->total_localtax2 = (float) price2num(((float) $this->total_ht + (float) $this->total_tva) * $localtax2_tx_pct, 'MT');
1053 }
1054 }
1055
1056 // For backward compatibility
1057 $this->amount_ht = $this->total_ht;
1058 $this->amount_tva = $this->total_tva;
1059 $this->amount_ttc = $this->total_ttc;
1060 $this->multicurrency_amount_ht = $this->multicurrency_total_ht;
1061 $this->multicurrency_amount_tva = $this->multicurrency_total_tva;
1062 $this->multicurrency_amount_ttc = $this->multicurrency_total_ttc;
1063
1064 $this->tva_tx = $tva_tx;
1065 $this->localtax1_tx = $localtax1_tx;
1066 $this->localtax1_type = $localtax1_type;
1067 $this->localtax2_tx = $localtax2_tx;
1068 $this->localtax2_type = $localtax2_type;
1069
1070 //var_dump("localtax1 mode=$localtax1_type2 localtax2 mode=$localtax2_type2");
1071
1072
1073 if (empty($this->total_ttc)) {
1074 // If the total amount comes from a split discount, we take it as-is
1075 // because recalculating the total amount from the net amount creates precision errors.
1076 $this->total_ttc = (float) price2num((float) $this->total_ht + (float) $this->total_tva + $this->total_localtax1 + $this->total_localtax2, 'MT');
1077 $this->multicurrency_total_ttc = (float) price2num(((float) $this->total_ht + (float) $this->total_tva + $this->total_localtax1 + $this->total_localtax2) * (float) $this->multicurrency_tx, 'MT');
1078
1079 // For backward compatibility
1080 $this->amount_ttc = $this->total_ttc;
1081 $this->multicurrency_amount_ttc = $this->multicurrency_total_ttc;
1082 }
1083
1084 //var_dump("total_ht=".$this->total_ht." total_tva=".$this->total_tva." total_localtax1=".$this->total_localtax1." total_localtax2=".$this->total_localtax2." total_ttc=".$this->total_ttc);
1085
1086 return $err;
1087 }
1088
1096 public function splitAmount($amount_ttc1, $amount_ttc2)
1097 {
1098 // Moved from remx.php :: domain-related operations within action confirm_split
1099 $newdiscount1 = new DiscountAbsolute($this->db);
1100 $newdiscount2 = new DiscountAbsolute($this->db);
1101 $newdiscount1->fk_facture_source = $this->fk_facture_source;
1102 $newdiscount2->fk_facture_source = $this->fk_facture_source;
1103 $newdiscount1->fk_facture = $this->fk_facture;
1104 $newdiscount2->fk_facture = $this->fk_facture;
1105 $newdiscount1->fk_facture_line = $this->fk_facture_line;
1106 $newdiscount2->fk_facture_line = $this->fk_facture_line;
1107 $newdiscount1->fk_invoice_supplier_source = $this->fk_invoice_supplier_source;
1108 $newdiscount2->fk_invoice_supplier_source = $this->fk_invoice_supplier_source;
1109 $newdiscount1->fk_invoice_supplier = $this->fk_invoice_supplier;
1110 $newdiscount2->fk_invoice_supplier = $this->fk_invoice_supplier;
1111 $newdiscount1->fk_invoice_supplier_line = $this->fk_invoice_supplier_line;
1112 $newdiscount2->fk_invoice_supplier_line = $this->fk_invoice_supplier_line;
1113 if ($this->description == '(CREDIT_NOTE)' || $this->description == '(DEPOSIT)') {
1114 $newdiscount1->description = $this->description;
1115 $newdiscount2->description = $this->description;
1116 } else {
1117 $newdiscount1->description = $this->description.' (1)';
1118 $newdiscount2->description = $this->description.' (2)';
1119 }
1120 $newdiscount1->fk_user = $this->fk_user;
1121 $newdiscount2->fk_user = $this->fk_user;
1122 $newdiscount1->fk_soc = $this->fk_soc;
1123 $newdiscount1->socid = $this->socid;
1124 $newdiscount2->fk_soc = $this->fk_soc;
1125 $newdiscount2->socid = $this->socid;
1126 $newdiscount1->discount_type = $this->discount_type;
1127 $newdiscount2->discount_type = $this->discount_type;
1128 $newdiscount1->datec = $this->datec;
1129 $newdiscount2->datec = $this->datec;
1130 $newdiscount1->tva_tx = $this->tva_tx;
1131 $newdiscount2->tva_tx = $this->tva_tx;
1132 $newdiscount1->localtax1_tx = $this->localtax1_tx;
1133 $newdiscount2->localtax1_tx = $this->localtax1_tx;
1134 $newdiscount1->localtax2_tx = $this->localtax2_tx;
1135 $newdiscount2->localtax2_tx = $this->localtax2_tx;
1136 $newdiscount1->vat_src_code = $this->vat_src_code;
1137 $newdiscount2->vat_src_code = $this->vat_src_code;
1138
1139 $newdiscount1->generateFromAmount($amount_ttc1, 1, $newdiscount1->tva_tx, $newdiscount1->localtax1_tx, $newdiscount1->localtax2_tx, $this->localtax1_type, $this->localtax2_type);
1140 $newdiscount2->generateFromAmount($amount_ttc2, 1, $newdiscount2->tva_tx, $newdiscount2->localtax1_tx, $newdiscount2->localtax2_tx, $this->localtax1_type, $this->localtax2_type);
1141
1142 return array($newdiscount1, $newdiscount2);
1143 }
1144}
const TYPE_CREDIT_NOTE
Credit note invoice.
const TYPE_DEPOSIT
Deposit invoice.
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_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)
Definition html.lib.php:181