dolibarr 24.0.0-beta
loanschedule.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2017 Florian HENRY <florian.henry@atm-consulting.fr>
3 * Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
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
26require_once DOL_DOCUMENT_ROOT.'/core/class/commonobject.class.php';
27
28
33{
37 public $element = 'loan_schedule';
38
42 public $table_element = 'loan_schedule';
43
47 public $fk_loan;
48
52 public $bank_account;
53
57 public $bank_line;
58
62 public $datec;
63
67 public $datep;
68
72 public $amounts = array(); // Array of amounts
76 public $amount_capital;
80 public $amount_insurance;
84 public $amount_interest;
85
89 public $fk_typepayment;
90
95 public $num_payment;
96
100 public $fk_bank;
101
105 public $fk_payment_loan;
106
110 public $fk_user_creat;
111
115 public $fk_user_modif;
116
121 public $lines = array();
122
128 public $total;
129
133 public $type_code;
137 public $type_label;
138
139
145 public function __construct($db)
146 {
147 $this->db = $db;
148 }
149
157 public function create($user)
158 {
159 global $conf, $langs;
160
161 $error = 0;
162
163 $now = dol_now();
164
165 // Validate parameters
166 if (!$this->datep) {
167 $this->error = 'ErrorBadValueForParameter';
168 return -1;
169 }
170
171 // Clean parameters
172 if (isset($this->fk_loan)) {
173 $this->fk_loan = (int) $this->fk_loan;
174 }
175 if (isset($this->amount_capital)) {
176 $this->amount_capital = trim($this->amount_capital ? $this->amount_capital : 0);
177 }
178 if (isset($this->amount_insurance)) {
179 $this->amount_insurance = trim($this->amount_insurance ? $this->amount_insurance : 0);
180 }
181 if (isset($this->amount_interest)) {
182 $this->amount_interest = trim($this->amount_interest ? $this->amount_interest : 0);
183 }
184 if (isset($this->fk_typepayment)) {
185 $this->fk_typepayment = (int) $this->fk_typepayment;
186 }
187 if (isset($this->fk_bank)) {
188 $this->fk_bank = (int) $this->fk_bank;
189 }
190 if (isset($this->fk_user_creat)) {
191 $this->fk_user_creat = (int) $this->fk_user_creat;
192 }
193 if (isset($this->fk_user_modif)) {
194 $this->fk_user_modif = (int) $this->fk_user_modif;
195 }
196
197 $totalamount = (float) $this->amount_capital + (float) $this->amount_insurance + (float) $this->amount_interest;
198 $totalamount = price2num($totalamount);
199
200 // Check parameters
201 if ($totalamount == 0) {
202 $this->errors[] = 'Amount must not be "0".';
203 return -1; // Negative amounts are accepted for reject prelevement but not null
204 }
205
206
207 $this->db->begin();
208
209 if ($totalamount != 0) {
210 $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (fk_loan, datec, datep, amount_capital, amount_insurance, amount_interest,";
211 $sql .= " fk_typepayment, fk_user_creat, fk_bank)";
212 $sql .= " VALUES (".$this->fk_loan.", '".$this->db->idate($now)."',";
213 $sql .= " '".$this->db->idate($this->datep)."',";
214 $sql .= " ".price2num($this->amount_capital).",";
215 $sql .= " ".price2num($this->amount_insurance).",";
216 $sql .= " ".price2num($this->amount_interest).",";
217 $sql .= " ".price2num($this->fk_typepayment).", ";
218 $sql .= " ".((int) $user->id).",";
219 $sql .= " ".((int) $this->fk_bank).")";
220
221 dol_syslog(get_class($this)."::create", LOG_DEBUG);
222 $resql = $this->db->query($sql);
223 if ($resql) {
224 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."loan_schedule");
225 } else {
226 $this->error = $this->db->lasterror();
227 $error++;
228 }
229 }
230
231 if ($totalamount != 0 && !$error) {
232 $this->amount_capital = $totalamount;
233 $this->db->commit();
234 return $this->id;
235 } else {
236 $this->errors[] = $this->db->lasterror();
237 $this->db->rollback();
238 return -1;
239 }
240 }
241
248 public function fetch($id)
249 {
250 global $langs;
251 $sql = "SELECT";
252 $sql .= " t.rowid,";
253 $sql .= " t.fk_loan,";
254 $sql .= " t.datec,";
255 $sql .= " t.tms,";
256 $sql .= " t.datep,";
257 $sql .= " t.amount_capital,";
258 $sql .= " t.amount_insurance,";
259 $sql .= " t.amount_interest,";
260 $sql .= " t.fk_typepayment,";
261 $sql .= " t.num_payment,";
262 $sql .= " t.note_private,";
263 $sql .= " t.note_public,";
264 $sql .= " t.fk_bank,";
265 $sql .= " t.fk_payment_loan,";
266 $sql .= " t.fk_user_creat,";
267 $sql .= " t.fk_user_modif,";
268 $sql .= " pt.code as type_code, pt.libelle as type_label,";
269 $sql .= ' b.fk_account';
270 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as t";
271 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pt ON t.fk_typepayment = pt.id";
272 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'bank as b ON t.fk_bank = b.rowid';
273 $sql .= " WHERE t.rowid = ".((int) $id);
274
275 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
276 $resql = $this->db->query($sql);
277 if ($resql) {
278 if ($this->db->num_rows($resql)) {
279 $obj = $this->db->fetch_object($resql);
280
281 $this->id = $obj->rowid;
282 $this->ref = $obj->rowid;
283
284 $this->fk_loan = $obj->fk_loan;
285 $this->datec = $this->db->jdate($obj->datec);
286 $this->tms = $this->db->jdate($obj->tms);
287 $this->datep = $this->db->jdate($obj->datep);
288 $this->amount_capital = $obj->amount_capital;
289 $this->amount_insurance = $obj->amount_insurance;
290 $this->amount_interest = $obj->amount_interest;
291 $this->fk_typepayment = $obj->fk_typepayment;
292 $this->num_payment = $obj->num_payment;
293 $this->note_private = $obj->note_private;
294 $this->note_public = $obj->note_public;
295 $this->fk_bank = $obj->fk_bank;
296 $this->fk_payment_loan = $obj->fk_payment_loan;
297 $this->fk_user_creat = $obj->fk_user_creat;
298 $this->fk_user_modif = $obj->fk_user_modif;
299
300 $this->type_code = $obj->type_code;
301 $this->type_label = $obj->type_label;
302
303 $this->bank_account = $obj->fk_account;
304 $this->bank_line = $obj->fk_bank;
305 }
306 $this->db->free($resql);
307
308 return 1;
309 } else {
310 $this->error = "Error ".$this->db->lasterror();
311 return -1;
312 }
313 }
314
315
323 public function update($user = null, $notrigger = 0)
324 {
325 global $conf, $langs;
326 $error = 0;
327
328 // Clean parameters
329 if (isset($this->amount_capital)) {
330 $this->amount_capital = trim($this->amount_capital);
331 }
332 if (isset($this->amount_insurance)) {
333 $this->amount_insurance = trim($this->amount_insurance);
334 }
335 if (isset($this->amount_interest)) {
336 $this->amount_interest = trim($this->amount_interest);
337 }
338 if (isset($this->num_payment)) {
339 $this->num_payment = trim($this->num_payment);
340 }
341 if (isset($this->note_private)) {
342 $this->note_private = trim($this->note_private);
343 }
344 if (isset($this->note_public)) {
345 $this->note_public = trim($this->note_public);
346 }
347 if (isset($this->fk_bank)) {
348 $this->fk_bank = (int) $this->fk_bank;
349 }
350 if (isset($this->fk_payment_loan)) {
351 $this->fk_payment_loan = (int) $this->fk_payment_loan;
352 }
353
354 // Check parameters
355 // Put here code to add control on parameters values
356
357 // Update request
358 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
359
360 $sql .= " fk_loan=".(isset($this->fk_loan) ? $this->fk_loan : "null").",";
361 $sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
362 $sql .= " tms=".(dol_strlen((string) $this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
363 $sql .= " datep=".(dol_strlen($this->datep) != 0 ? "'".$this->db->idate($this->datep)."'" : 'null').",";
364 $sql .= " amount_capital=".(isset($this->amount_capital) ? $this->amount_capital : "null").",";
365 $sql .= " amount_insurance=".(isset($this->amount_insurance) ? $this->amount_insurance : "null").",";
366 $sql .= " amount_interest=".(isset($this->amount_interest) ? $this->amount_interest : "null").",";
367 $sql .= " fk_typepayment=".(isset($this->fk_typepayment) ? $this->fk_typepayment : "null").",";
368 $sql .= " num_payment=".(isset($this->num_payment) ? "'".$this->db->escape($this->num_payment)."'" : "null").",";
369 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
370 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
371 $sql .= " fk_bank=".(isset($this->fk_bank) ? ((int) $this->fk_bank) : "null").",";
372 $sql .= " fk_payment_loan=".(isset($this->fk_payment_loan) ? ((int) $this->fk_payment_loan) : "null").",";
373 $sql .= " fk_user_creat=".(isset($this->fk_user_creat) ? ((int) $this->fk_user_creat) : "null").",";
374 $sql .= " fk_user_modif=".(isset($this->fk_user_modif) ? ((int) $this->fk_user_modif) : "null");
375
376 $sql .= " WHERE rowid=".((int) $this->id);
377
378 $this->db->begin();
379
380 dol_syslog(get_class($this)."::update", LOG_DEBUG);
381 $resql = $this->db->query($sql);
382 if (!$resql) {
383 $error++;
384 $this->errors[] = "Error ".$this->db->lasterror();
385 }
386
387 // Commit or rollback
388 if ($error) {
389 $this->db->rollback();
390 return -1 * $error;
391 } else {
392 $this->db->commit();
393 return 1;
394 }
395 }
396
397
405 public function delete($user, $notrigger = 0)
406 {
407 global $conf, $langs;
408 $error = 0;
409
410 $this->db->begin();
411
412 if (!$error) {
413 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element;
414 $sql .= " WHERE rowid=".((int) $this->id);
415
416 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
417 $resql = $this->db->query($sql);
418 if (!$resql) {
419 $error++;
420 $this->errors[] = "Error ".$this->db->lasterror();
421 }
422 }
423
424 // Commit or rollback
425 if ($error) {
426 foreach ($this->errors as $errmsg) {
427 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
428 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
429 }
430 $this->db->rollback();
431 return -1 * $error;
432 } else {
433 $this->db->commit();
434 return 1;
435 }
436 }
437
446 public function calcMonthlyPayments($capital, $rate, $nbterm)
447 {
448 $result = '';
449
450 if (!empty($capital) && !empty($nbterm)) {
451 if (!empty($rate)) {
452 $result = ($capital * ($rate / 12)) / (1 - pow((1 + ($rate / 12)), ($nbterm * -1)));
453 } else {
454 $result = $capital / $nbterm;
455 }
456 }
457
458 return $result;
459 }
460
461
468 public function fetchAll($loanid)
469 {
470 $sql = "SELECT";
471 $sql .= " t.rowid,";
472 $sql .= " t.fk_loan,";
473 $sql .= " t.datec,";
474 $sql .= " t.tms,";
475 $sql .= " t.datep,";
476 $sql .= " t.amount_capital,";
477 $sql .= " t.amount_insurance,";
478 $sql .= " t.amount_interest,";
479 $sql .= " t.fk_typepayment,";
480 $sql .= " t.num_payment,";
481 $sql .= " t.note_private,";
482 $sql .= " t.note_public,";
483 $sql .= " t.fk_bank,";
484 $sql .= " t.fk_payment_loan,";
485 $sql .= " t.fk_user_creat,";
486 $sql .= " t.fk_user_modif";
487 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as t";
488 $sql .= " WHERE t.fk_loan = ".((int) $loanid);
489
490 dol_syslog(get_class($this)."::fetchAll", LOG_DEBUG);
491 $resql = $this->db->query($sql);
492
493 if ($resql) {
494 while ($obj = $this->db->fetch_object($resql)) {
495 $line = new LoanSchedule($this->db);
496 $line->id = $obj->rowid;
497 $line->ref = $obj->rowid;
498
499 $line->fk_loan = $obj->fk_loan;
500 $line->datec = $this->db->jdate($obj->datec);
501 $line->tms = $this->db->jdate($obj->tms);
502 $line->datep = $this->db->jdate($obj->datep);
503 $line->amount_capital = $obj->amount_capital;
504 $line->amount_insurance = $obj->amount_insurance;
505 $line->amount_interest = $obj->amount_interest;
506 $line->fk_typepayment = $obj->fk_typepayment;
507 $line->num_payment = $obj->num_payment;
508 $line->note_private = $obj->note_private;
509 $line->note_public = $obj->note_public;
510 $line->fk_bank = $obj->fk_bank;
511 $line->fk_payment_loan = $obj->fk_payment_loan;
512 $line->fk_user_creat = $obj->fk_user_creat;
513 $line->fk_user_modif = $obj->fk_user_modif;
514
515 $this->lines[] = $line;
516 }
517 $this->db->free($resql);
518 return 1;
519 } else {
520 $this->error = "Error ".$this->db->lasterror();
521 return -1;
522 }
523 }
524
530 private function transPayment() // @phpstan-ignore-line
531 {
532 require_once DOL_DOCUMENT_ROOT.'/loan/class/loan.class.php';
533 require_once DOL_DOCUMENT_ROOT.'/core/lib/loan.lib.php';
534 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
535
536 $toinsert = array();
537
538 $sql = "SELECT l.rowid";
539 $sql .= " FROM ".MAIN_DB_PREFIX."loan as l";
540 $sql .= " WHERE l.paid = 0";
541 $resql = $this->db->query($sql);
542
543 if ($resql) {
544 while ($obj = $this->db->fetch_object($resql)) {
545 $lastrecorded = $this->lastPayment($obj->rowid);
546 $toinsert = $this->paimenttorecord($obj->rowid, $lastrecorded);
547 if (count($toinsert) > 0) {
548 foreach ($toinsert as $echid) {
549 $this->db->begin();
550 $sql = "INSERT INTO ".MAIN_DB_PREFIX."payment_loan ";
551 $sql .= "(fk_loan,datec,tms,datep,amount_capital,amount_insurance,amount_interest,fk_typepayment,num_payment,note_private,note_public,fk_bank,fk_user_creat,fk_user_modif) ";
552 $sql .= "SELECT fk_loan,datec,tms,datep,amount_capital,amount_insurance,amount_interest,fk_typepayment,num_payment,note_private,note_public,fk_bank,fk_user_creat,fk_user_modif";
553 $sql .= " FROM ".MAIN_DB_PREFIX."loan_schedule WHERE rowid =".((int) $echid);
554 $res = $this->db->query($sql);
555 if ($res) {
556 $this->db->commit();
557 } else {
558 $this->db->rollback();
559 }
560 }
561 }
562 }
563 }
564 }
565
566
573 private function lastPayment($loanid)
574 {
575 $sql = "SELECT p.datep";
576 $sql .= " FROM ".MAIN_DB_PREFIX."payment_loan as p ";
577 $sql .= " WHERE p.fk_loan = ".((int) $loanid);
578 $sql .= " ORDER BY p.datep DESC ";
579 $sql .= " LIMIT 1 ";
580
581 $resql = $this->db->query($sql);
582
583 if ($resql) {
584 $obj = $this->db->fetch_object($resql);
585 return $this->db->jdate($obj->datep);
586 } else {
587 return -1;
588 }
589 }
590
598 public function paimenttorecord($loanid, $datemax)
599 {
600 $result = array();
601
602 $sql = "SELECT p.rowid";
603 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as p ";
604 $sql .= " WHERE p.fk_loan = ".((int) $loanid);
605 if (!empty($datemax)) {
606 $sql .= " AND p.datep > '".$this->db->idate($datemax)."'";
607 }
608 $sql .= " AND p.datep <= '".$this->db->idate(dol_now())."'";
609
610 $resql = $this->db->query($sql);
611
612 if ($resql) {
613 while ($obj = $this->db->fetch_object($resql)) {
614 $result[] = $obj->rowid;
615 }
616 }
617
618 return $result;
619 }
620}
$object ref
Definition info.php:90
Parent class of all other business classes (invoices, contracts, proposals, orders,...
Class to manage Schedule of loans.
__construct($db)
Constructor.
fetchAll($loanid)
Load all object in memory from database.
calcMonthlyPayments($capital, $rate, $nbterm)
Calculate Monthly Payments.
create($user)
Create payment of loan into database.
fetch($id)
Load object in memory from database.
paimenttorecord($loanid, $datemax)
paimenttorecord
update($user=null, $notrigger=0)
Update database.
lastPayment($loanid)
lastpayment
transPayment()
transPayment
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
dol_now($mode='gmt')
Return date for now.
price2num($amount, $rounding='', $option=0)
Function that return a number with universal decimal format (decimal separator is '.
dol_syslog($message, $level=LOG_INFO, $ident=0, $suffixinfilename='', $restricttologhandler='', $logcontext=null)
Write log message into outputs.