dolibarr  16.0.1
commoninvoice.class.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2012 Regis Houssin <regis.houssin@inodbox.com>
3  * Copyright (C) 2012 Cédric Salvador <csalvador@gpcsolutions.fr>
4  * Copyright (C) 2012-2014 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program. If not, see <https://www.gnu.org/licenses/>.
18  */
19 
26 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
27 require_once DOL_DOCUMENT_ROOT.'/core/class/commonincoterm.class.php';
28 
32 abstract class CommonInvoice extends CommonObject
33 {
34  use CommonIncoterm;
35 
39  const TYPE_STANDARD = 0;
40 
44  const TYPE_REPLACEMENT = 1;
45 
49  const TYPE_CREDIT_NOTE = 2;
50 
54  const TYPE_DEPOSIT = 3;
55 
60  const TYPE_PROFORMA = 4;
61 
65  const TYPE_SITUATION = 5;
66 
70  const STATUS_DRAFT = 0;
71 
75  const STATUS_VALIDATED = 1;
76 
84  const STATUS_CLOSED = 2;
85 
93  const STATUS_ABANDONED = 3;
94 
95 
96  public $totalpaid; // duplicate with sumpayed
97  public $totaldeposits; // duplicate with sumdeposit
98  public $totalcreditnotes; // duplicate with sumcreditnote
99 
100  public $sumpayed;
101  public $sumpayed_multicurrency;
102  public $sumdeposit;
103  public $sumdeposit_multicurrency;
104  public $sumcreditnote;
105  public $sumcreditnote_multicurrency;
106  public $remaintopay;
107 
108 
116  public function getRemainToPay($multicurrency = 0)
117  {
118  $alreadypaid = 0.0;
119  $alreadypaid += $this->getSommePaiement($multicurrency);
120  $alreadypaid += $this->getSumDepositsUsed($multicurrency);
121  $alreadypaid += $this->getSumCreditNotesUsed($multicurrency);
122 
123  $remaintopay = price2num($this->total_ttc - $alreadypaid, 'MT');
124  if ($this->statut == self::STATUS_CLOSED && $this->close_code == 'discount_vat') { // If invoice closed with discount for anticipated payment
125  $remaintopay = 0.0;
126  }
127  return $remaintopay;
128  }
129 
137  public function getSommePaiement($multicurrency = 0)
138  {
139  $table = 'paiement_facture';
140  $field = 'fk_facture';
141  if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
142  $table = 'paiementfourn_facturefourn';
143  $field = 'fk_facturefourn';
144  }
145 
146  $sql = "SELECT sum(amount) as amount, sum(multicurrency_amount) as multicurrency_amount";
147  $sql .= " FROM ".$this->db->prefix().$table;
148  $sql .= " WHERE ".$field." = ".((int) $this->id);
149 
150  dol_syslog(get_class($this)."::getSommePaiement", LOG_DEBUG);
151  $resql = $this->db->query($sql);
152  if ($resql) {
153  $obj = $this->db->fetch_object($resql);
154 
155  $this->db->free($resql);
156  if ($multicurrency) {
157  $this->sumpayed_multicurrency = $obj->multicurrency_amount;
158  return $obj->multicurrency_amount;
159  } else {
160  $this->sumpayed = $obj->amount;
161  return $obj->amount;
162  }
163  } else {
164  $this->error = $this->db->lasterror();
165  return -1;
166  }
167  }
168 
177  public function getSumDepositsUsed($multicurrency = 0)
178  {
179  /*if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
180  // FACTURE_DEPOSITS_ARE_JUST_PAYMENTS was never supported for purchase invoice, so we can return 0 with no need of SQL for this case.
181  return 0.0;
182  }*/
183 
184  require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
185 
186  $discountstatic = new DiscountAbsolute($this->db);
187  $result = $discountstatic->getSumDepositsUsed($this, $multicurrency);
188 
189  if ($result >= 0) {
190  if ($multicurrency) {
191  $this->sumdeposit_multicurrency = $result;
192  } else {
193  $this->sumdeposit = $result;
194  }
195 
196  return $result;
197  } else {
198  $this->error = $discountstatic->error;
199  return -1;
200  }
201  }
202 
209  public function getSumCreditNotesUsed($multicurrency = 0)
210  {
211  require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
212 
213  $discountstatic = new DiscountAbsolute($this->db);
214  $result = $discountstatic->getSumCreditNotesUsed($this, $multicurrency);
215  if ($result >= 0) {
216  if ($multicurrency) {
217  $this->sumcreditnote_multicurrency = $result;
218  } else {
219  $this->sumcreditnote = $result;
220  }
221 
222  return $result;
223  } else {
224  $this->error = $discountstatic->error;
225  return -1;
226  }
227  }
228 
235  public function getSumFromThisCreditNotesNotUsed($multicurrency = 0)
236  {
237  require_once DOL_DOCUMENT_ROOT.'/core/class/discount.class.php';
238 
239  $discountstatic = new DiscountAbsolute($this->db);
240  $result = $discountstatic->getSumFromThisCreditNotesNotUsed($this, $multicurrency);
241  if ($result >= 0) {
242  return $result;
243  } else {
244  $this->error = $discountstatic->error;
245  return -1;
246  }
247  }
248 
254  public function getListIdAvoirFromInvoice()
255  {
256  $idarray = array();
257 
258  $sql = "SELECT rowid";
259  $sql .= " FROM ".$this->db->prefix().$this->table_element;
260  $sql .= " WHERE fk_facture_source = ".((int) $this->id);
261  $sql .= " AND type = 2";
262  $resql = $this->db->query($sql);
263  if ($resql) {
264  $num = $this->db->num_rows($resql);
265  $i = 0;
266  while ($i < $num) {
267  $row = $this->db->fetch_row($resql);
268  $idarray[] = $row[0];
269  $i++;
270  }
271  } else {
272  dol_print_error($this->db);
273  }
274  return $idarray;
275  }
276 
283  public function getIdReplacingInvoice($option = '')
284  {
285  $sql = "SELECT rowid";
286  $sql .= " FROM ".$this->db->prefix().$this->table_element;
287  $sql .= " WHERE fk_facture_source = ".((int) $this->id);
288  $sql .= " AND type < 2";
289  if ($option == 'validated') {
290  $sql .= ' AND fk_statut = 1';
291  }
292  // PROTECTION BAD DATA
293  // In case the database is corrupted and there is a valid replectement invoice
294  // and another no, priority is given to the valid one.
295  // Should not happen (unless concurrent access and 2 people have created a
296  // replacement invoice for the same invoice at the same time)
297  $sql .= " ORDER BY fk_statut DESC";
298 
299  $resql = $this->db->query($sql);
300  if ($resql) {
301  $obj = $this->db->fetch_object($resql);
302  if ($obj) {
303  // If there is any
304  return $obj->rowid;
305  } else {
306  // If no invoice replaces it
307  return 0;
308  }
309  } else {
310  return -1;
311  }
312  }
313 
320  public function getListOfPayments($filtertype = '')
321  {
322  $retarray = array();
323 
324  $table = 'paiement_facture';
325  $table2 = 'paiement';
326  $field = 'fk_facture';
327  $field2 = 'fk_paiement';
328  $field3 = ', p.ref_ext';
329  $sharedentity = 'facture';
330  if ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
331  $table = 'paiementfourn_facturefourn';
332  $table2 = 'paiementfourn';
333  $field = 'fk_facturefourn';
334  $field2 = 'fk_paiementfourn';
335  $field3 = '';
336  $sharedentity = 'facture_fourn';
337  }
338 
339  $sql = "SELECT p.ref, pf.amount, pf.multicurrency_amount, p.fk_paiement, p.datep, p.num_paiement as num, t.code".$field3;
340  $sql .= " FROM ".$this->db->prefix().$table." as pf, ".$this->db->prefix().$table2." as p, ".$this->db->prefix()."c_paiement as t";
341  $sql .= " WHERE pf.".$field." = ".((int) $this->id);
342  $sql .= " AND pf.".$field2." = p.rowid";
343  $sql .= ' AND p.fk_paiement = t.id';
344  $sql .= ' AND p.entity IN ('.getEntity($sharedentity).')';
345  if ($filtertype) {
346  $sql .= " AND t.code='PRE'";
347  }
348 
349  dol_syslog(get_class($this)."::getListOfPayments", LOG_DEBUG);
350  $resql = $this->db->query($sql);
351  if ($resql) {
352  $num = $this->db->num_rows($resql);
353  $i = 0;
354  while ($i < $num) {
355  $obj = $this->db->fetch_object($resql);
356  $tmp = array('amount'=>$obj->amount, 'type'=>$obj->code, 'date'=>$obj->datep, 'num'=>$obj->num, 'ref'=>$obj->ref);
357  if (!empty($field3)) {
358  $tmp['ref_ext'] = $obj->ref_ext;
359  }
360  $retarray[] = $tmp;
361  $i++;
362  }
363  $this->db->free($resql);
364 
365  //look for credit notes and discounts and deposits
366  $sql = '';
367  if ($this->element == 'facture' || $this->element == 'invoice') {
368  $sql = "SELECT rc.amount_ttc as amount, rc.multicurrency_amount_ttc as multicurrency_amount, rc.datec as date, f.ref as ref, rc.description as type";
369  $sql .= ' FROM '.$this->db->prefix().'societe_remise_except as rc, '.$this->db->prefix().'facture as f';
370  $sql .= ' WHERE rc.fk_facture_source=f.rowid AND rc.fk_facture = '.((int) $this->id);
371  $sql .= ' AND (f.type = 2 OR f.type = 0 OR f.type = 3)'; // Find discount coming from credit note or excess received or deposits (payments from deposits are always null except if FACTURE_DEPOSITS_ARE_JUST_PAYMENTS is set)
372  } elseif ($this->element == 'facture_fourn' || $this->element == 'invoice_supplier') {
373  $sql = "SELECT rc.amount_ttc as amount, rc.multicurrency_amount_ttc as multicurrency_amount, rc.datec as date, f.ref as ref, rc.description as type";
374  $sql .= ' FROM '.$this->db->prefix().'societe_remise_except as rc, '.$this->db->prefix().'facture_fourn as f';
375  $sql .= ' WHERE rc.fk_invoice_supplier_source=f.rowid AND rc.fk_invoice_supplier = '.((int) $this->id);
376  $sql .= ' AND (f.type = 2 OR f.type = 0 OR f.type = 3)'; // Find discount coming from credit note or excess received or deposits (payments from deposits are always null except if FACTURE_DEPOSITS_ARE_JUST_PAYMENTS is set)
377  }
378 
379  if ($sql) {
380  $resql = $this->db->query($sql);
381  if ($resql) {
382  $num = $this->db->num_rows($resql);
383  $i = 0;
384  while ($i < $num) {
385  $obj = $this->db->fetch_object($resql);
386  if ($multicurrency) {
387  $retarray[] = array('amount'=>$obj->multicurrency_amount, 'type'=>$obj->type, 'date'=>$obj->date, 'num'=>'0', 'ref'=>$obj->ref);
388  } else {
389  $retarray[] = array('amount'=>$obj->amount, 'type'=>$obj->type, 'date'=>$obj->date, 'num'=>'', 'ref'=>$obj->ref);
390  }
391  $i++;
392  }
393  } else {
394  $this->error = $this->db->lasterror();
395  dol_print_error($this->db);
396  return array();
397  }
398  $this->db->free($resql);
399  }
400 
401  return $retarray;
402  } else {
403  $this->error = $this->db->lasterror();
404  dol_print_error($this->db);
405  return array();
406  }
407  }
408 
409 
410  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
424  public function is_erasable()
425  {
426  // phpcs:enable
427  global $conf;
428 
429  // We check if invoice is a temporary number (PROVxxxx)
430  $tmppart = substr($this->ref, 1, 4);
431 
432  if ($this->statut == self::STATUS_DRAFT && $tmppart === 'PROV') { // If draft invoice and ref not yet defined
433  return 1;
434  }
435 
436  if (!empty($conf->global->INVOICE_CAN_NEVER_BE_REMOVED)) {
437  return 0;
438  }
439 
440  // If not a draft invoice and not temporary invoice
441  if ($tmppart !== 'PROV') {
442  $ventilExportCompta = $this->getVentilExportCompta();
443  if ($ventilExportCompta != 0) {
444  return -1;
445  }
446 
447  // Get last number of validated invoice
448  if ($this->element != 'invoice_supplier') {
449  if (empty($this->thirdparty)) {
450  $this->fetch_thirdparty(); // We need to have this->thirdparty defined, in case of numbering rule use tags that depend on thirdparty (like {t} tag).
451  }
452  $maxref = $this->getNextNumRef($this->thirdparty, 'last');
453 
454  // If there is no invoice into the reset range and not already dispatched, we can delete
455  // If invoice to delete is last one and not already dispatched, we can delete
456  if (empty($conf->global->INVOICE_CAN_ALWAYS_BE_REMOVED) && $maxref != '' && $maxref != $this->ref) {
457  return -2;
458  }
459 
460  // TODO If there is payment in bookkeeping, check payment is not dispatched in accounting
461  // ...
462 
463  if ($this->situation_cycle_ref && method_exists($this, 'is_last_in_cycle')) {
464  $last = $this->is_last_in_cycle();
465  if (!$last) {
466  return -3;
467  }
468  }
469  }
470  }
471 
472  // Test if there is at least one payment. If yes, refuse to delete.
473  if (empty($conf->global->INVOICE_CAN_ALWAYS_BE_REMOVED) && $this->getSommePaiement() > 0) {
474  return -4;
475  }
476 
477  return 2;
478  }
479 
485  public function getVentilExportCompta()
486  {
487  $alreadydispatched = 0;
488 
489  $type = 'customer_invoice';
490  if ($this->element == 'invoice_supplier') {
491  $type = 'supplier_invoice';
492  }
493 
494  $sql = " SELECT COUNT(ab.rowid) as nb FROM ".$this->db->prefix()."accounting_bookkeeping as ab WHERE ab.doc_type='".$this->db->escape($type)."' AND ab.fk_doc = ".((int) $this->id);
495  $resql = $this->db->query($sql);
496  if ($resql) {
497  $obj = $this->db->fetch_object($resql);
498  if ($obj) {
499  $alreadydispatched = $obj->nb;
500  }
501  } else {
502  $this->error = $this->db->lasterror();
503  return -1;
504  }
505 
506  if ($alreadydispatched) {
507  return 1;
508  }
509  return 0;
510  }
511 
512 
518  public function getLibType()
519  {
520  global $langs;
521  if ($this->type == CommonInvoice::TYPE_STANDARD) {
522  return $langs->trans("InvoiceStandard");
523  } elseif ($this->type == CommonInvoice::TYPE_REPLACEMENT) {
524  return $langs->trans("InvoiceReplacement");
525  } elseif ($this->type == CommonInvoice::TYPE_CREDIT_NOTE) {
526  return $langs->trans("InvoiceAvoir");
527  } elseif ($this->type == CommonInvoice::TYPE_DEPOSIT) {
528  return $langs->trans("InvoiceDeposit");
529  } elseif ($this->type == CommonInvoice::TYPE_PROFORMA) {
530  return $langs->trans("InvoiceProForma"); // Not used.
531  } elseif ($this->type == CommonInvoice::TYPE_SITUATION) {
532  return $langs->trans("InvoiceSituation");
533  }
534  return $langs->trans("Unknown");
535  }
536 
544  public function getLibStatut($mode = 0, $alreadypaid = -1)
545  {
546  return $this->LibStatut($this->paye, $this->statut, $mode, $alreadypaid, $this->type);
547  }
548 
549  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
560  public function LibStatut($paye, $status, $mode = 0, $alreadypaid = -1, $type = -1)
561  {
562  // phpcs:enable
563  global $langs;
564  $langs->load('bills');
565 
566  if ($type == -1) {
567  $type = $this->type;
568  }
569 
570  $statusType = 'status0';
571  $prefix = 'Short';
572  if (!$paye) {
573  if ($status == 0) {
574  $labelStatus = $langs->transnoentitiesnoconv('BillStatusDraft');
575  $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusDraft');
576  } elseif (($status == 3 || $status == 2) && $alreadypaid <= 0) {
577  if ($status == 3) {
578  $labelStatus = $langs->transnoentitiesnoconv('BillStatusCanceled');
579  $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusCanceled');
580  } else {
581  $labelStatus = $langs->transnoentitiesnoconv('BillStatusClosedUnpaid');
582  $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusClosedUnpaid');
583  }
584  $statusType = 'status5';
585  } elseif (($status == 3 || $status == 2) && $alreadypaid > 0) {
586  $labelStatus = $langs->transnoentitiesnoconv('BillStatusClosedPaidPartially');
587  $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusClosedPaidPartially');
588  $statusType = 'status9';
589  } elseif ($alreadypaid == 0) {
590  $labelStatus = $langs->transnoentitiesnoconv('BillStatusNotPaid');
591  $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusNotPaid');
592  $statusType = 'status1';
593  } else {
594  $labelStatus = $langs->transnoentitiesnoconv('BillStatusStarted');
595  $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusStarted');
596  $statusType = 'status3';
597  }
598  } else {
599  $statusType = 'status6';
600 
601  if ($type == self::TYPE_CREDIT_NOTE) {
602  $labelStatus = $langs->transnoentitiesnoconv('BillStatusPaidBackOrConverted'); // credit note
603  $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusPaidBackOrConverted'); // credit note
604  } elseif ($type == self::TYPE_DEPOSIT) {
605  $labelStatus = $langs->transnoentitiesnoconv('BillStatusConverted'); // deposit invoice
606  $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusConverted'); // deposit invoice
607  } else {
608  $labelStatus = $langs->transnoentitiesnoconv('BillStatusPaid');
609  $labelStatusShort = $langs->transnoentitiesnoconv('Bill'.$prefix.'StatusPaid');
610  }
611  }
612 
613  return dolGetStatus($labelStatus, $labelStatusShort, '', $statusType, $mode);
614  }
615 
616  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
624  public function calculate_date_lim_reglement($cond_reglement = 0)
625  {
626  // phpcs:enable
627  if (!$cond_reglement) {
628  $cond_reglement = $this->cond_reglement_code;
629  }
630  if (!$cond_reglement) {
631  $cond_reglement = $this->cond_reglement_id;
632  }
633 
634  $cdr_nbjour = 0;
635  $cdr_type = 0;
636  $cdr_decalage = 0;
637 
638  $sqltemp = "SELECT c.type_cdr, c.nbjour, c.decalage";
639  $sqltemp .= " FROM ".$this->db->prefix()."c_payment_term as c";
640  if (is_numeric($cond_reglement)) {
641  $sqltemp .= " WHERE c.rowid=".((int) $cond_reglement);
642  } else {
643  $sqltemp .= " WHERE c.entity IN (".getEntity('c_payment_term').")";
644  $sqltemp .= " AND c.code = '".$this->db->escape($cond_reglement)."'";
645  }
646 
647  dol_syslog(get_class($this).'::calculate_date_lim_reglement', LOG_DEBUG);
648  $resqltemp = $this->db->query($sqltemp);
649  if ($resqltemp) {
650  if ($this->db->num_rows($resqltemp)) {
651  $obj = $this->db->fetch_object($resqltemp);
652  $cdr_nbjour = $obj->nbjour;
653  $cdr_type = $obj->type_cdr;
654  $cdr_decalage = $obj->decalage;
655  }
656  } else {
657  $this->error = $this->db->error();
658  return -1;
659  }
660  $this->db->free($resqltemp);
661 
662  /* Definition de la date limite */
663 
664  // 0 : adding the number of days
665  if ($cdr_type == 0) {
666  $datelim = $this->date + ($cdr_nbjour * 3600 * 24);
667 
668  $datelim += ($cdr_decalage * 3600 * 24);
669  } elseif ($cdr_type == 1) {
670  // 1 : application of the "end of the month" rule
671  $datelim = $this->date + ($cdr_nbjour * 3600 * 24);
672 
673  $mois = date('m', $datelim);
674  $annee = date('Y', $datelim);
675  if ($mois == 12) {
676  $mois = 1;
677  $annee += 1;
678  } else {
679  $mois += 1;
680  }
681  // We move at the beginning of the next month, and we take a day off
682  $datelim = dol_mktime(12, 0, 0, $mois, 1, $annee);
683  $datelim -= (3600 * 24);
684 
685  $datelim += ($cdr_decalage * 3600 * 24);
686  } elseif ($cdr_type == 2 && !empty($cdr_decalage)) {
687  // 2 : application of the rule, the N of the current or next month
688  include_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
689  $datelim = $this->date + ($cdr_nbjour * 3600 * 24);
690 
691  $date_piece = dol_mktime(0, 0, 0, date('m', $datelim), date('d', $datelim), date('Y', $datelim)); // Sans les heures minutes et secondes
692  $date_lim_current = dol_mktime(0, 0, 0, date('m', $datelim), $cdr_decalage, date('Y', $datelim)); // Sans les heures minutes et secondes
693  $date_lim_next = dol_time_plus_duree($date_lim_current, 1, 'm'); // Add 1 month
694 
695  $diff = $date_piece - $date_lim_current;
696 
697  if ($diff < 0) {
698  $datelim = $date_lim_current;
699  } else {
700  $datelim = $date_lim_next;
701  }
702  } else {
703  return 'Bad value for type_cdr in database for record cond_reglement = '.$cond_reglement;
704  }
705 
706  return $datelim;
707  }
708 
709  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
720  public function demande_prelevement($fuser, $amount = 0, $type = 'direct-debit', $sourcetype = 'facture')
721  {
722  // phpcs:enable
723  global $conf;
724 
725  $error = 0;
726 
727  dol_syslog(get_class($this)."::demande_prelevement", LOG_DEBUG);
728 
729  if ($this->statut > self::STATUS_DRAFT && $this->paye == 0) {
730  require_once DOL_DOCUMENT_ROOT.'/societe/class/companybankaccount.class.php';
731  $bac = new CompanyBankAccount($this->db);
732  $bac->fetch(0, $this->socid);
733 
734  $sql = "SELECT count(*)";
735  $sql .= " FROM ".$this->db->prefix()."prelevement_facture_demande";
736  if ($type == 'bank-transfer') {
737  $sql .= " WHERE fk_facture_fourn = ".((int) $this->id);
738  } else {
739  $sql .= " WHERE fk_facture = ".((int) $this->id);
740  }
741  $sql .= " AND ext_payment_id IS NULL"; // To exclude record done for some online payments
742  $sql .= " AND traite = 0";
743 
744  dol_syslog(get_class($this)."::demande_prelevement", LOG_DEBUG);
745  $resql = $this->db->query($sql);
746  if ($resql) {
747  $row = $this->db->fetch_row($resql);
748  if ($row[0] == 0) {
749  $now = dol_now();
750 
751  $totalpaid = $this->getSommePaiement();
752  $totalcreditnotes = $this->getSumCreditNotesUsed();
753  $totaldeposits = $this->getSumDepositsUsed();
754  //print "totalpaid=".$totalpaid." totalcreditnotes=".$totalcreditnotes." totaldeposts=".$totaldeposits;
755 
756  // We can also use bcadd to avoid pb with floating points
757  // For example print 239.2 - 229.3 - 9.9; does not return 0.
758  //$resteapayer=bcadd($this->total_ttc,$totalpaid,$conf->global->MAIN_MAX_DECIMALS_TOT);
759  //$resteapayer=bcadd($resteapayer,$totalavoir,$conf->global->MAIN_MAX_DECIMALS_TOT);
760  if (empty($amount)) {
761  $amount = price2num($this->total_ttc - $totalpaid - $totalcreditnotes - $totaldeposits, 'MT');
762  }
763 
764  if (is_numeric($amount) && $amount != 0) {
765  $sql = 'INSERT INTO '.$this->db->prefix().'prelevement_facture_demande(';
766  if ($type == 'bank-transfer') {
767  $sql .= 'fk_facture_fourn, ';
768  } else {
769  $sql .= 'fk_facture, ';
770  }
771  $sql .= ' amount, date_demande, fk_user_demande, code_banque, code_guichet, number, cle_rib, sourcetype, entity)';
772  $sql .= " VALUES (".((int) $this->id);
773  $sql .= ", ".((float) price2num($amount));
774  $sql .= ", '".$this->db->idate($now)."'";
775  $sql .= ", ".((int) $fuser->id);
776  $sql .= ", '".$this->db->escape($bac->code_banque)."'";
777  $sql .= ", '".$this->db->escape($bac->code_guichet)."'";
778  $sql .= ", '".$this->db->escape($bac->number)."'";
779  $sql .= ", '".$this->db->escape($bac->cle_rib)."'";
780  $sql .= ", '".$this->db->escape($sourcetype)."'";
781  $sql .= ", ".((int) $conf->entity);
782  $sql .= ")";
783 
784  dol_syslog(get_class($this)."::demande_prelevement", LOG_DEBUG);
785  $resql = $this->db->query($sql);
786  if (!$resql) {
787  $this->error = $this->db->lasterror();
788  dol_syslog(get_class($this).'::demandeprelevement Erreur');
789  $error++;
790  }
791  } else {
792  $this->error = 'WithdrawRequestErrorNilAmount';
793  dol_syslog(get_class($this).'::demandeprelevement WithdrawRequestErrorNilAmount');
794  $error++;
795  }
796 
797  if (!$error) {
798  // Force payment mode of invoice to withdraw
799  $payment_mode_id = dol_getIdFromCode($this->db, ($type == 'bank-transfer' ? 'VIR' : 'PRE'), 'c_paiement', 'code', 'id', 1);
800  if ($payment_mode_id > 0) {
801  $result = $this->setPaymentMethods($payment_mode_id);
802  }
803  }
804 
805  if ($error) {
806  return -1;
807  }
808  return 1;
809  } else {
810  $this->error = "A request already exists";
811  dol_syslog(get_class($this).'::demandeprelevement Impossible de creer une demande, demande deja en cours');
812  return 0;
813  }
814  } else {
815  $this->error = $this->db->error();
816  dol_syslog(get_class($this).'::demandeprelevement Erreur -2');
817  return -2;
818  }
819  } else {
820  $this->error = "Status of invoice does not allow this";
821  dol_syslog(get_class($this)."::demandeprelevement ".$this->error." $this->statut, $this->paye, $this->mode_reglement_id");
822  return -3;
823  }
824  }
825 
826  // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
834  public function demande_prelevement_delete($fuser, $did)
835  {
836  // phpcs:enable
837  $sql = 'DELETE FROM '.$this->db->prefix().'prelevement_facture_demande';
838  $sql .= ' WHERE rowid = '.((int) $did);
839  $sql .= ' AND traite = 0';
840  if ($this->db->query($sql)) {
841  return 0;
842  } else {
843  $this->error = $this->db->lasterror();
844  dol_syslog(get_class($this).'::demande_prelevement_delete Error '.$this->error);
845  return -1;
846  }
847  }
848 
849 
855  public function buildZATCAQRString()
856  {
857  global $conf, $mysoc;
858 
859  $tmplang = new Translate('', $conf);
860  $tmplang->setDefaultLang('en_US');
861  $tmplang->load("main");
862 
863  $datestring = dol_print_date($this->date, 'dayhourrfc');
864  //$pricewithtaxstring = price($this->total_ttc, 0, $tmplang, 0, -1, 2);
865  //$pricetaxstring = price($this->total_tva, 0, $tmplang, 0, -1, 2);
866  $pricewithtaxstring = price2num($this->total_ttc, 2, 1);
867  $pricetaxstring = price2num($this->total_tva, 2, 1);
868 
869  /*
870  $name = implode(unpack("H*", $this->thirdparty->name));
871  $vatnumber = implode(unpack("H*", $this->thirdparty->tva_intra));
872  $date = implode(unpack("H*", $datestring));
873  $pricewithtax = implode(unpack("H*", price2num($pricewithtaxstring, 2)));
874  $pricetax = implode(unpack("H*", $pricetaxstring));
875 
876  //var_dump(strlen($this->thirdparty->name));
877  //var_dump(str_pad(dechex('9'), 2, '0', STR_PAD_LEFT));
878  //var_dump($this->thirdparty->name);
879  //var_dump(implode(unpack("H*", $this->thirdparty->name)));
880  //var_dump(price($this->total_tva, 0, $tmplang, 0, -1, 2));
881 
882  $s = '01'.str_pad(dechex(strlen($this->thirdparty->name)), 2, '0', STR_PAD_LEFT).$name;
883  $s .= '02'.str_pad(dechex(strlen($this->thirdparty->tva_intra)), 2, '0', STR_PAD_LEFT).$vatnumber;
884  $s .= '03'.str_pad(dechex(strlen($datestring)), 2, '0', STR_PAD_LEFT).$date;
885  $s .= '04'.str_pad(dechex(strlen($pricewithtaxstring)), 2, '0', STR_PAD_LEFT).$pricewithtax;
886  $s .= '05'.str_pad(dechex(strlen($pricetaxstring)), 2, '0', STR_PAD_LEFT).$pricetax;
887  $s .= ''; // Hash of xml invoice
888  $s .= ''; // ecda signature
889  $s .= ''; // ecda public key
890  $s .= ''; // ecda signature of public key stamp
891  */
892 
893  // Using TLV format
894  $s = pack('C1', 1).pack('C1', strlen($mysoc->name)).$mysoc->name;
895  $s .= pack('C1', 2).pack('C1', strlen($mysoc->tva_intra)).$mysoc->tva_intra;
896  $s .= pack('C1', 3).pack('C1', strlen($datestring)).$datestring;
897  $s .= pack('C1', 4).pack('C1', strlen($pricewithtaxstring)).$pricewithtaxstring;
898  $s .= pack('C1', 5).pack('C1', strlen($pricetaxstring)).$pricetaxstring;
899  $s .= ''; // Hash of xml invoice
900  $s .= ''; // ecda signature
901  $s .= ''; // ecda public key
902  $s .= ''; // ecda signature of public key stamp
903 
904  $s = base64_encode($s);
905 
906  return $s;
907  }
908 
909 
915  public function buildSwitzerlandQRString()
916  {
917  global $conf, $mysoc;
918 
919  $tmplang = new Translate('', $conf);
920  $tmplang->setDefaultLang('en_US');
921  $tmplang->load("main");
922 
923  $pricewithtaxstring = price2num($this->total_ttc, 2, 1);
924  $pricetaxstring = price2num($this->total_tva, 2, 1);
925 
926  $complementaryinfo = '';
927  /*
928  Example: //S1/10/10201409/11/190512/20/1400.000-53/30/106017086/31/180508/32/7.7/40/2:10;0:30
929  /10/ Numéro de facture – 10201409
930  /11/ Date de facture – 12.05.2019
931  /20/ Référence client – 1400.000-53
932  /30/ Numéro IDE pour la TVA – CHE-106.017.086 TVA
933  /31/ Date de la prestation pour la comptabilisation de la TVA – 08.05.2018
934  /32/ Taux de TVA sur le montant total de la facture – 7.7%
935  /40/ Conditions – 2% d’escompte à 10 jours, paiement net à 30 jours
936  */
937  $datestring = dol_print_date($this->date, '%y%m%d');
938  //$pricewithtaxstring = price($this->total_ttc, 0, $tmplang, 0, -1, 2);
939  //$pricetaxstring = price($this->total_tva, 0, $tmplang, 0, -1, 2);
940  $complementaryinfo = '//S1/10/'.str_replace('/', '', $this->ref).'/11/'.$datestring;
941  if ($this->ref_client) {
942  $complementaryinfo .= '/20/'.$this->ref_client;
943  }
944  if ($this->thirdparty->vat_number) {
945  $complementaryinfo .= '/30/'.$this->thirdparty->vat_number;
946  }
947 
948  // Header
949  $s .= "SPC\n";
950  $s .= "0200\n";
951  $s .= "1\n";
952  if ($this->fk_account > 0) {
953  // Bank BAN if country is LI or CH
954  // TODO Add
955  $bankaccount = new Account($this->db);
956  $bankaccount->fetch($this->fk_account);
957  $s .= $bankaccount->iban."\n";
958  } else {
959  $s .= "\n";
960  }
961  // Seller
962  $s .= "S\n";
963  $s .= dol_trunc($mysoc->name, 70, 'right', 'UTF-8', 1)."\n";
964  $addresslinearray = explode("\n", $mysoc->address);
965  $s .= dol_trunc(empty($addresslinearray[1]) ? '' : $addresslinearray[1], 70, 'right', 'UTF-8', 1)."\n"; // address line 1
966  $s .= dol_trunc(empty($addresslinearray[2]) ? '' : $addresslinearray[2], 70, 'right', 'UTF-8', 1)."\n"; // address line 2
967  $s .= dol_trunc($mysoc->zip, 16, 'right', 'UTF-8', 1)."\n";
968  $s .= dol_trunc($mysoc->town, 35, 'right', 'UTF-8', 1)."\n";
969  $s .= dol_trunc($mysoc->country_code, 2, 'right', 'UTF-8', 1)."\n";
970  // Final seller
971  $s .= "\n";
972  $s .= "\n";
973  $s .= "\n";
974  $s .= "\n";
975  $s .= "\n";
976  $s .= "\n";
977  $s .= "\n";
978  // Amount of payment (to do?)
979  $s .= price($pricewithtaxstring, 0, 'none', 0, 0, 2)."\n";
980  $s .= ($this->multicurrency_code ? $this->multicurrency_code : $conf->currency)."\n";
981  // Buyer
982  $s .= "S\n";
983  $s .= dol_trunc($this->thirdparty->name, 70, 'right', 'UTF-8', 1)."\n";
984  $addresslinearray = explode("\n", $this->thirdparty->address);
985  $s .= dol_trunc(empty($addresslinearray[1]) ? '' : $addresslinearray[1], 70, 'right', 'UTF-8', 1)."\n"; // address line 1
986  $s .= dol_trunc(empty($addresslinearray[2]) ? '' : $addresslinearray[2], 70, 'right', 'UTF-8', 1)."\n"; // address line 2
987  $s .= dol_trunc($this->thirdparty->zip, 16, 'right', 'UTF-8', 1)."\n";
988  $s .= dol_trunc($this->thirdparty->town, 35, 'right', 'UTF-8', 1)."\n";
989  $s .= dol_trunc($this->thirdparty->country_code, 2, 'right', 'UTF-8', 1)."\n";
990  // ID of payment
991  $s .= "NON\n"; // NON or QRR
992  $s .= "\n"; // QR Code if previous field is QRR
993  if ($complementaryinfo) {
994  $s .= $complementaryinfo."\n";
995  } else {
996  $s .= "\n";
997  }
998  $s .= "EPD\n";
999  $s .= "\n";
1000  //var_dump($s);exit;
1001  return $s;
1002  }
1003 }
1004 
1005 
1006 
1007 require_once DOL_DOCUMENT_ROOT.'/core/class/commonobjectline.class.php';
1008 
1012 abstract class CommonInvoiceLine extends CommonObjectLine
1013 {
1018  public $label;
1019 
1024  public $ref; // Product ref (deprecated)
1029  public $libelle; // Product label (deprecated)
1030 
1035  public $product_type = 0;
1036 
1041  public $product_ref;
1042 
1047  public $product_label;
1048 
1053  public $product_desc;
1054 
1059  public $qty;
1060 
1065  public $subprice;
1066 
1072  public $price;
1073 
1078  public $fk_product;
1079 
1084  public $vat_src_code;
1085 
1090  public $tva_tx;
1091 
1096  public $localtax1_tx;
1097 
1102  public $localtax2_tx;
1103 
1108  public $localtax1_type;
1109 
1114  public $localtax2_type;
1115 
1120  public $remise_percent;
1121 
1127  public $remise;
1128 
1133  public $total_ht;
1134 
1139  public $total_tva;
1140 
1145  public $total_localtax1;
1146 
1151  public $total_localtax2;
1152 
1157  public $total_ttc;
1158 
1159  public $date_start_fill; // If set to 1, when invoice is created from a template invoice, it will also auto set the field date_start at creation
1160  public $date_end_fill; // If set to 1, when invoice is created from a template invoice, it will also auto set the field date_end at creation
1161 
1162  public $buy_price_ht;
1163  public $buyprice; // For backward compatibility
1164  public $pa_ht; // For backward compatibility
1165 
1166  public $marge_tx;
1167  public $marque_tx;
1168 
1175  public $info_bits = 0;
1176 
1177  public $special_code = 0;
1178 
1179  public $fk_multicurrency;
1180  public $multicurrency_code;
1181  public $multicurrency_subprice;
1182  public $multicurrency_total_ht;
1183  public $multicurrency_total_tva;
1184  public $multicurrency_total_ttc;
1185 
1186  public $fk_user_author;
1187  public $fk_user_modif;
1188 
1189  public $fk_accounting_account;
1190 }
calculate_date_lim_reglement($cond_reglement=0)
Returns an invoice payment deadline based on the invoice settlement conditions and billing date...
const STATUS_CLOSED
Classified paid.
const TYPE_STANDARD
Standard invoice.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm= 'auto', $check=1)
Return a timestamp date built from detailed informations (by default a local PHP server timestamp) Re...
getSumFromThisCreditNotesNotUsed($multicurrency=0)
Return amount (with tax) of all converted amount for this credit note.
buildZATCAQRString()
Build string for ZATCA QR Code (Arabi Saudia)
is_erasable()
Return if an invoice can be deleted Rule is: If invoice is draft and has a temporary ref -&gt; yes (1) I...
$conf db
API class for accounts.
Definition: inc.php:41
getListOfPayments($filtertype= '')
Return list of payments.
dol_now($mode= 'auto')
Return date for now.
demande_prelevement_delete($fuser, $did)
Remove a direct debit request or a credit transfer request.
buildSwitzerlandQRString()
Build string for QR-Bill (Switzerland)
Class to manage bank accounts description of third parties.
getLibStatut($mode=0, $alreadypaid=-1)
Return label of object status.
$label
Custom label of line.
fetch_thirdparty($force_thirdparty_id=0)
Load the third party of object, from id $this-&gt;socid or $this-&gt;fk_soc, into this-&gt;thirdparty.
dol_getIdFromCode($db, $key, $tablename, $fieldkey= 'code', $fieldid= 'id', $entityfilter=0, $filters= '')
Return an id or code from a code or id.
Parent class of all other business classes for details of elements (invoices, contracts, proposals, orders, ...)
dol_time_plus_duree($time, $duration_value, $duration_unit, $ruleforendofmonth=0)
Add a delay to a date.
Definition: date.lib.php:121
getListIdAvoirFromInvoice()
Returns array of credit note ids from the invoice.
Class to manage bank accounts.
const TYPE_PROFORMA
Proforma invoice.
price($amount, $form=0, $outlangs= '', $trunc=1, $rounding=-1, $forcerounding=-1, $currency_code= '')
Function to format a value into an amount for visual output Function used into PDF and HTML pages...
demande_prelevement($fuser, $amount=0, $type= 'direct-debit', $sourcetype= 'facture')
Create a withdrawal request for a direct debit order or a credit transfer order.
Parent class for class inheritance lines of business objects This class is useless for the moment so ...
getLibType()
Return label of type of invoice.
const TYPE_REPLACEMENT
Replacement invoice.
getRemainToPay($multicurrency=0)
Return remain amount to pay.
getSommePaiement($multicurrency=0)
Return amount of payments already done.
price2num($amount, $rounding= '', $option=0)
Function that return a number with universal decimal format (decimal separator is &#39;...
getSumCreditNotesUsed($multicurrency=0)
Return amount (with tax) of all credit notes invoices + excess received used by invoice.
const STATUS_DRAFT
Draft status.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename= '', $restricttologhandler= '', $logcontext=null)
Write log message into outputs.
Class to manage translations.
if(isModEnabled('facture')&&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur')&&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD)&&$user->rights->fournisseur->facture->lire)||(isModEnabled('supplier_invoice')&&$user->rights->supplier_invoice->lire)) if(isModEnabled('don')&&!empty($user->rights->don->lire)) if(isModEnabled('tax')&&!empty($user->rights->tax->charges->lire)) if(isModEnabled('facture')&&isModEnabled('commande')&&$user->rights->commande->lire &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:742
Superclass for invoices classes.
setPaymentMethods($id)
Change the payments methods.
trait CommonIncoterm
Superclass for incoterm classes.
const STATUS_ABANDONED
Classified abandoned and no payment done.
dol_print_date($time, $format= '', $tzoutput= 'auto', $outputlangs= '', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
getSumDepositsUsed($multicurrency=0)
Return amount (with tax) of all deposits invoices used by invoice.
const TYPE_SITUATION
Situation invoice.
getIdReplacingInvoice($option= '')
Returns the id of the invoice that replaces it.
getVentilExportCompta()
Return if an invoice was dispatched into bookkeeping.
$object ref
Definition: info.php:77
dol_print_error($db= '', $error= '', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
Class to manage absolute discounts.
const STATUS_VALIDATED
Validated (need to be paid)
dol_trunc($string, $size=40, $trunc= 'right', $stringencoding= 'UTF-8', $nodot=0, $display=0)
Truncate a string to a particular length adding &#39;…&#39; if string larger than length. ...
dolGetStatus($statusLabel= '', $statusLabelShort= '', $html= '', $statusType= 'status0', $displayMode=0, $url= '', $params=array())
Output the badge of a status.
const TYPE_DEPOSIT
Deposit invoice.
if(preg_match('/crypted:/i', $dolibarr_main_db_pass)||!empty($dolibarr_main_db_encrypted_pass)) $conf db type
Definition: repair.php:119
Parent class of all other business classes (invoices, contracts, proposals, orders, ...)
LibStatut($paye, $status, $mode=0, $alreadypaid=-1, $type=-1)
Return label of a status.
const TYPE_CREDIT_NOTE
Credit note invoice.