dolibarr  17.0.4
paiement.php
Go to the documentation of this file.
1 <?php
2 /* Copyright (C) 2001-2006 Rodolphe Quiedeville <rodolphe@quiedeville.org>
3  * Copyright (C) 2004-2017 Laurent Destailleur <eldy@users.sourceforge.net>
4  * Copyright (C) 2005 Marc Barilley / Ocebo <marc@ocebo.com>
5  * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
6  * Copyright (C) 2007 Franky Van Liedekerke <franky.van.liedekerke@telenet.be>
7  * Copyright (C) 2012 Cédric Salvador <csalvador@gpcsolutions.fr>
8  * Copyright (C) 2014 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
9  * Copyright (C) 2014 Teddy Andreotti <125155@supinfo.com>
10  * Copyright (C) 2015 Juanjo Menent <jmenent@2byte.es>
11  * Copyright (C) 2018-2021 Frédéric France <frederic.france@netlogic.fr>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 3 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program. If not, see <https://www.gnu.org/licenses/>.
25  */
26 
33 // Load Dolibarr environment
34 require '../main.inc.php';
35 require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
36 require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
37 require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
38 require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
39 
40 // Load translation files required by the page
41 $langs->loadLangs(array('companies', 'bills', 'banks', 'multicurrency'));
42 
43 $action = GETPOST('action', 'alpha');
44 $confirm = GETPOST('confirm', 'alpha');
45 
46 $facid = GETPOST('facid', 'int');
47 $accountid = GETPOST('accountid', 'int');
48 $paymentnum = GETPOST('num_paiement', 'alpha');
49 $socid = GETPOST('socid', 'int');
50 
51 $sortfield = GETPOST('sortfield', 'aZ09comma');
52 $sortorder = GETPOST('sortorder', 'aZ09comma');
53 $page = GETPOSTISSET('pageplusone') ? (GETPOST('pageplusone') - 1) : GETPOST("page", 'int');
54 
55 $amounts = array();
56 $amountsresttopay = array();
57 $addwarning = 0;
58 
59 $multicurrency_amounts = array();
60 $multicurrency_amountsresttopay = array();
61 
62 // Security check
63 if ($user->socid > 0) {
64  $socid = $user->socid;
65 }
66 
67 $object = new Facture($db);
68 
69 // Load object
70 if ($facid > 0) {
71  $ret = $object->fetch($facid);
72 }
73 
74 // Initialize technical object to manage hooks of paiements. Note that conf->hooks_modules contains array array
75 $hookmanager->initHooks(array('paiementcard', 'globalcard'));
76 
77 $formquestion = array();
78 
79 $usercanissuepayment = !empty($user->rights->facture->paiement);
80 
81 $fieldid = 'rowid';
82 $isdraft = (($object->statut == Facture::STATUS_DRAFT) ? 1 : 0);
83 $result = restrictedArea($user, 'facture', $object->id, '', '', 'fk_soc', $fieldid, $isdraft);
84 
85 
86 /*
87  * Actions
88  */
89 
90 $parameters = array('socid'=>$socid);
91 $reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks
92 if ($reshook < 0) {
93  setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
94 }
95 
96 if (empty($reshook)) {
97  if (($action == 'add_paiement' || ($action == 'confirm_paiement' && $confirm == 'yes')) && $usercanissuepayment) {
98  $error = 0;
99 
100  $datepaye = dol_mktime(12, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int'));
101  $paiement_id = 0;
102  $totalpayment = 0;
103  $multicurrency_totalpayment = 0;
104  $atleastonepaymentnotnull = 0;
105  $formquestion = array();
106  $i = 0;
107 
108  // Generate payment array and check if there is payment higher than invoice and payment date before invoice date
109  $tmpinvoice = new Facture($db);
110  foreach ($_POST as $key => $value) {
111  if (substr($key, 0, 7) == 'amount_' && GETPOST($key) != '') {
112  $cursorfacid = substr($key, 7);
113  $amounts[$cursorfacid] = price2num(GETPOST($key));
114  $totalpayment = $totalpayment + $amounts[$cursorfacid];
115  if (!empty($amounts[$cursorfacid])) {
116  $atleastonepaymentnotnull++;
117  }
118  $result = $tmpinvoice->fetch($cursorfacid);
119  if ($result <= 0) {
120  dol_print_error($db);
121  }
122  $amountsresttopay[$cursorfacid] = price2num($tmpinvoice->total_ttc - $tmpinvoice->getSommePaiement());
123  if ($amounts[$cursorfacid]) {
124  // Check amount
125  if ($amounts[$cursorfacid] && (abs($amounts[$cursorfacid]) > abs($amountsresttopay[$cursorfacid]))) {
126  $addwarning = 1;
127  $formquestion['text'] = img_warning($langs->trans("PaymentHigherThanReminderToPay")).' '.$langs->trans("HelpPaymentHigherThanReminderToPay");
128  }
129  // Check date
130  if ($datepaye && ($datepaye < $tmpinvoice->date)) {
131  $langs->load("errors");
132  //$error++;
133  setEventMessages($langs->transnoentities("WarningPaymentDateLowerThanInvoiceDate", dol_print_date($datepaye, 'day'), dol_print_date($tmpinvoice->date, 'day'), $tmpinvoice->ref), null, 'warnings');
134  }
135  }
136 
137  $formquestion[$i++] = array('type' => 'hidden', 'name' => $key, 'value' => GETPOST($key));
138  } elseif (substr($key, 0, 21) == 'multicurrency_amount_') {
139  $cursorfacid = substr($key, 21);
140  $multicurrency_amounts[$cursorfacid] = price2num(GETPOST($key));
141  $multicurrency_totalpayment += floatval($multicurrency_amounts[$cursorfacid]);
142  if (!empty($multicurrency_amounts[$cursorfacid])) {
143  $atleastonepaymentnotnull++;
144  }
145  $result = $tmpinvoice->fetch($cursorfacid);
146  if ($result <= 0) {
147  dol_print_error($db);
148  }
149  $multicurrency_amountsresttopay[$cursorfacid] = price2num($tmpinvoice->multicurrency_total_ttc - $tmpinvoice->getSommePaiement(1));
150  if ($multicurrency_amounts[$cursorfacid]) {
151  // Check amount
152  if ($multicurrency_amounts[$cursorfacid] && (abs($multicurrency_amounts[$cursorfacid]) > abs($multicurrency_amountsresttopay[$cursorfacid]))) {
153  $addwarning = 1;
154  $formquestion['text'] = img_warning($langs->trans("PaymentHigherThanReminderToPay")).' '.$langs->trans("HelpPaymentHigherThanReminderToPay");
155  }
156  // Check date
157  if ($datepaye && ($datepaye < $tmpinvoice->date)) {
158  $langs->load("errors");
159  //$error++;
160  setEventMessages($langs->transnoentities("WarningPaymentDateLowerThanInvoiceDate", dol_print_date($datepaye, 'day'), dol_print_date($tmpinvoice->date, 'day'), $tmpinvoice->ref), null, 'warnings');
161  }
162  }
163 
164  $formquestion[$i++] = array('type' => 'hidden', 'name' => $key, 'value' => GETPOST($key, 'int'));
165  }
166  }
167 
168  // Check parameters
169  if (!GETPOST('paiementcode')) {
170  setEventMessages($langs->transnoentities('ErrorFieldRequired', $langs->transnoentities('PaymentMode')), null, 'errors');
171  $error++;
172  }
173 
174  if (isModEnabled("banque")) {
175  // If bank module is on, account is required to enter a payment
176  if (GETPOST('accountid') <= 0) {
177  setEventMessages($langs->transnoentities('ErrorFieldRequired', $langs->transnoentities('AccountToCredit')), null, 'errors');
178  $error++;
179  }
180  }
181 
182  if (empty($totalpayment) && empty($multicurrency_totalpayment) && empty($atleastonepaymentnotnull)) {
183  setEventMessages($langs->transnoentities('ErrorFieldRequired', $langs->trans('PaymentAmount')), null, 'errors');
184  $error++;
185  }
186 
187  if (empty($datepaye)) {
188  setEventMessages($langs->transnoentities('ErrorFieldRequired', $langs->transnoentities('Date')), null, 'errors');
189  $error++;
190  }
191 
192  // Check if payments in both currency
193  if ($totalpayment > 0 && $multicurrency_totalpayment > 0) {
194  $langs->load("errors");
195  setEventMessages($langs->transnoentities('ErrorPaymentInBothCurrency'), null, 'errors');
196  $error++;
197  }
198  }
199 
200  /*
201  * Action add_paiement
202  */
203  if ($action == 'add_paiement') {
204  if ($error) {
205  $action = 'create';
206  }
207  // The next of this action is displayed at the page's bottom.
208  }
209 
210  /*
211  * Action confirm_paiement
212  */
213  if ($action == 'confirm_paiement' && $confirm == 'yes' && $usercanissuepayment) {
214  $error = 0;
215 
216  $datepaye = dol_mktime(12, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int'), 'tzuser');
217 
218  $db->begin();
219 
220  $thirdparty = new Societe($db);
221  if ($socid > 0) {
222  $thirdparty->fetch($socid);
223  }
224 
225  $multicurrency_code = array();
226 
227  // Clean parameters amount if payment is for a credit note
228  foreach ($amounts as $key => $value) { // How payment is dispatched
229  $tmpinvoice = new Facture($db);
230  $tmpinvoice->fetch($key);
231  if ($tmpinvoice->type == Facture::TYPE_CREDIT_NOTE) {
232  $newvalue = price2num($value, 'MT');
233  $amounts[$key] = - abs($newvalue);
234  }
235  $multicurrency_code[$key] = $tmpinvoice->multicurrency_code;
236  }
237 
238  foreach ($multicurrency_amounts as $key => $value) { // How payment is dispatched
239  $tmpinvoice = new Facture($db);
240  $tmpinvoice->fetch($key);
241  if ($tmpinvoice->type == Facture::TYPE_CREDIT_NOTE) {
242  $newvalue = price2num($value, 'MT');
243  $multicurrency_amounts[$key] = - abs($newvalue);
244  }
245  $multicurrency_code[$key] = $tmpinvoice->multicurrency_code;
246  }
247 
248  if (isModEnabled("banque")) {
249  // If the bank module is active, an account is required to input a payment
250  if (GETPOST('accountid', 'int') <= 0) {
251  setEventMessages($langs->trans('ErrorFieldRequired', $langs->transnoentities('AccountToCredit')), null, 'errors');
252  $error++;
253  }
254  }
255 
256  // Creation of payment line
257  $paiement = new Paiement($db);
258  $paiement->datepaye = $datepaye;
259  $paiement->amounts = $amounts; // Array with all payments dispatching with invoice id
260  $paiement->multicurrency_amounts = $multicurrency_amounts; // Array with all payments dispatching
261  $paiement->multicurrency_code = $multicurrency_code; // Array with all currency of payments dispatching
262  $paiement->paiementid = dol_getIdFromCode($db, GETPOST('paiementcode'), 'c_paiement', 'code', 'id', 1);
263  $paiement->num_payment = GETPOST('num_paiement', 'alpha');
264  $paiement->note_private = GETPOST('comment', 'alpha');
265  $paiement->fk_account = GETPOST('accountid', 'int');
266 
267  if (!$error) {
268  // Create payment and update this->multicurrency_amounts if this->amounts filled or
269  // this->amounts if this->multicurrency_amounts filled.
270  // This also set ->amount and ->multicurrency_amount
271  $paiement_id = $paiement->create($user, (GETPOST('closepaidinvoices') == 'on' ? 1 : 0), $thirdparty); // This include closing invoices and regenerating documents
272  if ($paiement_id < 0) {
273  setEventMessages($paiement->error, $paiement->errors, 'errors');
274  $error++;
275  }
276  }
277 
278  if (!$error) {
279  $label = '(CustomerInvoicePayment)';
280  if (GETPOST('type') == Facture::TYPE_CREDIT_NOTE) {
281  $label = '(CustomerInvoicePaymentBack)'; // Refund of a credit note
282  }
283  $result = $paiement->addPaymentToBank($user, 'payment', $label, GETPOST('accountid', 'int'), GETPOST('chqemetteur'), GETPOST('chqbank'));
284  if ($result < 0) {
285  setEventMessages($paiement->error, $paiement->errors, 'errors');
286  $error++;
287  }
288  }
289 
290  if (!$error) {
291  $db->commit();
292 
293  // If payment dispatching on more than one invoice, we stay on summary page, otherwise jump on invoice card
294  $invoiceid = 0;
295  foreach ($paiement->amounts as $key => $amount) {
296  $facid = $key;
297  if (is_numeric($amount) && $amount <> 0) {
298  if ($invoiceid != 0) {
299  $invoiceid = -1; // There is more than one invoice payed by this payment
300  } else {
301  $invoiceid = $facid;
302  }
303  }
304  }
305  if ($invoiceid > 0) {
306  $loc = DOL_URL_ROOT.'/compta/facture/card.php?facid='.$invoiceid;
307  } else {
308  $loc = DOL_URL_ROOT.'/compta/paiement/card.php?id='.$paiement_id;
309  }
310  header('Location: '.$loc);
311  exit;
312  } else {
313  $db->rollback();
314  }
315  }
316 }
317 
318 
319 /*
320  * View
321  */
322 
323 $form = new Form($db);
324 
325 
326 llxHeader('', $langs->trans("Payment"));
327 
328 
329 
330 if ($action == 'create' || $action == 'confirm_paiement' || $action == 'add_paiement') {
331  $facture = new Facture($db);
332  $result = $facture->fetch($facid);
333 
334  if ($result >= 0) {
335  $facture->fetch_thirdparty();
336 
337  $title = '';
338  if ($facture->type != Facture::TYPE_CREDIT_NOTE) {
339  $title .= $langs->trans("EnterPaymentReceivedFromCustomer");
340  }
341  if ($facture->type == Facture::TYPE_CREDIT_NOTE) {
342  $title .= $langs->trans("EnterPaymentDueToCustomer");
343  }
344  print load_fiche_titre($title);
345 
346  // Initialize data for confirmation (this is used because data can be change during confirmation)
347  if ($action == 'add_paiement') {
348  $i = 0;
349 
350  $formquestion[$i++] = array('type' => 'hidden', 'name' => 'facid', 'value' => $facture->id);
351  $formquestion[$i++] = array('type' => 'hidden', 'name' => 'socid', 'value' => $facture->socid);
352  $formquestion[$i++] = array('type' => 'hidden', 'name' => 'type', 'value' => $facture->type);
353  }
354 
355  // Invoice with Paypal transaction
356  // TODO add hook here
357  if (!empty($conf->paypalplus->enabled) && $conf->global->PAYPAL_ENABLE_TRANSACTION_MANAGEMENT && !empty($facture->ref_ext)) {
358  if (!empty($conf->global->PAYPAL_BANK_ACCOUNT)) {
359  $accountid = $conf->global->PAYPAL_BANK_ACCOUNT;
360  }
361  $paymentnum = $facture->ref_ext;
362  }
363 
364  // Add realtime total information
365  if (!empty($conf->use_javascript_ajax)) {
366  print "\n".'<script type="text/javascript">';
367  print '$(document).ready(function () {
368  setPaiementCode();
369 
370  $("#selectpaiementcode").change(function() {
371  setPaiementCode();
372  });
373 
374  function setPaiementCode()
375  {
376  var code = $("#selectpaiementcode option:selected").val();
377  console.log("setPaiementCode code="+code);
378 
379  if (code == \'CHQ\' || code == \'VIR\')
380  {
381  if (code == \'CHQ\')
382  {
383  $(\'.fieldrequireddyn\').addClass(\'fieldrequired\');
384  }
385  if ($(\'#fieldchqemetteur\').val() == \'\')
386  {
387  var emetteur = ('.$facture->type.' == '.Facture::TYPE_CREDIT_NOTE.') ? \''.dol_escape_js(dol_escape_htmltag($conf->global->MAIN_INFO_SOCIETE_NOM)).'\' : jQuery(\'#thirdpartylabel\').val();
388  $(\'#fieldchqemetteur\').val(emetteur);
389  }
390  }
391  else
392  {
393  $(\'.fieldrequireddyn\').removeClass(\'fieldrequired\');
394  $(\'#fieldchqemetteur\').val(\'\');
395  }
396  }
397 
398  function _elemToJson(selector)
399  {
400  var subJson = {};
401  $.map(selector.serializeArray(), function(n,i)
402  {
403  subJson[n["name"]] = n["value"];
404  });
405 
406  return subJson;
407  }
408  function callForResult(imgId)
409  {
410  var json = {};
411  var form = $("#payment_form");
412 
413  json["invoice_type"] = $("#invoice_type").val();
414  json["amountPayment"] = $("#amountpayment").attr("value");
415  json["amounts"] = _elemToJson(form.find("input.amount"));
416  json["remains"] = _elemToJson(form.find("input.remain"));
417  json["token"] = "'.currentToken().'";
418  if (imgId != null) {
419  json["imgClicked"] = imgId;
420  }
421 
422  $.post("'.DOL_URL_ROOT.'/compta/ajaxpayment.php", json, function(data)
423  {
424  json = $.parseJSON(data);
425 
426  form.data(json);
427 
428  for (var key in json)
429  {
430  if (key == "result") {
431  if (json["makeRed"]) {
432  $("#"+key).addClass("error");
433  } else {
434  $("#"+key).removeClass("error");
435  }
436  json[key]=json["label"]+" "+json[key];
437  $("#"+key).text(json[key]);
438  } else {console.log(key);
439  form.find("input[name*=\""+key+"\"]").each(function() {
440  $(this).attr("value", json[key]);
441  });
442  }
443  }
444  });
445  }
446  $("#payment_form").find("input.amount").change(function() {
447  callForResult();
448  });
449  $("#payment_form").find("input.amount").keyup(function() {
450  callForResult();
451  });
452  ';
453 
454  print ' });'."\n";
455 
456  //Add js for AutoFill
457  print ' $(document).ready(function () {';
458  print ' $(".AutoFillAmout").on(\'click touchstart\', function(){
459  $("input[name="+$(this).data(\'rowname\')+"]").val($(this).data("value")).trigger("change");
460  });';
461  print ' });'."\n";
462 
463  print ' </script>'."\n";
464  }
465 
466  print '<form id="payment_form" name="add_paiement" action="'.$_SERVER["PHP_SELF"].'" method="POST">';
467  print '<input type="hidden" name="token" value="'.newToken().'">';
468  print '<input type="hidden" name="action" value="add_paiement">';
469  print '<input type="hidden" name="facid" value="'.$facture->id.'">';
470  print '<input type="hidden" name="socid" value="'.$facture->socid.'">';
471  print '<input type="hidden" name="type" id="invoice_type" value="'.$facture->type.'">';
472  print '<input type="hidden" name="thirdpartylabel" id="thirdpartylabel" value="'.dol_escape_htmltag($facture->thirdparty->name).'">';
473 
474  print dol_get_fiche_head();
475 
476  print '<table class="border centpercent">';
477 
478  // Third party
479  print '<tr><td class="titlefieldcreate"><span class="fieldrequired">'.$langs->trans('Company').'</span></td><td>'.$facture->thirdparty->getNomUrl(4)."</td></tr>\n";
480 
481  // Date payment
482  print '<tr><td><span class="fieldrequired">'.$langs->trans('Date').'</span></td><td>';
483  $datepayment = dol_mktime(12, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int'));
484  $datepayment = ($datepayment == '' ? (empty($conf->global->MAIN_AUTOFILL_DATE) ?-1 : '') : $datepayment);
485  print $form->selectDate($datepayment, '', '', '', 0, "add_paiement", 1, 1, 0, '', '', $facture->date);
486  print '</td></tr>';
487 
488  // Payment mode
489  print '<tr><td><span class="fieldrequired">'.$langs->trans('PaymentMode').'</span></td><td>';
490  $form->select_types_paiements((GETPOST('paiementcode') ?GETPOST('paiementcode') : $facture->mode_reglement_code), 'paiementcode', '', 2);
491  print "</td>\n";
492  print '</tr>';
493 
494  // Bank account
495  print '<tr>';
496  if (isModEnabled("banque")) {
497  if ($facture->type != 2) {
498  print '<td><span class="fieldrequired">'.$langs->trans('AccountToCredit').'</span></td>';
499  }
500  if ($facture->type == 2) {
501  print '<td><span class="fieldrequired">'.$langs->trans('AccountToDebit').'</span></td>';
502  }
503 
504  print '<td>';
505  print img_picto('', 'bank_account');
506  print $form->select_comptes($accountid, 'accountid', 0, '', 2, '', 0, 'widthcentpercentminusx maxwidth500', 1);
507  print '</td>';
508  } else {
509  print '<td>&nbsp;</td>';
510  }
511  print "</tr>\n";
512 
513  // Bank check number
514  print '<tr><td>'.$langs->trans('Numero');
515  print ' <em class="opacitymedium">('.$langs->trans("ChequeOrTransferNumber").')</em>';
516  print '</td>';
517  print '<td><input name="num_paiement" type="text" class="maxwidth200" value="'.$paymentnum.'"></td></tr>';
518 
519  // Check transmitter
520  print '<tr><td class="'.(GETPOST('paiementcode') == 'CHQ' ? 'fieldrequired ' : '').'fieldrequireddyn">'.$langs->trans('CheckTransmitter');
521  print ' <em class="opacitymedium">('.$langs->trans("ChequeMaker").')</em>';
522  print '</td>';
523  print '<td><input id="fieldchqemetteur" class="maxwidth300" name="chqemetteur" type="text" value="'.GETPOST('chqemetteur', 'alphanohtml').'"></td></tr>';
524 
525  // Bank name
526  print '<tr><td>'.$langs->trans('Bank');
527  print ' <em class="opacitymedium">('.$langs->trans("ChequeBank").')</em>';
528  print '</td>';
529  print '<td><input name="chqbank" class="maxwidth300" type="text" value="'.GETPOST('chqbank', 'alphanohtml').'"></td></tr>';
530 
531  // Comments
532  print '<tr><td>'.$langs->trans('Comments').'</td>';
533  print '<td class="tdtop">';
534  print '<textarea name="comment" wrap="soft" class="quatrevingtpercent" rows="'.ROWS_3.'">'.GETPOST('comment', 'restricthtml').'</textarea>';
535  print '</td></tr>';
536 
537  print '</table>';
538 
539  print dol_get_fiche_end();
540 
541 
542  /*
543  * List of unpaid invoices
544  */
545 
546  $sql = 'SELECT f.rowid as facid, f.ref, f.total_ttc, f.multicurrency_code, f.multicurrency_total_ttc, f.type,';
547  $sql .= ' f.datef as df, f.fk_soc as socid, f.date_lim_reglement as dlr';
548  $sql .= ' FROM '.MAIN_DB_PREFIX.'facture as f';
549  $sql .= ' WHERE f.entity IN ('.getEntity('facture').')';
550  $sql .= ' AND (f.fk_soc = '.((int) $facture->socid);
551  // Can pay invoices of all child of parent company
552  if (!empty($conf->global->FACTURE_PAYMENTS_ON_DIFFERENT_THIRDPARTIES_BILLS) && !empty($facture->thirdparty->parent)) {
553  $sql .= ' OR f.fk_soc IN (SELECT rowid FROM '.MAIN_DB_PREFIX.'societe WHERE parent = '.((int) $facture->thirdparty->parent).')';
554  }
555  // Can pay invoices of all child of myself
556  if (!empty($conf->global->FACTURE_PAYMENTS_ON_SUBSIDIARY_COMPANIES)) {
557  $sql .= ' OR f.fk_soc IN (SELECT rowid FROM '.MAIN_DB_PREFIX.'societe WHERE parent = '.((int) $facture->thirdparty->id).')';
558  }
559  $sql .= ') AND f.paye = 0';
560  $sql .= ' AND f.fk_statut = 1'; // Statut=0 => not validated, Statut=2 => canceled
561  if ($facture->type != Facture::TYPE_CREDIT_NOTE) {
562  $sql .= ' AND type IN (0,1,3,5)'; // Standard invoice, replacement, deposit, situation
563  } else {
564  $sql .= ' AND type = 2'; // If paying back a credit note, we show all credit notes
565  }
566  // Sort invoices by date and serial number: the older one comes first
567  $sql .= ' ORDER BY f.datef ASC, f.ref ASC';
568 
569  $resql = $db->query($sql);
570  if ($resql) {
571  $num = $db->num_rows($resql);
572  if ($num > 0) {
573  $arraytitle = $langs->trans('Invoice');
574  if ($facture->type == 2) {
575  $arraytitle = $langs->trans("CreditNotes");
576  }
577  $alreadypayedlabel = $langs->trans('Received');
578  $multicurrencyalreadypayedlabel = $langs->trans('MulticurrencyReceived');
579  if ($facture->type == 2) {
580  $alreadypayedlabel = $langs->trans("PaidBack");
581  $multicurrencyalreadypayedlabel = $langs->trans("MulticurrencyPaidBack");
582  }
583  $remaindertopay = $langs->trans('RemainderToTake');
584  $multicurrencyremaindertopay = $langs->trans('MulticurrencyRemainderToTake');
585  if ($facture->type == 2) {
586  $remaindertopay = $langs->trans("RemainderToPayBack");
587  $multicurrencyremaindertopay = $langs->trans("MulticurrencyRemainderToPayBack");
588  }
589 
590  $i = 0;
591  //print '<tr><td colspan="3">';
592  print '<br>';
593 
594  print '<div class="div-table-responsive-no-min">'; // You can use div-table-responsive-no-min if you dont need reserved height for your table
595  print '<table class="noborder centpercent">';
596 
597  print '<tr class="liste_titre">';
598  print '<td>'.$arraytitle.'</td>';
599  print '<td class="center">'.$langs->trans('Date').'</td>';
600  print '<td class="center">'.$langs->trans('DateMaxPayment').'</td>';
601  if (isModEnabled('multicurrency')) {
602  print '<td>'.$langs->trans('Currency').'</td>';
603  print '<td class="right">'.$langs->trans('MulticurrencyAmountTTC').'</td>';
604  print '<td class="right">'.$multicurrencyalreadypayedlabel.'</td>';
605  print '<td class="right">'.$multicurrencyremaindertopay.'</td>';
606  print '<td class="right">'.$langs->trans('MulticurrencyPaymentAmount').'</td>';
607  }
608  print '<td class="right">'.$langs->trans('AmountTTC').'</td>';
609  print '<td class="right">'.$alreadypayedlabel.'</td>';
610  print '<td class="right">'.$remaindertopay.'</td>';
611  print '<td class="right">'.$langs->trans('PaymentAmount').'</td>';
612 
613  $parameters = array();
614  $reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters, $facture, $action); // Note that $action and $object may have been modified by hook
615 
616  print '<td align="right">&nbsp;</td>';
617  print "</tr>\n";
618 
619  $total_ttc = 0;
620  $totalrecu = 0;
621  $totalrecucreditnote = 0;
622  $totalrecudeposits = 0;
623 
624  while ($i < $num) {
625  $objp = $db->fetch_object($resql);
626 
627  $sign = 1;
628  if ($facture->type == Facture::TYPE_CREDIT_NOTE) {
629  $sign = -1;
630  }
631 
632  $soc = new Societe($db);
633  $soc->fetch($objp->socid);
634 
635  $invoice = new Facture($db);
636  $invoice->fetch($objp->facid);
637  $paiement = $invoice->getSommePaiement();
638  $creditnotes = $invoice->getSumCreditNotesUsed();
639  $deposits = $invoice->getSumDepositsUsed();
640  $alreadypayed = price2num($paiement + $creditnotes + $deposits, 'MT');
641  $remaintopay = price2num($invoice->total_ttc - $paiement - $creditnotes - $deposits, 'MT');
642 
643  // Multicurrency Price
644  if (isModEnabled('multicurrency')) {
645  $multicurrency_payment = $invoice->getSommePaiement(1);
646  $multicurrency_creditnotes = $invoice->getSumCreditNotesUsed(1);
647  $multicurrency_deposits = $invoice->getSumDepositsUsed(1);
648  $multicurrency_alreadypayed = price2num($multicurrency_payment + $multicurrency_creditnotes + $multicurrency_deposits, 'MT');
649  $multicurrency_remaintopay = price2num($invoice->multicurrency_total_ttc - $multicurrency_payment - $multicurrency_creditnotes - $multicurrency_deposits, 'MT');
650  }
651 
652 
653  print '<tr class="oddeven'.(($invoice->id == $facid) ? ' highlight' : '').'">';
654 
655  print '<td class="nowraponall">';
656  print $invoice->getNomUrl(1, '');
657  if ($objp->socid != $facture->thirdparty->id) {
658  print ' - '.$soc->getNomUrl(1).' ';
659  }
660  print "</td>\n";
661 
662  // Date
663  print '<td class="center">'.dol_print_date($db->jdate($objp->df), 'day')."</td>\n";
664 
665  // Due date
666  if ($objp->dlr > 0) {
667  print '<td class="nowraponall center">';
668  print dol_print_date($db->jdate($objp->dlr), 'day');
669 
670  if ($invoice->hasDelay()) {
671  print img_warning($langs->trans('Late'));
672  }
673 
674  print '</td>';
675  } else {
676  print '<td align="center"></td>';
677  }
678 
679  // Currency
680  if (isModEnabled('multicurrency')) {
681  print '<td class="center">'.$objp->multicurrency_code."</td>\n";
682  }
683 
684  // Multicurrency Price
685  if (isModEnabled('multicurrency')) {
686  print '<td class="right">';
687  if ($objp->multicurrency_code && $objp->multicurrency_code != $conf->currency) {
688  print price($sign * $objp->multicurrency_total_ttc);
689  }
690  print '</td>';
691 
692  // Multicurrency Price
693  print '<td class="right">';
694  if ($objp->multicurrency_code && $objp->multicurrency_code != $conf->currency) {
695  print price($sign * $multicurrency_payment);
696  if ($multicurrency_creditnotes) {
697  print '+'.price($multicurrency_creditnotes);
698  }
699  if ($multicurrency_deposits) {
700  print '+'.price($multicurrency_deposits);
701  }
702  }
703  print '</td>';
704 
705  // Multicurrency remain to pay
706  print '<td class="right">';
707  if ($objp->multicurrency_code && $objp->multicurrency_code != $conf->currency) {
708  print price($sign * $multicurrency_remaintopay);
709  }
710  print '</td>';
711 
712  print '<td class="right nowraponall">';
713 
714  // Add remind multicurrency amount
715  $namef = 'multicurrency_amount_'.$objp->facid;
716  $nameRemain = 'multicurrency_remain_'.$objp->facid;
717 
718  if ($objp->multicurrency_code && $objp->multicurrency_code != $conf->currency) {
719  if ($action != 'add_paiement') {
720  if (!empty($conf->use_javascript_ajax)) {
721  print img_picto("Auto fill", 'rightarrow', "class='AutoFillAmout' data-rowname='".$namef."' data-value='".($sign * $multicurrency_remaintopay)."'");
722  }
723  print '<input type="text" class="maxwidth75 multicurrency_amount" name="'.$namef.'" value="'.GETPOST($namef).'">';
724  print '<input type="hidden" class="multicurrency_remain" name="'.$nameRemain.'" value="'.$multicurrency_remaintopay.'">';
725  } else {
726  print '<input type="text" class="maxwidth75" name="'.$namef.'_disabled" value="'.GETPOST($namef).'" disabled>';
727  print '<input type="hidden" name="'.$namef.'" value="'.GETPOST($namef).'">';
728  }
729  }
730  print "</td>";
731  }
732 
733  // Price
734  print '<td class="right"><span class="amount">'.price($sign * $objp->total_ttc).'</span></td>';
735 
736  // Received + already paid
737  print '<td class="right"><span class="amount">'.price($sign * $paiement);
738  if ($creditnotes) {
739  print '<span class="opacitymedium">+'.price($creditnotes).'</span>';
740  }
741  if ($deposits) {
742  print '<span class="opacitymedium">+'.price($deposits).'</span>';
743  }
744  print '</span></td>';
745 
746  // Remain to take or to pay back
747  print '<td class="right">';
748  print price($sign * $remaintopay);
749  if (!empty($conf->prelevement->enabled)) {
750  $numdirectdebitopen = 0;
751  $totaldirectdebit = 0;
752  $sql = "SELECT COUNT(pfd.rowid) as nb, SUM(pfd.amount) as amount";
753  $sql .= " FROM ".MAIN_DB_PREFIX."prelevement_demande as pfd";
754  $sql .= " WHERE fk_facture = ".((int) $objp->facid);
755  $sql .= " AND pfd.traite = 0";
756  $sql .= " AND pfd.ext_payment_id IS NULL";
757 
758  $result_sql = $db->query($sql);
759  if ($result_sql) {
760  $obj = $db->fetch_object($result_sql);
761  $numdirectdebitopen = $obj->nb;
762  $totaldirectdebit = $obj->amount;
763  } else {
764  dol_print_error($db);
765  }
766  if ($numdirectdebitopen) {
767  $langs->load("withdrawals");
768  print img_warning($langs->trans("WarningSomeDirectDebitOrdersAlreadyExists", $numdirectdebitopen, price(price2num($totaldirectdebit, 'MT'), 0, $langs, 1, -1, -1, $conf->currency)), '', 'classfortooltip');
769  }
770  }
771  print '</td>';
772  //$test= price(price2num($objp->total_ttc - $paiement - $creditnotes - $deposits));
773 
774  // Amount
775  print '<td class="right nowraponall">';
776 
777  // Add remind amount
778  $namef = 'amount_'.$objp->facid;
779  $nameRemain = 'remain_'.$objp->facid;
780 
781  if ($action != 'add_paiement') {
782  if (!empty($conf->use_javascript_ajax)) {
783  print img_picto("Auto fill", 'rightarrow', "class='AutoFillAmout' data-rowname='".$namef."' data-value='".($sign * $remaintopay)."'");
784  }
785  print '<input type="text" class="maxwidth75 amount" name="'.$namef.'" value="'.dol_escape_htmltag(GETPOST($namef)).'">';
786  print '<input type="hidden" class="remain" name="'.$nameRemain.'" value="'.$remaintopay.'">';
787  } else {
788  print '<input type="text" class="maxwidth75" name="'.$namef.'_disabled" value="'.dol_escape_htmltag(GETPOST($namef)).'" disabled>';
789  print '<input type="hidden" name="'.$namef.'" value="'.dol_escape_htmltag(GETPOST($namef)).'">';
790  }
791  print "</td>";
792 
793  $parameters = array();
794  $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters, $objp, $action); // Note that $action and $object may have been modified by hook
795 
796  // Warning
797  print '<td align="center" width="16">';
798  //print "xx".$amounts[$invoice->id]."-".$amountsresttopay[$invoice->id]."<br>";
799  if (!empty($amounts[$invoice->id]) && (abs($amounts[$invoice->id]) > abs($amountsresttopay[$invoice->id]))
800  || !empty($multicurrency_amounts[$invoice->id]) && (abs($multicurrency_amounts[$invoice->id]) > abs($multicurrency_amountsresttopay[$invoice->id]))) {
801  print ' '.img_warning($langs->trans("PaymentHigherThanReminderToPay"));
802  }
803  print '</td>';
804 
805  print "</tr>\n";
806 
807  $total_ttc += $objp->total_ttc;
808  $totalrecu += $paiement;
809  $totalrecucreditnote += $creditnotes;
810  $totalrecudeposits += $deposits;
811  $i++;
812  }
813 
814  if ($i > 1) {
815  // Print total
816  print '<tr class="liste_total">';
817  print '<td colspan="3" class="left">'.$langs->trans('TotalTTC').'</td>';
818  if (isModEnabled('multicurrency')) {
819  print '<td></td>';
820  print '<td></td>';
821  print '<td></td>';
822  print '<td></td>';
823  print '<td class="right" id="multicurrency_result" style="font-weight: bold;"></td>';
824  }
825  print '<td class="right"><b>'.price($sign * $total_ttc).'</b></td>';
826  print '<td class="right"><b>'.price($sign * $totalrecu);
827  if ($totalrecucreditnote) {
828  print '+'.price($totalrecucreditnote);
829  }
830  if ($totalrecudeposits) {
831  print '+'.price($totalrecudeposits);
832  }
833  print '</b></td>';
834  print '<td class="right"><b>'.price($sign * price2num($total_ttc - $totalrecu - $totalrecucreditnote - $totalrecudeposits, 'MT')).'</b></td>';
835  print '<td class="right" id="result" style="font-weight: bold;"></td>'; // Autofilled
836  print '<td align="center">&nbsp;</td>';
837  print "</tr>\n";
838  }
839  print "</table>";
840  print "</div>\n";
841  }
842  $db->free($resql);
843  } else {
844  dol_print_error($db);
845  }
846 
847  $formconfirm = '';
848 
849  // Save button
850  if ($action != 'add_paiement') {
851  $checkboxlabel = $langs->trans("ClosePaidInvoicesAutomatically");
852  if ($facture->type == Facture::TYPE_CREDIT_NOTE) {
853  $checkboxlabel = $langs->trans("ClosePaidCreditNotesAutomatically");
854  }
855  $buttontitle = $langs->trans('ToMakePayment');
856  if ($facture->type == Facture::TYPE_CREDIT_NOTE) {
857  $buttontitle = $langs->trans('ToMakePaymentBack');
858  }
859 
860  print '<br><div class="center">';
861  print '<input type="checkbox" checked name="closepaidinvoices"> '.$checkboxlabel;
862  /*if (!empty($conf->prelevement->enabled))
863  {
864  $langs->load("withdrawals");
865  if (!empty($conf->global->WITHDRAW_DISABLE_AUTOCREATE_ONPAYMENTS)) print '<br>'.$langs->trans("IfInvoiceNeedOnWithdrawPaymentWontBeClosed");
866  }*/
867  print '<br><input type="submit" class="button" value="'.dol_escape_htmltag($buttontitle).'"><br><br>';
868  print '</div>';
869  }
870 
871  // Form to confirm payment
872  if ($action == 'add_paiement') {
873  $preselectedchoice = $addwarning ? 'no' : 'yes';
874 
875  print '<br>';
876  if (!empty($totalpayment)) {
877  $text = $langs->trans('ConfirmCustomerPayment', $totalpayment, $langs->trans("Currency".$conf->currency));
878  }
879  if (!empty($multicurrency_totalpayment)) {
880  $text .= '<br>'.$langs->trans('ConfirmCustomerPayment', $multicurrency_totalpayment, $langs->trans("paymentInInvoiceCurrency"));
881  }
882  if (GETPOST('closepaidinvoices')) {
883  $text .= '<br>'.$langs->trans("AllCompletelyPayedInvoiceWillBeClosed");
884  print '<input type="hidden" name="closepaidinvoices" value="'.GETPOST('closepaidinvoices').'">';
885  }
886  $formconfirm = $form->formconfirm($_SERVER['PHP_SELF'].'?facid='.$facture->id.'&socid='.$facture->socid.'&type='.$facture->type, $langs->trans('ReceivedCustomersPayments'), $text, 'confirm_paiement', $formquestion, $preselectedchoice);
887  }
888 
889  // Call Hook formConfirm
890  $parameters = array('formConfirm' => $formconfirm);
891  $reshook = $hookmanager->executeHooks('formConfirm', $parameters, $object, $action); // Note that $action and $object may have been modified by hook
892  if (empty($reshook)) {
893  $formconfirm .= $hookmanager->resPrint;
894  } elseif ($reshook > 0) {
895  $formconfirm = $hookmanager->resPrint;
896  }
897 
898  // Print form confirm
899  print $formconfirm;
900 
901  print "</form>\n";
902  }
903 }
904 
905 
909 if (!GETPOST('action', 'aZ09')) {
910  if (empty($page) || $page == -1) {
911  $page = 0;
912  }
913  $limit = GETPOST('limit', 'int') ?GETPOST('limit', 'int') : $conf->liste_limit;
914  $offset = $limit * $page;
915 
916  if (!$sortorder) {
917  $sortorder = 'DESC';
918  }
919  if (!$sortfield) {
920  $sortfield = 'p.datep';
921  }
922 
923  $sql = 'SELECT p.datep as dp, p.amount, f.total_ttc as fa_amount, f.ref';
924  $sql .= ', f.rowid as facid, c.libelle as paiement_type, p.num_paiement as num_payment';
925  $sql .= ' FROM '.MAIN_DB_PREFIX.'paiement as p LEFT JOIN '.MAIN_DB_PREFIX.'c_paiement as c ON p.fk_paiement = c.id';
926  $sql .= ', '.MAIN_DB_PREFIX.'facture as f';
927  $sql .= ' WHERE p.fk_facture = f.rowid';
928  $sql .= ' AND f.entity IN ('.getEntity('invoice').')';
929  if ($socid) {
930  $sql .= ' AND f.fk_soc = '.((int) $socid);
931  }
932 
933  $sql .= $db->order($sortfield, $sortorder);
934  $sql .= $db->plimit($limit + 1, $offset);
935  $resql = $db->query($sql);
936 
937  if ($resql) {
938  $num = $db->num_rows($resql);
939  $i = 0;
940 
941  print_barre_liste($langs->trans('Payments'), $page, $_SERVER["PHP_SELF"], '', $sortfield, $sortorder, '', $num);
942  print '<table class="noborder centpercent">';
943  print '<tr class="liste_titre">';
944  print_liste_field_titre('Invoice', $_SERVER["PHP_SELF"], 'f.ref', '', '', '', $sortfield, $sortorder);
945  print_liste_field_titre('Date', $_SERVER["PHP_SELF"], 'p.datep', '', '', '', $sortfield, $sortorder);
946  print_liste_field_titre('Type', $_SERVER["PHP_SELF"], 'c.libelle', '', '', '', $sortfield, $sortorder);
947  print_liste_field_titre('Amount', $_SERVER["PHP_SELF"], 'p.amount', '', '', '', $sortfield, $sortorder, 'right ');
948  print_liste_field_titre('', $_SERVER["PHP_SELF"], "", '', '', '', $sortfield, $sortorder, 'maxwidthsearch ');
949  print "</tr>\n";
950 
951  while ($i < min($num, $limit)) {
952  $objp = $db->fetch_object($resql);
953 
954  print '<tr class="oddeven">';
955  print '<td><a href="'.DOL_URL_ROOT.'/compta/facture/card.php?facid='.$objp->facid.'">'.$objp->ref."</a></td>\n";
956  print '<td>'.dol_print_date($db->jdate($objp->dp))."</td>\n";
957  print '<td>'.$objp->paiement_type.' '.$objp->num_payment."</td>\n";
958  print '<td class="right"><span class="amount">'.price($objp->amount).'</span></td>';
959  print '<td>&nbsp;</td>';
960  print '</tr>';
961 
962  $parameters = array();
963  $reshook = $hookmanager->executeHooks('printObjectLine', $parameters, $objp, $action); // Note that $action and $object may have been modified by hook
964 
965  $i++;
966  }
967  print '</table>';
968  }
969 }
970 
971 llxFooter();
972 
973 $db->close();
if(!defined('NOREQUIRESOC')) if(!defined('NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined('NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined('NOREQUIREAJAX')) llxHeader()
Empty header.
Definition: wrapper.php:56
llxFooter()
Empty footer.
Definition: wrapper.php:70
Class to manage invoices.
const STATUS_DRAFT
Draft status.
const TYPE_CREDIT_NOTE
Credit note invoice.
Class to manage generation of HTML components Only common components must be here.
Class to manage payments of customer invoices.
Class to manage third parties objects (customers, suppliers, prospects...)
if(isModEnabled('facture') &&!empty($user->rights->facture->lire)) if((isModEnabled('fournisseur') &&empty($conf->global->MAIN_USE_NEW_SUPPLIERMOD) && $user->hasRight("fournisseur", "facture", "lire"))||(isModEnabled('supplier_invoice') && $user->hasRight("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->hasRight("commande", "lire") &&empty($conf->global->WORKFLOW_DISABLE_CREATE_INVOICE_FROM_ORDER)) $resql
Social contributions to pay.
Definition: index.php:745
if($cancel &&! $id) if($action=='add' &&! $cancel) if($action=='delete') if($id) $form
Actions.
Definition: card.php:143
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...
load_fiche_titre($titre, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='')
Show tabs of a record.
img_warning($titlealt='default', $moreatt='', $morecss='pictowarning')
Show warning logo.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0)
Returns text escaped for inclusion in HTML alt or title tags, or into values of HTML input fields.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_print_error($db='', $error='', $errors=null)
Displays error message system with all the information to facilitate the diagnosis and the escalation...
currentToken()
Return the value of token currently saved into session with name 'token'.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='')
Set event messages in dol_events session object.
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.
dol_print_date($time, $format='', $tzoutput='auto', $outputlangs='', $encodetooutput=false)
Output date in a string format according to outputlangs (or langs if not defined).
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=false, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2)
Show picto whatever it's its name (generic function)
dol_escape_js($stringtoescape, $mode=0, $noescapebackslashn=0)
Returns text escaped for inclusion into javascript code.
dol_getIdFromCode($db, $key, $tablename, $fieldkey='code', $fieldid='id', $entityfilter=0, $filters='')
Return an id or code from a code or id.
print_liste_field_titre($name, $file="", $field="", $begin="", $moreparam="", $moreattrib="", $sortfield="", $sortorder="", $prefix="", $tooltip="", $forcenowrapcolumntitle=0)
Show title line of an array.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
print_barre_liste($titre, $page, $file, $options='', $sortfield='', $sortorder='', $morehtmlcenter='', $num=-1, $totalnboflines='', $picto='generic', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limit=-1, $hideselectlimit=0, $hidenavigation=0, $pagenavastextinput=0, $morehtmlrightbeforearrow='')
Print a title with navigation controls for pagination.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
isModEnabled($module)
Is Dolibarr module enabled.
$formconfirm
if ($action == 'delbookkeepingyear') {
restrictedArea(User $user, $features, $object=0, $tableandshare='', $feature2='', $dbt_keyfield='fk_soc', $dbt_select='rowid', $isdraft=0, $mode=0)
Check permissions of a user to show a page and an object.