dolibarr 25.0.0-alpha
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-2026 Frédéric France <frederic.france@free.fr>
4 * Copyright (C) 2024-2026 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
158 public function create($user, $notrigger = 0)
159 {
160 global $conf, $langs;
161
162 $error = 0;
163
164 $now = dol_now();
165
166 // Validate parameters
167 if (!$this->datep) {
168 $this->error = 'ErrorBadValueForParameter';
169 return -1;
170 }
171
172 // Clean parameters
173 if (isset($this->fk_loan)) {
174 $this->fk_loan = (int) $this->fk_loan;
175 }
176 if (isset($this->amount_capital)) {
177 $this->amount_capital = trim($this->amount_capital ? $this->amount_capital : 0);
178 }
179 if (isset($this->amount_insurance)) {
180 $this->amount_insurance = trim($this->amount_insurance ? $this->amount_insurance : 0);
181 }
182 if (isset($this->amount_interest)) {
183 $this->amount_interest = trim($this->amount_interest ? $this->amount_interest : 0);
184 }
185 if (isset($this->fk_typepayment)) {
186 $this->fk_typepayment = (int) $this->fk_typepayment;
187 }
188 if (isset($this->fk_bank)) {
189 $this->fk_bank = (int) $this->fk_bank;
190 }
191 if (isset($this->fk_user_creat)) {
192 $this->fk_user_creat = (int) $this->fk_user_creat;
193 }
194 if (isset($this->fk_user_modif)) {
195 $this->fk_user_modif = (int) $this->fk_user_modif;
196 }
197
198 $totalamount = (float) $this->amount_capital + (float) $this->amount_insurance + (float) $this->amount_interest;
199 $totalamount = price2num($totalamount);
200
201 // Check parameters
202 if ($totalamount == 0) {
203 $this->errors[] = 'Amount must not be "0".';
204 return -1; // Negative amounts are accepted for reject prelevement but not null
205 }
206
207
208 $this->db->begin();
209
210 if ($totalamount != 0) {
211 $sql = "INSERT INTO ".MAIN_DB_PREFIX.$this->table_element." (fk_loan, datec, datep, amount_capital, amount_insurance, amount_interest,";
212 $sql .= " fk_typepayment, fk_user_creat, fk_bank)";
213 $sql .= " VALUES (".((int) $this->fk_loan).", '".$this->db->idate($now)."',";
214 $sql .= " '".$this->db->idate($this->datep)."',";
215 $sql .= " ".price2num($this->amount_capital).",";
216 $sql .= " ".price2num($this->amount_insurance).",";
217 $sql .= " ".price2num($this->amount_interest).",";
218 $sql .= " ".price2num($this->fk_typepayment).", ";
219 $sql .= " ".((int) $user->id).",";
220 $sql .= " ".((int) $this->fk_bank).")";
221
222 dol_syslog(get_class($this)."::create", LOG_DEBUG);
223 $resql = $this->db->query($sql);
224 if ($resql) {
225 $this->id = $this->db->last_insert_id(MAIN_DB_PREFIX."loan_schedule");
226 } else {
227 $this->error = $this->db->lasterror();
228 $error++;
229 }
230 }
231
232 if ($totalamount != 0 && !$error && !$notrigger) {
233 // Call trigger
234 $result = $this->call_trigger('LOANSCHEDULE_CREATE', $user);
235 if ($result < 0) {
236 $error++;
237 }
238 // End call triggers
239 }
240
241 if ($totalamount != 0 && !$error) {
242 $this->amount_capital = $totalamount;
243 $this->db->commit();
244 return $this->id;
245 } else {
246 $this->errors[] = $this->db->lasterror();
247 $this->db->rollback();
248 return -1;
249 }
250 }
251
258 public function fetch($id)
259 {
260 global $langs;
261 $sql = "SELECT";
262 $sql .= " t.rowid,";
263 $sql .= " t.fk_loan,";
264 $sql .= " t.datec,";
265 $sql .= " t.tms,";
266 $sql .= " t.datep,";
267 $sql .= " t.amount_capital,";
268 $sql .= " t.amount_insurance,";
269 $sql .= " t.amount_interest,";
270 $sql .= " t.fk_typepayment,";
271 $sql .= " t.num_payment,";
272 $sql .= " t.note_private,";
273 $sql .= " t.note_public,";
274 $sql .= " t.fk_bank,";
275 $sql .= " t.fk_payment_loan,";
276 $sql .= " t.fk_user_creat,";
277 $sql .= " t.fk_user_modif,";
278 $sql .= " pt.code as type_code, pt.libelle as type_label,";
279 $sql .= ' b.fk_account';
280 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as t";
281 $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pt ON t.fk_typepayment = pt.id";
282 $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'bank as b ON t.fk_bank = b.rowid';
283 $sql .= " WHERE t.rowid = ".((int) $id);
284
285 dol_syslog(get_class($this)."::fetch", LOG_DEBUG);
286 $resql = $this->db->query($sql);
287 if ($resql) {
288 if ($this->db->num_rows($resql)) {
289 $obj = $this->db->fetch_object($resql);
290
291 $this->id = $obj->rowid;
292 $this->ref = $obj->rowid;
293
294 $this->fk_loan = $obj->fk_loan;
295 $this->datec = $this->db->jdate($obj->datec);
296 $this->tms = $this->db->jdate($obj->tms);
297 $this->datep = $this->db->jdate($obj->datep);
298 $this->amount_capital = $obj->amount_capital;
299 $this->amount_insurance = $obj->amount_insurance;
300 $this->amount_interest = $obj->amount_interest;
301 $this->fk_typepayment = $obj->fk_typepayment;
302 $this->num_payment = $obj->num_payment;
303 $this->note_private = $obj->note_private;
304 $this->note_public = $obj->note_public;
305 $this->fk_bank = $obj->fk_bank;
306 $this->fk_payment_loan = $obj->fk_payment_loan;
307 $this->fk_user_creat = $obj->fk_user_creat;
308 $this->fk_user_modif = $obj->fk_user_modif;
309
310 $this->type_code = $obj->type_code;
311 $this->type_label = $obj->type_label;
312
313 $this->bank_account = $obj->fk_account;
314 $this->bank_line = $obj->fk_bank;
315 }
316 $this->db->free($resql);
317
318 return 1;
319 } else {
320 $this->error = "Error ".$this->db->lasterror();
321 return -1;
322 }
323 }
324
325
333 public function update($user = null, $notrigger = 0)
334 {
335 global $conf, $langs;
336 $error = 0;
337
338 // Clean parameters
339 if (isset($this->amount_capital)) {
340 $this->amount_capital = trim($this->amount_capital);
341 }
342 if (isset($this->amount_insurance)) {
343 $this->amount_insurance = trim($this->amount_insurance);
344 }
345 if (isset($this->amount_interest)) {
346 $this->amount_interest = trim($this->amount_interest);
347 }
348 if (isset($this->num_payment)) {
349 $this->num_payment = trim($this->num_payment);
350 }
351 if (isset($this->note_private)) {
352 $this->note_private = trim($this->note_private);
353 }
354 if (isset($this->note_public)) {
355 $this->note_public = trim($this->note_public);
356 }
357 if (isset($this->fk_bank)) {
358 $this->fk_bank = (int) $this->fk_bank;
359 }
360 if (isset($this->fk_payment_loan)) {
361 $this->fk_payment_loan = (int) $this->fk_payment_loan;
362 }
363
364 // Check parameters
365 // Put here code to add control on parameters values
366
367 // Update request
368 $sql = "UPDATE ".MAIN_DB_PREFIX.$this->table_element." SET";
369
370 $sql .= " fk_loan=".(isset($this->fk_loan) ? ((int) $this->fk_loan) : "null").",";
371 $sql .= " datec=".(dol_strlen($this->datec) != 0 ? "'".$this->db->idate($this->datec)."'" : 'null').",";
372 $sql .= " tms=".(dol_strlen((string) $this->tms) != 0 ? "'".$this->db->idate($this->tms)."'" : 'null').",";
373 $sql .= " datep=".(dol_strlen($this->datep) != 0 ? "'".$this->db->idate($this->datep)."'" : 'null').",";
374 $sql .= " amount_capital=".(isset($this->amount_capital) ? ((float) $this->amount_capital) : "null").",";
375 $sql .= " amount_insurance=".(isset($this->amount_insurance) ? ((float) $this->amount_insurance) : "null").",";
376 $sql .= " amount_interest=".(isset($this->amount_interest) ? ((float) $this->amount_interest) : "null").",";
377 $sql .= " fk_typepayment=".(isset($this->fk_typepayment) ? ((int) $this->fk_typepayment) : "null").",";
378 $sql .= " num_payment=".(isset($this->num_payment) ? "'".$this->db->escape($this->num_payment)."'" : "null").",";
379 $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").",";
380 $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").",";
381 $sql .= " fk_bank=".(isset($this->fk_bank) ? ((int) $this->fk_bank) : "null").",";
382 $sql .= " fk_payment_loan=".(isset($this->fk_payment_loan) ? ((int) $this->fk_payment_loan) : "null").",";
383 $sql .= " fk_user_creat=".(isset($this->fk_user_creat) ? ((int) $this->fk_user_creat) : "null").",";
384 $sql .= " fk_user_modif=".(isset($this->fk_user_modif) ? ((int) $this->fk_user_modif) : "null");
385
386 $sql .= " WHERE rowid=".((int) $this->id);
387
388 $this->db->begin();
389
390 dol_syslog(get_class($this)."::update", LOG_DEBUG);
391 $resql = $this->db->query($sql);
392 if (!$resql) {
393 $error++;
394 $this->errors[] = "Error ".$this->db->lasterror();
395 }
396
397 if (!$error && $user && !$notrigger) {
398 // Call trigger
399 $result = $this->call_trigger('LOANSCHEDULE_MODIFY', $user);
400 if ($result < 0) {
401 $error++;
402 }
403 // End call triggers
404 }
405
406 // Commit or rollback
407 if ($error) {
408 $this->db->rollback();
409 return -1 * $error;
410 } else {
411 $this->db->commit();
412 return 1;
413 }
414 }
415
416
424 public function delete($user, $notrigger = 0)
425 {
426 global $conf, $langs;
427 $error = 0;
428
429 $this->db->begin();
430
431 if (!$error) {
432 $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element;
433 $sql .= " WHERE rowid=".((int) $this->id);
434
435 dol_syslog(get_class($this)."::delete", LOG_DEBUG);
436 $resql = $this->db->query($sql);
437 if (!$resql) {
438 $error++;
439 $this->errors[] = "Error ".$this->db->lasterror();
440 }
441 }
442
443 if (!$error && !$notrigger) {
444 // Call trigger
445 $result = $this->call_trigger('LOANSCHEDULE_DELETE', $user);
446 if ($result < 0) {
447 $error++;
448 }
449 // End call triggers
450 }
451
452 // Commit or rollback
453 if ($error) {
454 foreach ($this->errors as $errmsg) {
455 dol_syslog(get_class($this)."::delete ".$errmsg, LOG_ERR);
456 $this->error .= ($this->error ? ', '.$errmsg : $errmsg);
457 }
458 $this->db->rollback();
459 return -1 * $error;
460 } else {
461 $this->db->commit();
462 return 1;
463 }
464 }
465
474 public function calcMonthlyPayments($capital, $rate, $nbterm)
475 {
476 $result = '';
477
478 if (!empty($capital) && !empty($nbterm)) {
479 if (!empty($rate)) {
480 $result = ($capital * ($rate / 12)) / (1 - pow((1 + ($rate / 12)), ($nbterm * -1)));
481 } else {
482 $result = $capital / $nbterm;
483 }
484 }
485
486 return $result;
487 }
488
489
496 public function fetchAll($loanid)
497 {
498 $sql = "SELECT";
499 $sql .= " t.rowid,";
500 $sql .= " t.fk_loan,";
501 $sql .= " t.datec,";
502 $sql .= " t.tms,";
503 $sql .= " t.datep,";
504 $sql .= " t.amount_capital,";
505 $sql .= " t.amount_insurance,";
506 $sql .= " t.amount_interest,";
507 $sql .= " t.fk_typepayment,";
508 $sql .= " t.num_payment,";
509 $sql .= " t.note_private,";
510 $sql .= " t.note_public,";
511 $sql .= " t.fk_bank,";
512 $sql .= " t.fk_payment_loan,";
513 $sql .= " t.fk_user_creat,";
514 $sql .= " t.fk_user_modif";
515 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as t";
516 $sql .= " WHERE t.fk_loan = ".((int) $loanid);
517
518 dol_syslog(get_class($this)."::fetchAll", LOG_DEBUG);
519 $resql = $this->db->query($sql);
520
521 if ($resql) {
522 while ($obj = $this->db->fetch_object($resql)) {
523 $line = new LoanSchedule($this->db);
524 $line->id = $obj->rowid;
525 $line->ref = $obj->rowid;
526
527 $line->fk_loan = $obj->fk_loan;
528 $line->datec = $this->db->jdate($obj->datec);
529 $line->tms = $this->db->jdate($obj->tms);
530 $line->datep = $this->db->jdate($obj->datep);
531 $line->amount_capital = $obj->amount_capital;
532 $line->amount_insurance = $obj->amount_insurance;
533 $line->amount_interest = $obj->amount_interest;
534 $line->fk_typepayment = $obj->fk_typepayment;
535 $line->num_payment = $obj->num_payment;
536 $line->note_private = $obj->note_private;
537 $line->note_public = $obj->note_public;
538 $line->fk_bank = $obj->fk_bank;
539 $line->fk_payment_loan = $obj->fk_payment_loan;
540 $line->fk_user_creat = $obj->fk_user_creat;
541 $line->fk_user_modif = $obj->fk_user_modif;
542
543 $this->lines[] = $line;
544 }
545 $this->db->free($resql);
546 return 1;
547 } else {
548 $this->error = "Error ".$this->db->lasterror();
549 return -1;
550 }
551 }
552
558 private function transPayment() // @phpstan-ignore-line
559 {
560 require_once DOL_DOCUMENT_ROOT.'/loan/class/loan.class.php';
561 require_once DOL_DOCUMENT_ROOT.'/core/lib/loan.lib.php';
562 require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
563
564 $toinsert = array();
565
566 $sql = "SELECT l.rowid";
567 $sql .= " FROM ".MAIN_DB_PREFIX."loan as l";
568 $sql .= " WHERE l.paid = 0";
569 $resql = $this->db->query($sql);
570
571 if ($resql) {
572 while ($obj = $this->db->fetch_object($resql)) {
573 $lastrecorded = $this->lastPayment($obj->rowid);
574 $toinsert = $this->paimenttorecord($obj->rowid, $lastrecorded);
575 if (count($toinsert) > 0) {
576 foreach ($toinsert as $echid) {
577 $this->db->begin();
578 $sql = "INSERT INTO ".MAIN_DB_PREFIX."payment_loan ";
579 $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) ";
580 $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";
581 $sql .= " FROM ".MAIN_DB_PREFIX."loan_schedule WHERE rowid =".((int) $echid);
582 $res = $this->db->query($sql);
583 if ($res) {
584 $this->db->commit();
585 } else {
586 $this->db->rollback();
587 }
588 }
589 }
590 }
591 }
592 }
593
594
601 private function lastPayment($loanid)
602 {
603 $sql = "SELECT p.datep";
604 $sql .= " FROM ".MAIN_DB_PREFIX."payment_loan as p ";
605 $sql .= " WHERE p.fk_loan = ".((int) $loanid);
606 $sql .= " ORDER BY p.datep DESC ";
607 $sql .= " LIMIT 1 ";
608
609 $resql = $this->db->query($sql);
610
611 if ($resql) {
612 $obj = $this->db->fetch_object($resql);
613 return $this->db->jdate($obj->datep);
614 } else {
615 return -1;
616 }
617 }
618
626 public function paimenttorecord($loanid, $datemax)
627 {
628 $result = array();
629
630 $sql = "SELECT p.rowid";
631 $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as p ";
632 $sql .= " WHERE p.fk_loan = ".((int) $loanid);
633 if (!empty($datemax)) {
634 $sql .= " AND p.datep > '".$this->db->idate($datemax)."'";
635 }
636 $sql .= " AND p.datep <= '".$this->db->idate(dol_now())."'";
637
638 $resql = $this->db->query($sql);
639
640 if ($resql) {
641 while ($obj = $this->db->fetch_object($resql)) {
642 $result[] = $obj->rowid;
643 }
644 }
645
646 return $result;
647 }
648}
$object ref
Definition info.php:90
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.
fetch($id)
Load object in memory from database.
paimenttorecord($loanid, $datemax)
paimenttorecord
update($user=null, $notrigger=0)
Update database.
lastPayment($loanid)
lastpayment
create($user, $notrigger=0)
Create payment of loan into database.
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.