diff --git a/htdocs/core/lib/loan.lib.php b/htdocs/core/lib/loan.lib.php index a3c352a0405..b8e4d39c591 100644 --- a/htdocs/core/lib/loan.lib.php +++ b/htdocs/core/lib/loan.lib.php @@ -1,6 +1,7 @@ * Copyright (C) 2015 Frederic France + * Copyright (C) 2020 Maxime DEMAREST * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -82,3 +83,51 @@ function loan_prepare_head($object) return $head; } + +/** + * Calculate remaining loan mensuality and interests + * + * @param int $mens Value of this mensuality (interests include, set 0 if we don't paid interests for this mensuality) + * @param float $capital Remaining capital for this mensuality + * @param float $rate Loan rate + * @param int $echance Actual loan term + * @param int $nbterm Total number of term for this loan + * @return array Array with remaining capital, interest, and mensuality for each remaining terms + */ +function loan_calcmens($mens, $capital, $rate, $echance, $nbterm) +{ + global $conf; + require_once DOL_DOCUMENT_ROOT.'/loan/class/loanschedule.class.php'; + $object = new LoanSchedule($db); + + // If mensuality is 0 we don't pay interests and remaining capital not modified + if ($mens == 0) + { + $int = 0; + $cap_rest = $capital; + } + else + { + $int = ($capital * ($rate / 12)); + $int = round($int, 2, PHP_ROUND_HALF_UP); + $cap_rest = round($capital - ($mens - $int), 2, PHP_ROUND_HALF_UP); + } + $output[$echance] = array('cap_rest'=>$cap_rest, 'cap_rest_str'=>price($cap_rest, 0, '', 1, -1, -1, $conf->currency), 'interet'=>$int, 'interet_str'=>price($int, 0, '', 1, -1, -1, $conf->currency), 'mens'=>$mens); + + $echance++; + $capital = $cap_rest; + while ($echance <= $nbterm) { + $mens = round($object->calcMonthlyPayments($capital, $rate, $nbterm - $echance + 1), 2, PHP_ROUND_HALF_UP); + + $int = ($capital * ($rate / 12)); + $int = round($int, 2, PHP_ROUND_HALF_UP); + $cap_rest = round($capital - ($mens - $int), 2, PHP_ROUND_HALF_UP); + + $output[$echance] = array('cap_rest'=>$cap_rest, 'cap_rest_str'=>price($cap_rest, 0, '', 1, -1, -1, $conf->currency), 'interet'=>$int, 'interet_str'=>price($int, 0, '', 1, -1, -1, $conf->currency), 'mens'=>$mens); + + $capital = $cap_rest; + $echance++; + } + + return $output; +} diff --git a/htdocs/install/mysql/migration/11.0.0-12.0.0.sql b/htdocs/install/mysql/migration/11.0.0-12.0.0.sql index a9099ee49c0..41d439d1e95 100644 --- a/htdocs/install/mysql/migration/11.0.0-12.0.0.sql +++ b/htdocs/install/mysql/migration/11.0.0-12.0.0.sql @@ -50,8 +50,8 @@ create table llx_c_shipment_package_type rowid integer AUTO_INCREMENT PRIMARY KEY, label varchar(50) NOT NULL, -- Short name description varchar(255), -- Description - active integer DEFAULT 1 NOT NULL, -- Active or not - entity integer DEFAULT 1 NOT NULL -- Multi company id + active integer DEFAULT 1 NOT NULL, -- Active or not + entity integer DEFAULT 1 NOT NULL -- Multi company id )ENGINE=innodb; create table llx_facturedet_rec_extrafields @@ -100,7 +100,7 @@ UPDATE llx_website_page SET lang = 'it' WHERE lang like 'it_%'; UPDATE llx_website_page SET lang = 'pt' WHERE lang like 'pt_%'; ALTER TABLE llx_website ADD COLUMN lang varchar(8); -ALTER TABLE llx_website ADD COLUMN otherlang varchar(255); +ALTER TABLE llx_website ADD COLUMN otherlang varchar(255); ALTER TABLE llx_website_page ADD COLUMN author_alias varchar(64); @@ -283,7 +283,7 @@ ALTER TABLE llx_categorie_website_page ADD INDEX idx_categorie_website_page_fk_w ALTER TABLE llx_categorie_website_page ADD CONSTRAINT fk_categorie_website_page_categorie_rowid FOREIGN KEY (fk_categorie) REFERENCES llx_categorie (rowid); ALTER TABLE llx_categorie_website_page ADD CONSTRAINT fk_categorie_website_page_website_page_rowid FOREIGN KEY (fk_website_page) REFERENCES llx_website_page (rowid); -ALTER TABLE llx_categorie ADD COLUMN date_creation datetime; +ALTER TABLE llx_categorie ADD COLUMN date_creation datetime; ALTER TABLE llx_categorie ADD COLUMN tms timestamp; ALTER TABLE llx_categorie ADD COLUMN fk_user_creat integer; ALTER TABLE llx_categorie ADD COLUMN fk_user_modif integer; @@ -301,11 +301,12 @@ ALTER TABLE llx_prelevement_facture ADD COLUMN fk_facture_fourn INTEGER NULL; ALTER TABLE llx_menu MODIFY COLUMN module varchar(255); -UPDATE llx_actioncomm SET fk_action = 50 where fk_action = 40 AND code = 'TICKET_MSG'; +UPDATE llx_actioncomm SET fk_action = 50 where fk_action = 40 AND code = 'TICKET_MSG'; ALTER TABLE llx_emailcollector_emailcollector ADD COLUMN hostcharset varchar(16) DEFAULT 'UTF-8'; ALTER TABLE llx_adherent_type MODIFY subscription varchar(3) NOT NULL DEFAULT '1'; ALTER TABLE llx_adherent_type MODIFY vote varchar(3) NOT NULL DEFAULT '1'; - + +ALTER TABLE llx_loan_schedule ADD column fk_payment_loan INTEGER; diff --git a/htdocs/install/mysql/tables/llx_loan_schedule.sql b/htdocs/install/mysql/tables/llx_loan_schedule.sql index 42592815526..e23bc678e71 100644 --- a/htdocs/install/mysql/tables/llx_loan_schedule.sql +++ b/htdocs/install/mysql/tables/llx_loan_schedule.sql @@ -32,6 +32,7 @@ create table llx_loan_schedule note_private text, note_public text, fk_bank integer NOT NULL, + fk_payment_loan integer, fk_user_creat integer, -- creation user fk_user_modif integer -- last modification user )ENGINE=innodb; diff --git a/htdocs/langs/en_US/loan.lang b/htdocs/langs/en_US/loan.lang index 534dee08867..e36f7cd424e 100644 --- a/htdocs/langs/en_US/loan.lang +++ b/htdocs/langs/en_US/loan.lang @@ -23,6 +23,9 @@ AddLoan=Create loan FinancialCommitment=Financial commitment InterestAmount=Interest CapitalRemain=Capital remain +TermPaidAllreadyPaid = This term is allready paid +CantUseScheduleWithLoanStartedToPaid = Can't use shcedule with loan started to paid +CantModifyInterestIfScheduleIsUsed = You can't modify interest if you use schedule # Admin ConfigLoan=Configuration of the module loan LOAN_ACCOUNTING_ACCOUNT_CAPITAL=Accounting account capital by default diff --git a/htdocs/langs/fr_FR/loan.lang b/htdocs/langs/fr_FR/loan.lang index 931ab8f76fb..8061e40332b 100644 --- a/htdocs/langs/fr_FR/loan.lang +++ b/htdocs/langs/fr_FR/loan.lang @@ -23,6 +23,9 @@ AddLoan=Créer prêt FinancialCommitment=Echéancier InterestAmount=Intérêt CapitalRemain=Capital restant +TermPaidAllreadyPaid = Cette échéance est déjà payé +CantUseScheduleWithLoanStartedToPaid = Vous ne pouvez pas utiliser l'échéancier si vous avez commencé a régler le prêt sans. +CantModifyInterestIfScheduleIsUsed = Vous ne pouvez pas modifier les intérêts si l'échéancier est utilisé. # Admin ConfigLoan=Configuration du module Emprunt LOAN_ACCOUNTING_ACCOUNT_CAPITAL=Compte comptable remboursement capital d'un emprunt par défaut diff --git a/htdocs/loan/calcmens.php b/htdocs/loan/calcmens.php index b65aa41c4d4..06e2bf93b47 100644 --- a/htdocs/loan/calcmens.php +++ b/htdocs/loan/calcmens.php @@ -1,6 +1,7 @@ + * Copyright (C) 2020 Maxime DEMAREST * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,7 +29,7 @@ if (!defined('NOREQUIREMENU')) define('NOREQUIREMENU', '1'); if (!defined('NOREQUIREAJAX')) define('NOREQUIREAJAX', '1'); require '../main.inc.php'; -require DOL_DOCUMENT_ROOT.'/loan/class/loanschedule.class.php'; +require DOL_DOCUMENT_ROOT.'/core/lib/loan.lib.php'; $mens = GETPOST('mens'); $capital = GETPOST('capital'); @@ -40,26 +41,6 @@ top_httphead(); $output = array(); -$object = new LoanSchedule($db); - -$int = ($capital * ($rate / 12)); -$int = round($int, 2, PHP_ROUND_HALF_UP); -$cap_rest = round($capital - ($mens - $int), 2, PHP_ROUND_HALF_UP); -$output[$echance] = array('cap_rest'=>$cap_rest, 'cap_rest_str'=>price($cap_rest, 0, '', 1, -1, -1, $conf->currency), 'interet'=>$int, 'interet_str'=>price($int, 0, '', 1, -1, -1, $conf->currency), 'mens'=>$mens); - -$echance++; -$capital = $cap_rest; -while ($echance <= $nbterm) { - $mens = round($object->calcMonthlyPayments($capital, $rate, $nbterm - $echance + 1), 2, PHP_ROUND_HALF_UP); - - $int = ($capital * ($rate / 12)); - $int = round($int, 2, PHP_ROUND_HALF_UP); - $cap_rest = round($capital - ($mens - $int), 2, PHP_ROUND_HALF_UP); - - $output[$echance] = array('cap_rest'=>$cap_rest, 'cap_rest_str'=>price($cap_rest, 0, '', 1, -1, -1, $conf->currency), 'interet'=>$int, 'interet_str'=>price($int, 0, '', 1, -1, -1, $conf->currency), 'mens'=>$mens); - - $capital = $cap_rest; - $echance++; -} +$output = loan_calcmens($mens, $capital, $rate, $echance, $nbterm); echo json_encode($output); diff --git a/htdocs/loan/card.php b/htdocs/loan/card.php index 8f82e94b8f9..fa3ca6afe52 100644 --- a/htdocs/loan/card.php +++ b/htdocs/loan/card.php @@ -2,6 +2,7 @@ /* Copyright (C) 2014-2018 Alexandre Spangaro * Copyright (C) 2015 Frederic France * Copyright (C) 2017 Laurent Destailleur + * Copyright (C) 2020 Maxime DEMAREST * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -81,7 +82,7 @@ if (empty($reshook)) if ($result > 0) { setEventMessages($langs->trans('LoanDeleted'), null, 'mesgs'); - header("Location: index.php"); + header("Location: list.php"); exit; } else { setEventMessages($loan->error, null, 'errors'); @@ -687,7 +688,7 @@ if ($id > 0) $totalpaid = $total_capital; - if ($object->paid == 0) + if ($object->paid == 0 || $object->paid == 2) { print ''.$langs->trans("AlreadyPaid").' :'.price($totalpaid, 0, $langs, 0, -1, -1, $conf->currency).''; print ''.$langs->trans("AmountExpected").' :'.price($object->capital, 0, $outputlangs, 1, -1, -1, $conf->currency).''; @@ -737,25 +738,25 @@ if ($id > 0) print '
'; // Edit - if ($object->paid == 0 && $user->rights->loan->write) + if (($object->paid == 0 || $object->paid == 2) && $user->rights->loan->write) { print ''; } // Emit payment - if ($object->paid == 0 && ((price2num($object->capital) > 0 && round($staytopay) < 0) || (price2num($object->capital) > 0 && round($staytopay) > 0)) && $user->rights->loan->write) + if (($object->paid == 0 || $object->paid == 2) && ((price2num($object->capital) > 0 && round($staytopay) < 0) || (price2num($object->capital) > 0 && round($staytopay) > 0)) && $user->rights->loan->write) { - print ''; + print ''; } // Classify 'paid' - if ($object->paid == 0 && round($staytopay) <= 0 && $user->rights->loan->write) + if (($object->paid == 0 || $object->paid == 2) && round($staytopay) <= 0 && $user->rights->loan->write) { print ''; } // Delete - if ($object->paid == 0 && $user->rights->loan->delete) + if (($object->paid == 0 || $object->paid == 2) && $user->rights->loan->delete) { print ''; } diff --git a/htdocs/loan/class/loan.class.php b/htdocs/loan/class/loan.class.php index a072199dc01..33f3d789806 100644 --- a/htdocs/loan/class/loan.class.php +++ b/htdocs/loan/class/loan.class.php @@ -107,6 +107,7 @@ class Loan extends CommonObject const STATUS_UNPAID = 0; const STATUS_PAID = 1; + const STATUS_STARTED = 2; /** @@ -385,7 +386,51 @@ class Loan extends CommonObject { // phpcs:enable $sql = "UPDATE ".MAIN_DB_PREFIX."loan SET"; - $sql .= " paid = 1"; + $sql .= " paid = ".$this::STATUS_PAID; + $sql .= " WHERE rowid = ".$this->id; + $return = $this->db->query($sql); + if ($return) { + return 1; + } else { + $this->error = $this->db->lasterror(); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Tag loan as payement started + * + * @param User $user Object user making change + * @return int <0 if KO, >0 if OK + */ + public function set_started($user) + { + // phpcs:enable + $sql = "UPDATE ".MAIN_DB_PREFIX."loan SET"; + $sql .= " paid = ".$this::STATUS_STARTED; + $sql .= " WHERE rowid = ".$this->id; + $return = $this->db->query($sql); + if ($return) { + return 1; + } else { + $this->error = $this->db->lasterror(); + return -1; + } + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps + /** + * Tag loan as payement as unpaid + * + * @param User $user Object user making change + * @return int <0 if KO, >0 if OK + */ + public function set_unpaid($user) + { + // phpcs:enable + $sql = "UPDATE ".MAIN_DB_PREFIX."loan SET"; + $sql .= " paid = ".$this::STATUS_UNPAID; $sql .= " WHERE rowid = ".$this->id; $return = $this->db->query($sql); if ($return) { @@ -429,17 +474,18 @@ class Loan extends CommonObject if (empty($this->labelStatus) || empty($this->labelStatusShort)) { global $langs; - //$langs->load("mymodule"); $this->labelStatus[self::STATUS_UNPAID] = $langs->trans('Unpaid'); $this->labelStatus[self::STATUS_PAID] = $langs->trans('Paid'); + $this->labelStatus[self::STATUS_STARTED] = $langs->trans("BillStatusStarted"); if ($status == 0 && $alreadypaid > 0) $this->labelStatus[self::STATUS_UNPAID] = $langs->trans("BillStatusStarted"); $this->labelStatusShort[self::STATUS_UNPAID] = $langs->trans('Unpaid'); $this->labelStatusShort[self::STATUS_PAID] = $langs->trans('Enabled'); + $this->labelStatusShort[self::STATUS_STARTED] = $langs->trans("BillStatusStarted"); if ($status == 0 && $alreadypaid > 0) $this->labelStatusShort[self::STATUS_UNPAID] = $langs->trans("BillStatusStarted"); } $statusType = 'status1'; - if ($status == 0 && $alreadypaid > 0) $statusType = 'status3'; + if (($status == 0 && $alreadypaid > 0) || $status == self::STATUS_STARTED) $statusType = 'status3'; if ($status == 1) $statusType = 'status6'; return dolGetStatus($this->labelStatus[$status], $this->labelStatusShort[$status], '', $statusType, $mode); diff --git a/htdocs/loan/class/loanschedule.class.php b/htdocs/loan/class/loanschedule.class.php index 7405a63e3b9..4fd4b526d25 100644 --- a/htdocs/loan/class/loanschedule.class.php +++ b/htdocs/loan/class/loanschedule.class.php @@ -76,6 +76,11 @@ class LoanSchedule extends CommonObject */ public $fk_bank; + /** + * @var int Loan Payment ID + */ + public $fk_payment_loan; + /** * @var int Bank ID */ @@ -198,27 +203,28 @@ class LoanSchedule extends CommonObject { global $langs; $sql = "SELECT"; - $sql .= " t.rowid,"; - $sql .= " t.fk_loan,"; - $sql .= " t.datec,"; - $sql .= " t.tms,"; - $sql .= " t.datep,"; - $sql .= " t.amount_capital,"; - $sql .= " t.amount_insurance,"; - $sql .= " t.amount_interest,"; - $sql .= " t.fk_typepayment,"; - $sql .= " t.num_payment,"; - $sql .= " t.note_private,"; - $sql .= " t.note_public,"; - $sql .= " t.fk_bank,"; - $sql .= " t.fk_user_creat,"; - $sql .= " t.fk_user_modif,"; - $sql .= " pt.code as type_code, pt.libelle as type_label,"; - $sql .= ' b.fk_account'; - $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as t"; - $sql .= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pt ON t.fk_typepayment = pt.id"; - $sql .= ' LEFT JOIN '.MAIN_DB_PREFIX.'bank as b ON t.fk_bank = b.rowid'; - $sql .= " WHERE t.rowid = ".$id; + $sql.= " t.rowid,"; + $sql.= " t.fk_loan,"; + $sql.= " t.datec,"; + $sql.= " t.tms,"; + $sql.= " t.datep,"; + $sql.= " t.amount_capital,"; + $sql.= " t.amount_insurance,"; + $sql.= " t.amount_interest,"; + $sql.= " t.fk_typepayment,"; + $sql.= " t.num_payment,"; + $sql.= " t.note_private,"; + $sql.= " t.note_public,"; + $sql.= " t.fk_bank,"; + $sql.= " t.fk_payment_loan,"; + $sql.= " t.fk_user_creat,"; + $sql.= " t.fk_user_modif,"; + $sql.= " pt.code as type_code, pt.libelle as type_label,"; + $sql.= ' b.fk_account'; + $sql.= " FROM ".MAIN_DB_PREFIX.$this->table_element." as t"; + $sql.= " LEFT JOIN ".MAIN_DB_PREFIX."c_paiement as pt ON t.fk_typepayment = pt.id"; + $sql.= ' LEFT JOIN '.MAIN_DB_PREFIX.'bank as b ON t.fk_bank = b.rowid'; + $sql.= " WHERE t.rowid = ".$id; dol_syslog(get_class($this)."::fetch", LOG_DEBUG); $resql = $this->db->query($sql); @@ -241,6 +247,7 @@ class LoanSchedule extends CommonObject $this->note_private = $obj->note_private; $this->note_public = $obj->note_public; $this->fk_bank = $obj->fk_bank; + $this->fk_payment_loan = $obj->fk_payment_loan; $this->fk_user_creat = $obj->fk_user_creat; $this->fk_user_modif = $obj->fk_user_modif; @@ -273,13 +280,14 @@ class LoanSchedule extends CommonObject $error = 0; // Clean parameters - if (isset($this->amount_capital)) $this->amount_capital = trim($this->amount_capital); - if (isset($this->amount_insurance)) $this->amount_insurance = trim($this->amount_insurance); - if (isset($this->amount_interest)) $this->amount_interest = trim($this->amount_interest); - if (isset($this->num_payment)) $this->num_payment = trim($this->num_payment); - if (isset($this->note_private)) $this->note_private = trim($this->note_private); - if (isset($this->note_public)) $this->note_public = trim($this->note_public); - if (isset($this->fk_bank)) $this->fk_bank = trim($this->fk_bank); + if (isset($this->amount_capital)) $this->amount_capital=trim($this->amount_capital); + if (isset($this->amount_insurance)) $this->amount_insurance=trim($this->amount_insurance); + if (isset($this->amount_interest)) $this->amount_interest=trim($this->amount_interest); + if (isset($this->num_payment)) $this->num_payment=trim($this->num_payment); + if (isset($this->note_private)) $this->note_private=trim($this->note_private); + if (isset($this->note_public)) $this->note_public=trim($this->note_public); + if (isset($this->fk_bank)) $this->fk_bank=trim($this->fk_bank); + if (isset($this->fk_payment_loan)) $this->fk_payment_loan = (int) $this->fk_payment_loan; // Check parameters // Put here code to add control on parameters values @@ -299,6 +307,7 @@ class LoanSchedule extends CommonObject $sql .= " note_private=".(isset($this->note_private) ? "'".$this->db->escape($this->note_private)."'" : "null").","; $sql .= " note_public=".(isset($this->note_public) ? "'".$this->db->escape($this->note_public)."'" : "null").","; $sql .= " fk_bank=".(isset($this->fk_bank) ? $this->fk_bank : "null").","; + $sql .= " fk_payment_loan=".(isset($this->fk_payment_loan) ? $this->fk_payment_loan : "null").","; $sql .= " fk_user_creat=".(isset($this->fk_user_creat) ? $this->fk_user_creat : "null").","; $sql .= " fk_user_modif=".(isset($this->fk_user_modif) ? $this->fk_user_modif : "null").""; @@ -405,6 +414,7 @@ class LoanSchedule extends CommonObject $sql .= " t.note_private,"; $sql .= " t.note_public,"; $sql .= " t.fk_bank,"; + $sql .= " t.fk_payment_loan,"; $sql .= " t.fk_user_creat,"; $sql .= " t.fk_user_modif"; $sql .= " FROM ".MAIN_DB_PREFIX.$this->table_element." as t"; @@ -433,6 +443,7 @@ class LoanSchedule extends CommonObject $line->note_private = $obj->note_private; $line->note_public = $obj->note_public; $line->fk_bank = $obj->fk_bank; + $line->fk_payment_loan = $obj->fk_payment_loan; $line->fk_user_creat = $obj->fk_user_creat; $line->fk_user_modif = $obj->fk_user_modif; diff --git a/htdocs/loan/class/paymentloan.class.php b/htdocs/loan/class/paymentloan.class.php index 84aca12394d..d50850cd949 100644 --- a/htdocs/loan/class/paymentloan.class.php +++ b/htdocs/loan/class/paymentloan.class.php @@ -1,6 +1,7 @@ - * Copyright (C) 2015-2020 Frederic France + * Copyright (C) 2015-2018 Frederic France + * Copyright (C) 2020 Maxime DEMAREST * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -366,6 +367,40 @@ class PaymentLoan extends CommonObject if (!$resql) { $error++; $this->errors[] = "Error ".$this->db->lasterror(); } } + // Set loan unpaid if loan has no other payment + if (!$error) + { + require_once DOL_DOCUMENT_ROOT.'/loan/class/loan.class.php'; + $loan = new Loan($this->db); + $loan->fetch($this->fk_loan); + $sum_payment = $loan->getSumPayment(); + if ($sum_payment == 0) + { + dol_syslog(get_class($this)."::delete : set loan to unpaid", LOG_DEBUG); + if ($loan->set_unpaid($user) < 1) + { + $error++; + dol_print_error($this->db); + } + } + } + + //if (! $error) + //{ + // if (! $notrigger) + // { + // Uncomment this and change MYOBJECT to your own tag if you + // want this action call a trigger. + + //// Call triggers + //include_once DOL_DOCUMENT_ROOT . '/core/class/interfaces.class.php'; + //$interface=new Interfaces($this->db); + //$result=$interface->run_triggers('MYOBJECT_DELETE',$this,$user,$langs,$conf); + //if ($result < 0) { $error++; $this->errors=$interface->errors; } + //// End call triggers + // } + //} + // Commit or rollback if ($error) { @@ -400,6 +435,7 @@ class PaymentLoan extends CommonObject global $conf; $error = 0; + $this->db->begin(); if (!empty($conf->banque->enabled)) { @@ -448,6 +484,7 @@ class PaymentLoan extends CommonObject } } + // Add link 'loan' in bank_url between invoice and bank transaction (for each invoice concerned by payment) if ($mode == 'payment_loan') { @@ -460,10 +497,32 @@ class PaymentLoan extends CommonObject } } + + // Set loan payment started if no set + if (!$error) + { + require_once DOL_DOCUMENT_ROOT.'/loan/class/loan.class.php'; + $loan = new Loan($this->db); + $loan->fetch($fk_loan); + if ($loan->paid == $loan::STATUS_UNPAID) + { + dol_syslog(get_class($this)."::addPaymentToBank : set loan payment to started", LOG_DEBUG); + if ($loan->set_started($user) < 1) + { + $error++; + dol_print_error($this->db); + } + } + } + if (!$error) { + $this->db->commit(); return 1; - } else { + } + else + { + $this->db->rollback(); return -1; } } diff --git a/htdocs/loan/payment/card.php b/htdocs/loan/payment/card.php index 31619be511b..4001037f887 100644 --- a/htdocs/loan/payment/card.php +++ b/htdocs/loan/payment/card.php @@ -57,11 +57,13 @@ if ($action == 'confirm_delete' && $confirm == 'yes' && $user->rights->loan->del $sql = "UPDATE ".MAIN_DB_PREFIX."loan_schedule SET fk_bank = 0 WHERE fk_bank = ".$payment->fk_bank; $db->query($sql); + $fk_loan = $payment->fk_loan; + $result = $payment->delete($user); if ($result > 0) { $db->commit(); - header("Location: ".DOL_URL_ROOT."/loan/list.php"); + header("Location: ".DOL_URL_ROOT."/loan/card.php?id=".$fk_loan); exit; } else { setEventMessages($payment->error, $payment->errors, 'errors'); diff --git a/htdocs/loan/payment/payment.php b/htdocs/loan/payment/payment.php index de5a43220ef..2443e44b510 100644 --- a/htdocs/loan/payment/payment.php +++ b/htdocs/loan/payment/payment.php @@ -1,6 +1,7 @@ * Copyright (C) 2015-2018 Frédéric France + * Copyright (C) 2020 Maxime DEMAREST * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,44 +28,50 @@ require_once DOL_DOCUMENT_ROOT.'/loan/class/loan.class.php'; require_once DOL_DOCUMENT_ROOT.'/loan/class/loanschedule.class.php'; require_once DOL_DOCUMENT_ROOT.'/loan/class/paymentloan.class.php'; require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/loan.lib.php'; $langs->loadLangs(array("bills", "loan")); -$chid = GETPOST('id', 'int'); -$action = GETPOST('action', 'aZ09'); -$cancel = GETPOST('cancel', 'alpha'); -$line_id = GETPOST('line_id', 'int'); -$last = GETPOST('last'); +$chid=GETPOST('id', 'int'); +$action=GETPOST('action', 'aZ09'); +$cancel=GETPOST('cancel', 'alpha'); +$datepaid = dol_mktime(12, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int')); // Security check $socid = 0; if ($user->socid > 0) -{ $socid = $user->socid; -} +else + if (GETPOSTISSET('socid')) $socid = GETPOST('socid', 'int'); +if (empty($user->rights->loan->write)) accessforbidden(); $loan = new Loan($db); $loan->fetch($chid); -if ($last) +$echance = 0; +$ls = new LoanSchedule($db); +// grab all loanschedule +$res = $ls->fetchAll($chid); +if ($res > 0) { - $ls = new LoanSchedule($db); - // grab all loanschedule - $res = $ls->fetchAll($chid); - if ($res > 0) + foreach ($ls->lines as $l) { - foreach ($ls->lines as $l) + $echance++; // Count term pos + // last unpaid term + if (empty($l->fk_bank)) { - // get the last unpaid loanschedule - if (empty($l->fk_bank)) - { - $line_id = $l->id; - break; - } + $line_id = $l->id; + break; + } + // If line_id provided, only count temp pos + elseif ($line_id == $l->id) + { + break; } } } +// Set current line with last unpaid line (only if shedule is used) if (!empty($line_id)) { $line = new LoanSchedule($db); @@ -73,6 +80,7 @@ if (!empty($line_id)) $amount_capital = price($line->amount_capital); $amount_insurance = price($line->amount_insurance); $amount_interest = price($line->amount_interest); + if (empty($datepaid)) $ts_temppaid = $line->datep; } } @@ -92,9 +100,7 @@ if ($action == 'add_payment') exit; } - $datepaid = dol_mktime(12, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int')); - - if (!GETPOST('paymenttype', 'int') > 0) + if (! GETPOST('paymenttype', 'int') > 0) { setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentities("PaymentMode")), null, 'errors'); $error++; @@ -114,7 +120,27 @@ if ($action == 'add_payment') { $paymentid = 0; - $amount = GETPOST('amount_capital') + GETPOST('amount_insurance') + GETPOST('amount_interest'); + $pay_amount_capital = price2num(GETPOST('amount_capital')); + $pay_amount_insurance = price2num(GETPOST('amount_insurance')); + // User can't set interest him self if schedule is set (else value in schedule can be incoherent) + if (!empty($line)) $pay_amount_interest = $line->amount_interest; + else $pay_amount_interest = price2num(GETPOST('amount_interest')); + $remaindertopay = price2num(GETPOST('remaindertopay')); + $amount = $pay_amount_capital + $pay_amount_insurance + $pay_amount_interest; + + // This term is allready paid + if (!empty($line) && !empty($line->fk_bank)) + { + setEventMessages($langs->trans('TermPaidAllreadyPaid'), null, 'errors'); + $error++; + } + + if (empty($remaindertopay)) + { + setEventMessages('Empty sumpaid', null, 'errors'); + $error++; + } + if ($amount == 0) { setEventMessages($langs->trans('ErrorNoPaymentDefined'), null, 'errors'); @@ -127,14 +153,14 @@ if ($action == 'add_payment') // Create a line of payments $payment = new PaymentLoan($db); - $payment->chid = $chid; - $payment->datep = $datepaid; - $payment->label = $loan->label; - $payment->amount_capital = GETPOST('amount_capital'); - $payment->amount_insurance = GETPOST('amount_insurance'); - $payment->amount_interest = GETPOST('amount_interest'); - $payment->paymenttype = GETPOST('paymenttype', 'int'); - $payment->num_payment = GETPOST('num_payment'); + $payment->chid = $chid; + $payment->datep = $datepaid; + $payment->label = $loan->label; + $payment->amount_capital = $pay_amount_capital; + $payment->amount_insurance = $pay_amount_interest; + $payment->amount_interest = $pay_amount_insurance; + $payment->paymenttype = GETPOST('paymenttype', 'int'); + $payment->num_payment = GETPOST('num_payment'); $payment->note_private = GETPOST('note_private', 'none'); $payment->note_public = GETPOST('note_public', 'none'); @@ -155,14 +181,52 @@ if ($action == 'add_payment') { setEventMessages($payment->error, $payment->errors, 'errors'); $error++; - } elseif (isset($line)) - { - $line->fk_bank = $payment->fk_bank; - $line->update($user); } } - if (!$error) + // Update loan schedule with payment value + if (! $error && !empty($line)) + { + // If payment values are modified, recalculate schedule + if (($line->amount_capital <> $pay_amount_capital) || ($line->amount_insurance <> $pay_amount_insurance) || ($line->amount_interest <> $pay_amount_interest)) + { + $arr_term = loan_calcmens(($pay_amount_capital + $pay_amount_interest), $remaindertopay, ($loan->rate / 100), $echance, $loan->nbterm); + foreach ($arr_term as $k=>$v) + { + // Update fk_bank for current line + if ($k == $echance) + { + $ls->lines[$k-1]->fk_bank = $payment->fk_bank; + $ls->lines[$k-1]->fk_payment_loan = $payment->id; + } + $ls->lines[$k-1]->amount_capital = $v['mens'] - $v['interet']; + $ls->lines[$k-1]->amount_interest = $v['interet']; + $ls->lines[$k-1]->tms = dol_now(); + $ls->lines[$k-1]->fk_user_modif = $user->id; + $result = $ls->lines[$k-1]->update($user, 0); + if ($result < 1) + { + setEventMessages(null, $ls->errors, 'errors'); + $error++; + break; + } + + } + } + else // Only add fk_bank bank to schedule line (mark as paid) + { + $line->fk_bank = $payment->fk_bank; + $line->fk_payment_loan = $payment->id; + $result = $line->update($user, 0); + if ($result < 1) + { + setEventMessages(null, $line->errors, 'errors'); + $error++; + } + } + } + + if (! $error) { $db->commit(); $loc = DOL_URL_ROOT.'/loan/card.php?id='.$chid; @@ -194,11 +258,23 @@ if ($action == 'create') print load_fiche_titre($langs->trans("DoPayment")); + $sql = "SELECT SUM(amount_capital) as total"; + $sql.= " FROM ".MAIN_DB_PREFIX."payment_loan"; + $sql.= " WHERE fk_loan = ".$chid; + $resql = $db->query($sql); + if ($resql) + { + $obj=$db->fetch_object($resql); + $sumpaid = $obj->total; + $db->free(); + } + print '
'; print ''; print ''; print ''; print ''; + print ''; print ''; dol_fiche_head(); @@ -208,20 +284,14 @@ if ($action == 'create') print ''.$langs->trans("Loan").''; print ''.$langs->trans("Ref").''.$chid.''; + if ($echance > 0) + { + print ''.$langs->trans("Term").''.$echance.''."\n"; + } print ''.$langs->trans("DateStart").''.dol_print_date($loan->datestart, 'day')."\n"; print ''.$langs->trans("Label").''.$loan->label."\n"; print ''.$langs->trans("Amount").''.price($loan->capital, 0, $outputlangs, 1, -1, -1, $conf->currency).''; - $sql = "SELECT SUM(amount_capital) as total"; - $sql .= " FROM ".MAIN_DB_PREFIX."payment_loan"; - $sql .= " WHERE fk_loan = ".$chid; - $resql = $db->query($sql); - if ($resql) - { - $obj = $db->fetch_object($resql); - $sumpaid = $obj->total; - $db->free(); - } print ''.$langs->trans("AlreadyPaid").''.price($sumpaid, 0, $outputlangs, 1, -1, -1, $conf->currency).''; print ''.$langs->trans("RemainderToPay").''.price($total - $sumpaid, 0, $outputlangs, 1, -1, -1, $conf->currency).''; print ''; @@ -236,10 +306,13 @@ if ($action == 'create') print ''; print ''.$langs->trans("Date").''; - $datepaid = dol_mktime(12, 0, 0, GETPOST('remonth', 'int'), GETPOST('reday', 'int'), GETPOST('reyear', 'int')); - $datepayment = empty($conf->global->MAIN_AUTOFILL_DATE) ? (empty($_POST["remonth"]) ?-1 : $datepaye) : 0; + if (empty($datepaid)) + if (empty($ts_temppaid)) $datepayment = empty($conf->global->MAIN_AUTOFILL_DATE)?-1:dol_now(); + else $datepayment = $ts_temppaid; + else $datepayment = $datepaid; print $form->selectDate($datepayment, '', '', '', '', "add_payment", 1, 1); print ""; + print ''; print ''.$langs->trans("PaymentMode").''; @@ -250,7 +323,7 @@ if ($action == 'create') print ''; print ''.$langs->trans('AccountToDebit').''; print ''; - $form->select_comptes(isset($_POST["accountid"]) ? $_POST["accountid"] : $loan->accountid, "accountid", 0, '', 1); // Show opend bank account list + $form->select_comptes(isset($_POST["accountid"])?$_POST["accountid"]:$loan->accountid, "accountid", 0, 'courant = '.Account::TYPE_CURRENT, 1); // Show opend bank account list print ''; // Number @@ -300,22 +373,28 @@ if ($action == 'create') print ''; if ($sumpaid < $loan->capital) { - print $langs->trans("LoanCapital").': '; - } else { + print $langs->trans("LoanCapital") .': '; + } + else + { print '-'; } print '
'; if ($sumpaid < $loan->capital) { - print $langs->trans("Insurance").': '; - } else { + print $langs->trans("Insurance") .': '; + } + else + { print '-'; } print '
'; if ($sumpaid < $loan->capital) { - print $langs->trans("Interest").': '; - } else { + print $langs->trans("Interest") .': '; + } + else + { print '-'; } print ""; diff --git a/htdocs/loan/schedule.php b/htdocs/loan/schedule.php index adcdf4ca049..d62eb8a212b 100644 --- a/htdocs/loan/schedule.php +++ b/htdocs/loan/schedule.php @@ -1,6 +1,7 @@ * Copyright (C) 2018 Alexandre Spangaro + * Copyright (C) 2020 Maxime DEMAREST * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,16 +28,102 @@ require_once DOL_DOCUMENT_ROOT.'/loan/class/loan.class.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/loan.lib.php'; require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php'; require_once DOL_DOCUMENT_ROOT.'/loan/class/loanschedule.class.php'; +require_once DOL_DOCUMENT_ROOT.'/loan/class/paymentloan.class.php'; $loanid = GETPOST('loanid', 'int'); $action = GETPOST('action', 'aZ09'); -$object = new Loan($db); -$object->fetch($loanid); +// Security check +$socid = 0; +if (GETPOSTISSET('socid')) $socid = GETPOST('socid', 'int'); +if ($user->socid) $socid=$user->socid; +if (empty($user->rights->loan->calc)) accessforbidden(); // Load translation files required by the page $langs->loadLangs(array("compta", "bills", "loan")); +$object = new Loan($db); +$object->fetch($loanid); + +$echeances = new LoanSchedule($db); +$echeances->fetchAll($object->id); + +if ($object->paid > 0 && count($echeances->lines) == 0) $pay_without_schedule = 1; + +/* + * Actions + */ + +if ($action == 'createecheancier' && empty($pay_without_schedule)) { + $db->begin(); + $i = 1; + while ($i < $object->nbterm + 1) { + $date = GETPOST('hi_date'.$i, 'int'); + $mens = price2num(GETPOST('mens'.$i)); + $int = price2num(GETPOST('hi_interets'.$i)); + $insurance = price2num(GETPOST('hi_insurance'.$i)); + + $new_echeance = new LoanSchedule($db); + + $new_echeance->fk_loan = $object->id; + $new_echeance->datec = dol_now(); + $new_echeance->tms = dol_now(); + $new_echeance->datep = $date; + $new_echeance->amount_capital = $mens - $int; + $new_echeance->amount_insurance = $insurance; + $new_echeance->amount_interest = $int; + $new_echeance->fk_typepayment = 3; + $new_echeance->fk_bank = 0; + $new_echeance->fk_user_creat = $user->id; + $new_echeance->fk_user_modif = $user->id; + $result = $new_echeance->create($user); + if ($result < 0) { + setEventMessages($new_echeance->error, $echeance->errors, 'errors'); + $db->rollback(); + unset($echeances->lines); + break; + } + $echeances->lines[] = $new_echeance; + $i++; + } + var_dump($result); + if ($result > 0) $db->commit(); +} + +if ($action == 'updateecheancier' && empty($pay_without_schedule)) { + $db->begin(); + $i = 1; + while ($i < $object->nbterm + 1) { + $mens = price2num(GETPOST('mens'.$i)); + $int = price2num(GETPOST('hi_interets'.$i)); + $id = GETPOST('hi_rowid'.$i); + $insurance = price2num(GETPOST('hi_insurance'.$i)); + + $new_echeance = new LoanSchedule($db); + $new_echeance->fetch($id); + $new_echeance->tms = dol_now(); + $new_echeance->amount_capital = $mens - $int; + $new_echeance->amount_insurance = $insurance; + $new_echeance->amount_interest = $int; + $new_echeance->fk_user_modif = $user->id; + $result = $new_echeance->update($user, 0); + if ($result < 0) { + setEventMessages(null, $new_echeance->errors, 'errors'); + $db->rollback(); + $echeances->fetchAll($object->id); + break; + } + + $echeances->lines[$i-1] = $new_echeance; + $i++; + } + if ($result > 0) $db->commit(); +} + +/* + * View + */ + $title = $langs->trans("Loan").' - '.$langs->trans("Card"); $help_url = 'EN:Module_Loan|FR:Module_Emprunt'; llxHeader("", $title, $help_url); @@ -86,73 +173,17 @@ $morehtmlref .= '
'; $morehtmlright = ''; -dol_banner_tab($object, 'id', $linkback, 1, 'rowid', 'ref', $morehtmlref, '', 0, '', $morehtmlright); - -if ($action == 'createecheancier') { - $i = 1; - while ($i < $object->nbterm + 1) { - $date = GETPOST('hi_date'.$i, 'int'); - $mens = GETPOST('mens'.$i); - $int = GETPOST('hi_interets'.$i); - $insurance = GETPOST('hi_insurance'.$i); - - $echeance = new LoanSchedule($db); - - $echeance->fk_loan = $object->id; - $echeance->datec = dol_now(); - $echeance->tms = dol_now(); - $echeance->datep = $date; - $echeance->amount_capital = $mens - $int; - $echeance->amount_insurance = $insurance; - $echeance->amount_interest = $int; - $echeance->fk_typepayment = 3; - $echeance->fk_bank = 0; - $echeance->fk_user_creat = $user->id; - $echeance->fk_user_modif = $user->id; - $result = $echeance->create($user); - if ($result < 0) { - setEventMessages($echeance->error, $echeance->errors, 'errors'); - } - $i++; - } -} - -if ($action == 'updateecheancier') { - $i = 1; - while ($i < $object->nbterm + 1) { - $mens = GETPOST('mens'.$i); - $int = GETPOST('hi_interets'.$i); - $id = GETPOST('hi_rowid'.$i); - $insurance = GETPOST('hi_insurance'.$i); - - $echeance = new LoanSchedule($db); - $echeance->fetch($id); - $echeance->tms = dol_now(); - $echeance->amount_capital = $mens - $int; - $echeance->amount_insurance = $insurance; - $echeance->amount_interest = $int; - $echeance->fk_user_modif = $user->id; - $result = $echeance->update($user, 0); - if ($result < 0) { - setEventMessages(null, $echeance->errors, 'errors'); - } - $i++; - } -} - -$echeance = new LoanSchedule($db); -$echeance->fetchAll($object->id); - +dol_banner_tab($object, 'loanid', $linkback, 1, 'rowid', 'ref', $morehtmlref, '', 0, '', $morehtmlright); ?> '.$langs->trans('CantUseScheduleWithLoanStartedToPaid').''."\n"; print ''; print ''; print ''; -if (count($echeance->lines) > 0) +if (count($echeances->lines) > 0) { print ''; } else { @@ -193,7 +226,7 @@ print '
'; print ''; print ''; $colspan = 6; -if (count($echeance->lines) > 0) $colspan++; +if (count($echeances->lines) > 0) $colspan++; print ''; @@ -209,10 +242,10 @@ print ''; -if (count($echeance->lines) > 0) print ''; +if (count($echeances->lines) > 0) print ''; print ''."\n"; -if ($object->nbterm > 0 && count($echeance->lines) == 0) +if ($object->nbterm > 0 && count($echeances->lines) == 0) { $i = 1; $capital = $object->capital; @@ -221,7 +254,7 @@ if ($object->nbterm > 0 && count($echeance->lines) == 0) $regulInsurance = price2num($object->insurance_amount - ($insurance * $object->nbterm)); while ($i < $object->nbterm + 1) { - $mens = price2num($echeance->calcMonthlyPayments($capital, $object->rate / 100, $object->nbterm - $i + 1), 'MT'); + $mens = price2num($echeances->calcMonthlyPayments($capital, $object->rate / 100, $object->nbterm - $i + 1), 'MT'); $int = ($capital * ($object->rate / 12)) / 100; $int = price2num($int, 'MT'); $insu = ($insurance + (($i == 1) ? $regulInsurance : 0)); @@ -237,7 +270,7 @@ if ($object->nbterm > 0 && count($echeance->lines) == 0) $i++; $capital = $cap_rest; } -} elseif (count($echeance->lines) > 0) +} elseif (count($echeances->lines) > 0) { $i = 1; $capital = $object->capital; @@ -245,7 +278,7 @@ if ($object->nbterm > 0 && count($echeance->lines) == 0) $insurance = price2num($insurance, 'MT'); $regulInsurance = price2num($object->insurance_amount - ($insurance * $object->nbterm)); $printed = false; - foreach ($echeance->lines as $line) { + foreach ($echeances->lines as $line) { $mens = $line->amount_capital + $line->amount_interest; $int = $line->amount_interest; $insu = ($insurance + (($i == 1) ? $regulInsurance : 0)); @@ -256,7 +289,7 @@ if ($object->nbterm > 0 && count($echeance->lines) == 0) print ''; print ''; print ''; - if ($line->datep > dol_now() && empty($line->fk_bank)) { + if (empty($line->fk_bank)) { print ''; } else { print ''; @@ -264,10 +297,15 @@ if ($object->nbterm > 0 && count($echeance->lines) == 0) print ''; print ''; @@ -282,10 +320,9 @@ print ''; print '
'; - -if (count($echeance->lines) == 0) $label = $langs->trans("Create"); +if (count($echeances->lines) == 0) $label = $langs->trans("Create"); else $label = $langs->trans("Save"); -print '
'; +print '
'; print ''; // End of page
'; print $langs->trans("FinancialCommitment"); print ''.$langs->trans("CapitalRemain"); print '
('.price($object->capital, 0, '', 1, -1, -1, $conf->currency).')'; print ''; print '
'.$langs->trans('DoPayment').''.$langs->trans('DoPayment').'
'.dol_print_date($line->datep, 'day').''.price($insu, 0, '', 1, -1, -1, $conf->currency).''.price($int, 0, '', 1, -1, -1, $conf->currency).''.price($mens, 0, '', 1, -1, -1, $conf->currency).''.price($cap_rest, 0, '', 1, -1, -1, $conf->currency).''; - if (!empty($line->fk_bank)) print $langs->trans('Paid'); + if (!empty($line->fk_bank)) + { + print $langs->trans('Paid'); + if (!empty($line->fk_payment_loan)) + print ' '.'('.img_object($langs->trans("Payment"), "payment").' '.$line->fk_payment_loan.')'.''; + } elseif (!$printed) { - print ''.$langs->trans('DoPayment').''; + print ''.$langs->trans('DoPayment').''; $printed = true; } print '