dolibarr 22.0.5
payment.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2014-2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
3 * Copyright (C) 2015-2025 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2020 Maxime DEMAREST <maxime@indelog.fr>
5 * Copyright (C) 2025 MDW <mdeweerd@users.noreply.github.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
27// Load Dolibarr environment
28require '../../main.inc.php';
29require_once DOL_DOCUMENT_ROOT.'/loan/class/loan.class.php';
30require_once DOL_DOCUMENT_ROOT.'/loan/class/loanschedule.class.php';
31require_once DOL_DOCUMENT_ROOT.'/loan/class/paymentloan.class.php';
32require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
33require_once DOL_DOCUMENT_ROOT.'/core/lib/loan.lib.php';
34
43$langs->loadLangs(array("bills", "loan"));
44
45$action = GETPOST('action', 'aZ09');
46$confirm = GETPOST('confirm', 'alpha');
47$cancel = GETPOST('cancel', 'alpha');
48
49$chid = GETPOSTINT('id');
50$datepaid = dol_mktime(12, 0, 0, GETPOSTINT('remonth'), GETPOSTINT('reday'), GETPOSTINT('reyear'));
51
52// Security check
53$socid = 0;
54if ($user->socid > 0) {
55 $socid = $user->socid;
56} elseif (GETPOSTISSET('socid')) {
57 $socid = GETPOSTINT('socid');
58}
59if (!$user->hasRight('loan', 'write')) {
61}
62
63$loan = new Loan($db);
64$loan->fetch($chid);
65
66$line_id = 0;
67$echance = 0;
68$amount_capital = 0;
69$amount_insurance = 0;
70$amount_interest = 0;
71
72$ls = new LoanSchedule($db);
73// grab all loanschedule
74$res = $ls->fetchAll($chid);
75if ($res > 0) {
76 foreach ($ls->lines as $l) {
77 $echance++; // Count term pos
78 // last unpaid term
79 if (empty($l->fk_bank)) {
80 $line_id = $l->id;
81 break;
82 } elseif ($line_id == $l->id) {
83 // If line_id provided, only count temp pos
84 break;
85 }
86 }
87}
88
89// Set current line with last unpaid line (only if schedule is used)
90if (!empty($line_id)) {
91 $line = new LoanSchedule($db);
92 $res = $line->fetch($line_id);
93 if ($res > 0) {
94 $amount_capital = price($line->amount_capital);
95 $amount_insurance = price($line->amount_insurance);
96 $amount_interest = price($line->amount_interest);
97 if (empty($datepaid)) {
98 $ts_temppaid = $line->datep;
99 }
100 }
101}
102
103$permissiontoadd = $user->hasRight('loan', 'write');
104
105
106/*
107 * Actions
108 */
109
110if ($action == 'add_payment' && $permissiontoadd) {
111 $error = 0;
112
113 if ($cancel) {
114 $loc = DOL_URL_ROOT.'/loan/card.php?id='.$chid;
115 header("Location: ".$loc);
116 exit;
117 }
118
119 if (!GETPOSTINT('paymenttype') > 0) {
120 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("PaymentMode")), null, 'errors');
121 $error++;
122 }
123 if ($datepaid == '') {
124 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("Date")), null, 'errors');
125 $error++;
126 }
127 if (isModEnabled("bank") && !GETPOSTINT('accountid') > 0) {
128 setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("AccountToCredit")), null, 'errors');
129 $error++;
130 }
131
132 if (!$error) {
133 $paymentid = 0;
134
135 $pay_amount_capital = (float) price2num(GETPOST('amount_capital'));
136 $pay_amount_insurance = (float) price2num(GETPOST('amount_insurance'));
137 // User can't set interest him self if schedule is set (else value in schedule can be incoherent)
138 if (!empty($line)) {
139 $pay_amount_interest = $line->amount_interest;
140 } else {
141 $pay_amount_interest = (float) price2num(GETPOST('amount_interest'));
142 }
143 $remaindertopay = (float) price2num(GETPOST('remaindertopay'));
144 $amount = (float) price2num($pay_amount_capital + $pay_amount_insurance + $pay_amount_interest, 'MT');
145
146 // This term is already paid
147 if (!empty($line) && !empty($line->fk_bank)) {
148 setEventMessages($langs->trans('TermPaidAllreadyPaid'), null, 'errors');
149 $error++;
150 }
151
152 if (empty($remaindertopay)) {
153 setEventMessages('Empty sumpaid', null, 'errors');
154 $error++;
155 }
156
157 if ($amount == 0) {
158 setEventMessages($langs->trans('ErrorNoPaymentDefined'), null, 'errors');
159 $error++;
160 }
161
162 if (!$error) {
163 $db->begin();
164
165 // Create a line of payments
166 $payment = new PaymentLoan($db);
167 $payment->chid = $chid;
168 $payment->datep = $datepaid;
169 $payment->label = $loan->label;
170 $payment->amount_capital = $pay_amount_capital;
171 $payment->amount_insurance = $pay_amount_insurance;
172 $payment->amount_interest = $pay_amount_interest;
173 $payment->fk_bank = GETPOSTINT('accountid');
174 $payment->paymenttype = GETPOSTINT('paymenttype');
175 $payment->num_payment = GETPOST('num_payment', 'alphanohtml');
176 $payment->note_private = GETPOST('note_private', 'restricthtml');
177 $payment->note_public = GETPOST('note_public', 'restricthtml');
178
179 if (!$error) {
180 $paymentid = $payment->create($user);
181 if ($paymentid < 0) {
182 setEventMessages($payment->error, $payment->errors, 'errors');
183 $error++;
184 }
185 }
186
187 if (!$error) {
188 // @phan-suppress-next-line PhanPluginSuspiciousParamOrder
189 $result = $payment->addPaymentToBank($user, $chid, 'payment_loan', '(LoanPayment)', $payment->fk_bank, '', '');
190 if (!($result > 0)) {
191 setEventMessages($payment->error, $payment->errors, 'errors');
192 $error++;
193 }
194 }
195
196 // Update loan schedule with payment value
197 if (!$error && !empty($line)) {
198 // If payment values are modified, recalculate schedule
199 if (($line->amount_capital != $pay_amount_capital) || ($line->amount_insurance != $pay_amount_insurance) || ($line->amount_interest != $pay_amount_interest)) {
200 $arr_term = loanCalcMonthlyPayment(($pay_amount_capital + $pay_amount_interest), $remaindertopay, ($loan->rate / 100), $echance, (int) $loan->nbterm);
201 foreach ($arr_term as $k => $v) {
202 // Update fk_bank for current line
203 if ($k == $echance) {
204 $ls->lines[$k - 1]->fk_bank = $payment->fk_bank;
205 $ls->lines[$k - 1]->fk_payment_loan = $payment->id;
206 }
207 $ls->lines[$k - 1]->amount_capital = ((float) price2num($v['mens'])) - $v['interet'];
208 $ls->lines[$k - 1]->amount_interest = $v['interet'];
209 $ls->lines[$k - 1]->tms = dol_now();
210 $ls->lines[$k - 1]->fk_user_modif = $user->id;
211 $result = $ls->lines[$k - 1]->update($user, 0);
212 if ($result < 1) {
213 setEventMessages(null, $ls->errors, 'errors');
214 $error++;
215 break;
216 }
217 }
218 } else { // Only add fk_bank bank to schedule line (mark as paid)
219 $line->fk_bank = $payment->fk_bank;
220 $line->fk_payment_loan = $payment->id;
221 $result = $line->update($user, 0);
222 if ($result < 1) {
223 setEventMessages(null, $line->errors, 'errors');
224 $error++;
225 }
226 }
227 }
228
229 if (!$error) {
230 $db->commit();
231 $loc = DOL_URL_ROOT.'/loan/card.php?id='.$chid;
232 header('Location: '.$loc);
233 exit;
234 } else {
235 $db->rollback();
236 }
237 }
238 }
239
240 $action = 'create';
241}
242
243
244/*
245 * View
246 */
247$form = new Form($db);
248
249$title = $langs->trans('Loans');
250$help_url = "EN:Module_Loan|FR:Module_Emprunt";
251
252llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'bodyforlist mod-loan page-payment-list');
253
254
255// Form to create loan's payment
256if ($action == 'create') {
257 $total = $loan->capital;
258 $sumpaid = 0;
259
260 print load_fiche_titre($langs->trans("DoPayment"));
261
262 $sql = "SELECT SUM(amount_capital) as total";
263 $sql .= " FROM ".MAIN_DB_PREFIX."payment_loan";
264 $sql .= " WHERE fk_loan = ".((int) $chid);
265 $resql = $db->query($sql);
266 if ($resql) {
267 $obj = $db->fetch_object($resql);
268 $sumpaid = $obj->total;
269 $db->free($resql);
270 }
271
272 print '<form name="add_payment" action="'.$_SERVER['PHP_SELF'].'" method="post">';
273 print '<input type="hidden" name="token" value="'.newToken().'">';
274 print '<input type="hidden" name="id" value="'.$chid.'">';
275 print '<input type="hidden" name="chid" value="'.$chid.'">';
276 print '<input type="hidden" name="line_id" value="'.$line_id.'">';
277 print '<input type="hidden" name="remaindertopay" value="'.($total - $sumpaid).'">';
278 print '<input type="hidden" name="action" value="add_payment">';
279
280 print dol_get_fiche_head();
281
282 /*
283 print '<table class="border centpercent">';
284
285 print '<tr><td class="titlefield">'.$langs->trans("Ref").'</td><td colspan="2"><a href="'.DOL_URL_ROOT.'/loan/card.php?id='.$chid.'">'.$chid.'</a></td></tr>';
286 if ($echance > 0)
287 {
288 print '<tr><td>'.$langs->trans("Term").'</td><td colspan="2"><a href="'.DOL_URL_ROOT.'/loan/schedule.php?loanid='.$chid.'#n'.$echance.'">'.$echance.'</a></td></tr>'."\n";
289 }
290 print '<tr><td>'.$langs->trans("DateStart").'</td><td colspan="2">'.dol_print_date($loan->datestart, 'day')."</td></tr>\n";
291 print '<tr><td>'.$langs->trans("Label").'</td><td colspan="2">'.$loan->label."</td></tr>\n";
292 print '<tr><td>'.$langs->trans("Amount").'</td><td colspan="2">'.price($loan->capital, 0, $outputlangs, 1, -1, -1, $conf->currency).'</td></tr>';
293
294 print '<tr><td>'.$langs->trans("AlreadyPaid").'</td><td colspan="2">'.price($sumpaid, 0, $outputlangs, 1, -1, -1, $conf->currency).'</td></tr>';
295 print '<tr><td class="tdtop">'.$langs->trans("RemainderToPay").'</td><td colspan="2">'.price($total - $sumpaid, 0, $outputlangs, 1, -1, -1, $conf->currency).'</td></tr>';
296 print '</tr>';
297
298 print '</table>';
299 */
300
301 print '<table class="border centpercent">';
302
303 print '<tr><td class="titlefield fieldrequired">'.$langs->trans("Date").'</td><td colspan="2">';
304 if (empty($datepaid)) {
305 if (empty($ts_temppaid)) {
306 $datepayment = !getDolGlobalString('MAIN_AUTOFILL_DATE') ? -1 : dol_now();
307 } else {
308 $datepayment = $ts_temppaid;
309 }
310 } else {
311 $datepayment = $datepaid;
312 }
313 print $form->selectDate($datepayment, '', 0, 0, 0, "add_payment", 1, 1);
314 print "</td>";
315 print '</tr>';
316
317 print '<tr><td class="fieldrequired">'.$langs->trans("PaymentMode").'</td><td colspan="2">';
318 print img_picto('', 'money-bill-alt', 'class="pictofixedwidth"');
319 $form->select_types_paiements(GETPOSTISSET("paymenttype") ? GETPOST("paymenttype", 'alphanohtml') : $loan->fk_typepayment, "paymenttype");
320 print "</td>\n";
321 print '</tr>';
322
323 print '<tr>';
324 print '<td class="fieldrequired">'.$langs->trans('AccountToDebit').'</td>';
325 print '<td colspan="2">';
326 print img_picto('', 'bank_account', 'class="pictofixedwidth"');
327 $form->select_comptes(GETPOSTISSET("accountid") ? GETPOSTINT("accountid") : $loan->accountid, "accountid", 0, 'courant = '.Account::TYPE_CURRENT, 1); // Show opened bank account list
328 print '</td></tr>';
329
330 // Number
331 print '<tr><td>'.$langs->trans('Numero');
332 print ' <em>('.$langs->trans("ChequeOrTransferNumber").')</em>';
333 print '</td>';
334 print '<td colspan="2"><input name="num_payment" type="text" value="'.GETPOST('num_payment', 'alphanohtml').'"></td>'."\n";
335 print "</tr>";
336
337 print '<tr>';
338 print '<td class="tdtop">'.$langs->trans("NotePrivate").'</td>';
339 print '<td valign="top" colspan="2"><textarea name="note_private" wrap="soft" cols="60" rows="'.ROWS_3.'"></textarea></td>';
340 print '</tr>';
341
342 print '<tr>';
343 print '<td class="tdtop">'.$langs->trans("NotePublic").'</td>';
344 print '<td valign="top" colspan="2"><textarea name="note_public" wrap="soft" cols="60" rows="'.ROWS_3.'"></textarea></td>';
345 print '</tr>';
346
347 print '</table>';
348
349 print dol_get_fiche_end();
350
351
352 print '<table class="noborder centpercent">';
353 print '<tr class="liste_titre">';
354 print '<td class="left">'.$langs->trans("DateDue").'</td>';
355 print '<td class="right">'.$langs->trans("LoanCapital").'</td>';
356 print '<td class="right">'.$langs->trans("AlreadyPaid").'</td>';
357 print '<td class="right">'.$langs->trans("RemainderToPay").'</td>';
358 print '<td class="right">'.$langs->trans("Amount").'</td>';
359 print "</tr>\n";
360
361 print '<tr class="oddeven">';
362
363 if ($loan->datestart > 0) {
364 print '<td class="left" valign="center">'.dol_print_date($loan->datestart, 'day').'</td>';
365 } else {
366 print '<td class="center" valign="center"><b>!!!</b></td>';
367 }
368
369 print '<td class="right" valign="center">'.price($loan->capital)."</td>";
370
371 print '<td class="right" valign="center">'.price($sumpaid)."</td>";
372
373 print '<td class="right" valign="center">'.price($loan->capital - $sumpaid)."</td>";
374
375 print '<td class="right">';
376 if ($sumpaid < $loan->capital) {
377 print $langs->trans("LoanCapital").': <input type="text" size="8" name="amount_capital" value="'.(GETPOSTISSET('amount_capital') ? GETPOST('amount_capital') : $amount_capital).'">';
378 } else {
379 print '-';
380 }
381 print '<br>';
382 if ($sumpaid < $loan->capital) {
383 print $langs->trans("Insurance").': <input type="text" size="8" name="amount_insurance" value="'.(GETPOSTISSET('amount_insurance') ? GETPOST('amount_insurance') : $amount_insurance).'">';
384 } else {
385 print '-';
386 }
387 print '<br>';
388 if ($sumpaid < $loan->capital) {
389 print $langs->trans("Interest").': <input type="text" size="8" name="amount_interest" value="'.(GETPOSTISSET('amount_interest') ? GETPOST('amount_interest') : $amount_interest).'" '.(!empty($line) ? 'disabled title="'.$langs->trans('CantModifyInterestIfScheduleIsUsed').'"' : '').'>';
390 } else {
391 print '-';
392 }
393 print "</td>";
394
395 print "</tr>\n";
396
397 print '</table>';
398
399 print $form->buttonsSaveCancel();
400
401 print "</form>\n";
402}
403
404llxFooter();
405$db->close();
llxFooter($comment='', $zone='private', $disabledoutputofmessages=0)
Empty footer.
Definition wrapper.php:91
if(!defined('NOREQUIRESOC')) if(!defined( 'NOREQUIRETRAN')) if(!defined('NOTOKENRENEWAL')) if(!defined( 'NOREQUIREMENU')) if(!defined('NOREQUIREHTML')) if(!defined( 'NOREQUIREAJAX')) llxHeader($head='', $title='', $help_url='', $target='', $disablejs=0, $disablehead=0, $arrayofjs='', $arrayofcss='', $morequerystring='', $morecssonbody='', $replacemainareaby='', $disablenofollow=0, $disablenoindex=0)
Empty header.
Definition wrapper.php:73
Class to manage bank accounts.
Class to manage generation of HTML components Only common components must be here.
Loan.
Class to manage Schedule of loans.
Class to manage payments of loans.
dol_mktime($hour, $minute, $second, $month, $day, $year, $gm='auto', $check=1)
Return a timestamp date built from detailed information (by default a local PHP server timestamp) Rep...
load_fiche_titre($title, $morehtmlright='', $picto='generic', $pictoisfullpath=0, $id='', $morecssontable='', $morehtmlcenter='')
Load a title with picto.
setEventMessages($mesg, $mesgs, $style='mesgs', $messagekey='', $noduplicate=0, $attop=0)
Set event messages in dol_events session object.
img_picto($titlealt, $picto, $moreatt='', $pictoisfullpath=0, $srconly=0, $notitle=0, $alt='', $morecss='', $marginleftonlyshort=2, $allowothertags=array())
Show picto whatever it's its name (generic function)
GETPOSTINT($paramname, $method=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
dol_get_fiche_head($links=array(), $active='', $title='', $notab=0, $picto='', $pictoisfullpath=0, $morehtmlright='', $morecss='', $limittoshow=0, $moretabssuffix='', $dragdropfile=0, $morecssdiv='')
Show tabs of a record.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_get_fiche_end($notab=0)
Return tab footer of a card.
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_now($mode='auto')
Return date for now.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0)
Return value of a param into GET or POST supervariable.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
loanCalcMonthlyPayment($mens, $capital, $rate, $numactualloadterm, $nbterm)
Calculate remaining loan mensuality and interests.
Definition loan.lib.php:103
accessforbidden($message='', $printheader=1, $printfooter=1, $showonlymessage=0, $params=null)
Show a message to say access is forbidden and stop program.