396 lines
14 KiB
Java
396 lines
14 KiB
Java
package com.tenwa.reckon.util;
|
||
|
||
import java.math.BigDecimal;
|
||
import java.text.ParseException;
|
||
import java.text.SimpleDateFormat;
|
||
import java.util.ArrayList;
|
||
import java.util.Calendar;
|
||
import java.util.Date;
|
||
import java.util.List;
|
||
|
||
import com.tenwa.reckon.bean.CalculationCondition;
|
||
import com.tenwa.reckon.bean.ConditionBean;
|
||
import com.tenwa.reckon.bean.FundRentPlanBean;
|
||
import com.tenwa.reckon.bean.RentPlanInfo;
|
||
import com.tenwa.reckon.constant.Scale;
|
||
|
||
|
||
public class RentCalculateUtil {
|
||
|
||
public static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
|
||
|
||
/**
|
||
* 计算两个日期之间的差的月数。
|
||
* 月付时:1月31号第一期,则第二期是2月28号或者29号。
|
||
* 这个需要判断,不能直接加一个月,否则会变成3月2号的
|
||
*
|
||
* @param beginDate
|
||
* @param endDate
|
||
* @return
|
||
* @throws Exception
|
||
*/
|
||
public static int getDiffMonth(Date beginDate, Date endDate) throws Exception {
|
||
Calendar calendar = Calendar.getInstance();
|
||
calendar.setTime(beginDate);
|
||
int beginYear = calendar.get(Calendar.YEAR);
|
||
int beginMonth = calendar.get(Calendar.MONTH);
|
||
calendar.setTime(endDate);
|
||
int endYear = calendar.get(Calendar.YEAR);
|
||
int endMonth = calendar.get(Calendar.MONTH);
|
||
int difMonth = (endYear-beginYear)*12+(endMonth-beginMonth);
|
||
return difMonth;
|
||
}
|
||
|
||
|
||
// /**
|
||
// * 初步获取一个空的日历计划,只为生成一个空架子,为了其他的测算做数据模板。 只有有还款期数编号,int类型1-n,还款日期。
|
||
// * 没有租金、本金和利息等数据。
|
||
// *
|
||
// * @param condition
|
||
// * @return
|
||
// * @throws ParseException
|
||
// */
|
||
// public static List<RentPlanInfo> getEmptyPlanModel(CalculationCondition condition) throws Exception {
|
||
// int incomeTimes = condition.getIncomeTimes();// 期数
|
||
// int issueNumber = condition.getIssueNumber();// 每期几个月
|
||
// int grace = condition.getGrace();// 宽限期
|
||
//
|
||
// Calendar calendar = Calendar.getInstance();// 推算日期的日历,推算前需要先找到第1期租金实际支付日
|
||
// Date startDate = condition.getStartDate();// 起租日期,计算最后一期的还款日期
|
||
// Date firstPlanDate = condition.getFirstPlanDate();// 计划第1期日期
|
||
// if(firstPlanDate == null){
|
||
// calendar.setTime(startDate);
|
||
// calendar.add(Calendar.MONTH, issueNumber * condition.getPeriodType());
|
||
// firstPlanDate = calendar.getTime();
|
||
// while(getDiffMonth(startDate, firstPlanDate) > issueNumber * condition.getPeriodType()){
|
||
// calendar.add(Calendar.DAY_OF_MONTH, -1);
|
||
// firstPlanDate = calendar.getTime();
|
||
// }
|
||
// }
|
||
//
|
||
// // 计算结束租期日子
|
||
//
|
||
// RentPlanInfo temp = null;
|
||
// List<RentPlanInfo> result = new ArrayList<RentPlanInfo>();
|
||
// // 计算期数
|
||
// for (int i = 0; i < grace + incomeTimes; i++) {
|
||
// temp = new RentPlanInfo();
|
||
// temp.setId(i + 1);// 设置期数
|
||
// temp.setBusinessId(condition.getId());// 设置业务ID
|
||
// if(i == 0){
|
||
// temp.setStartDate(startDate);
|
||
// temp.setEndDate(firstPlanDate);
|
||
// } else {
|
||
// temp.setStartDate(result.get(i - 1).getEndDate());
|
||
// calendar.setTime(firstPlanDate);
|
||
// calendar.add(Calendar.MONTH, issueNumber * i);
|
||
// Date endDateTemp = calendar.getTime();
|
||
// while(getDiffMonth(firstPlanDate, endDateTemp) > issueNumber * i){
|
||
// calendar.add(Calendar.DAY_OF_MONTH, -1);
|
||
// endDateTemp = calendar.getTime();
|
||
// }
|
||
// temp.setEndDate(endDateTemp);
|
||
// }
|
||
// result.add(temp);
|
||
// logger.info("租金测算[RentCalculateUtil]获取租金计划模板:" + dateFormat.format(temp.getEndDate()));
|
||
// }
|
||
// return result;
|
||
// }
|
||
|
||
/**
|
||
* 初步获取一个空的日历计划,只为生成一个空架子,为了其他的测算做数据模板。 只有有还款期数编号,int类型1-n,还款日期。
|
||
* 没有租金、本金和利息等数据。
|
||
* 压栈处理租金计划日期
|
||
* @param condition
|
||
* @return
|
||
* @throws ParseException
|
||
*/
|
||
public static List<RentPlanInfo> getEmptyPlanModel(CalculationCondition condition) throws Exception {
|
||
|
||
Date startDate = condition.getStartDate();// 起租日期,计算最后一期的还款日期
|
||
Date firstPlanDate = condition.getFirstPlanDate();// 计划第1期日期
|
||
Date secondPlanDate = condition.getSecondPlanDate();// 计划第2期日期
|
||
List<Date> planDateList = new ArrayList<Date>();
|
||
planDateList.add(startDate);// 必须有起始日期
|
||
if(firstPlanDate != null){
|
||
planDateList.add(firstPlanDate);
|
||
if(secondPlanDate != null){
|
||
planDateList.add(secondPlanDate);
|
||
}
|
||
} else {
|
||
// 起租类型 注意: 期初 数字 1 字符串 period_type_1 #分割线# 期末 数字0 字符串 period_type_0
|
||
if(condition.getPeriodType() == 1){// 期初
|
||
planDateList.add(startDate);//第一期和起租日期一样
|
||
}
|
||
}
|
||
|
||
int incomeTimes = condition.getIncomeTimes();// 期数
|
||
int grace = condition.getGrace();// 宽限期
|
||
int issueNumber = condition.getIssueNumber();// 每期几个月
|
||
// 计算结束租期日子
|
||
Calendar calendar = Calendar.getInstance();// 推算日期的日历
|
||
Date reference = planDateList.get(planDateList.size() - 1);
|
||
for (int i = 1; planDateList.size() < grace + incomeTimes + 1; i++) {
|
||
calendar.setTime(reference);
|
||
calendar.add(Calendar.MONTH, issueNumber * i);
|
||
Date tempDate = calendar.getTime();
|
||
while(getDiffMonth(reference, tempDate) > issueNumber * i){
|
||
calendar.add(Calendar.DAY_OF_MONTH, -1);
|
||
tempDate = calendar.getTime();
|
||
}
|
||
planDateList.add(tempDate);
|
||
}
|
||
|
||
RentPlanInfo temp = null;
|
||
List<RentPlanInfo> result = new ArrayList<RentPlanInfo>();
|
||
for (int i = 1; i < grace + incomeTimes + 1; i++){
|
||
temp = new RentPlanInfo();
|
||
temp.setId(i);
|
||
temp.setStartDate(planDateList.get(i - 1));
|
||
temp.setEndDate(planDateList.get(i));
|
||
result.add(temp);
|
||
}
|
||
return result;
|
||
}
|
||
|
||
|
||
/**
|
||
* 返回两时间间隔天数 bdate--开始时间字符串 edate--结束时间字符串
|
||
*
|
||
* @param startDate
|
||
* @param endDate
|
||
* @return
|
||
*/
|
||
public static long getDateDiff(Date startDate, Date endDate) {
|
||
try {
|
||
// 86400000毫秒,一天的时间
|
||
long datediff = endDate.getTime() - startDate.getTime();
|
||
datediff = datediff / 86400000;
|
||
return datediff;
|
||
} catch (Exception e) {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 返回两时间间隔天数 bdate--开始时间字符串 edate--结束时间字符串
|
||
*
|
||
* @param startDate
|
||
* @param endDate
|
||
* @return
|
||
*/
|
||
public static long getDateDiff(String startDate, String endDateStr) {
|
||
try {
|
||
// 86400000毫秒,一天的时间
|
||
Date beginDate = dateFormat.parse(startDate);
|
||
Date endDate = dateFormat.parse(endDateStr);
|
||
|
||
long datediff = endDate.getTime() - beginDate.getTime();
|
||
datediff = datediff / 86400000;
|
||
return datediff;
|
||
} catch (Exception e) {
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 起租日和首期租金支付日的差额,
|
||
* 如果选择期末支付 计算第一期缺少的天数当中的利息,从第一期利息中减去
|
||
* 如起租日期是1号,第一期租金支付日是25号,那么会计算出25-30号的利息,
|
||
* 第一期利息需要减去这个利息。
|
||
*
|
||
* @param condition
|
||
* @return
|
||
*/
|
||
public static BigDecimal getFirstInterestDiff(CalculationCondition condition) {
|
||
/**
|
||
BigDecimal interest = new BigDecimal(0);
|
||
Date startDate = condition.getStartDate();//起租日期
|
||
Date firstPlanDate = condition.getFirstPlanDate();//第一期租金支付日
|
||
|
||
Calendar calendar = Calendar.getInstance();
|
||
calendar.setTime(startDate);
|
||
if(condition.getPeriodType() == 1){//期末的话,应该加一期,才是理论上的第一期租金支付日
|
||
calendar.add(Calendar.MONTH, condition.getIssueNumber());// 计算实际应该的第一期租金日期
|
||
}
|
||
|
||
// 计算预期的起租日期和第一期租金支付日中间的日期差
|
||
long diff = RentCalculateUtil.getDateDiff(calendar.getTime(), firstPlanDate);
|
||
BigDecimal leaseAmt = condition.getLeaseAmt();
|
||
BigDecimal dayRate = condition.getDayRate();
|
||
interest = leaseAmt.multiply(dayRate).multiply(new BigDecimal(diff)).setScale(Scale.INTEREST_SCALE, BigDecimal.ROUND_HALF_UP);
|
||
logger.info("租金测算[RentCalculateUtil]租前息:" + interest.toString());
|
||
return interest;
|
||
**/
|
||
return BigDecimal.ZERO;
|
||
}
|
||
|
||
/**
|
||
* 等额本金法 计算 租金 ,本金 ,利息
|
||
* 租金未 保留2位小数
|
||
* 本金已保留 2位小数 最后一期已做处理
|
||
* 利息 未保留2位小数
|
||
* @param condition
|
||
* @return
|
||
* @throws ParseException
|
||
*/
|
||
public static void calculateForSameCorpus(ConditionBean cb,CalculationCondition condition,List<String> corpusList,String type,List<String> interestDateList,FundRentPlanBean frpb) throws Exception {
|
||
|
||
List<String> rentList=new ArrayList<String>();
|
||
List<String> businessCorpusList=new ArrayList<String>();
|
||
List<String> interestList=new ArrayList<String>();
|
||
|
||
BigDecimal leaseAmt = condition.getCleanLeaseMoney();// 融资额
|
||
int incomeTimes = condition.getIncomeTimes();// 期数
|
||
|
||
BigDecimal issueRate = condition.getIssueRate();// 期利率
|
||
BigDecimal leaseAmtRemain = leaseAmt;// 剩余本金
|
||
BigDecimal finalPayment = new BigDecimal(cb.getFinalPayment());
|
||
//求出日利率(按365天算)
|
||
for (int i = 0; i < interestDateList.size(); i++) {
|
||
BigDecimal corpus = null;
|
||
if("one_1".equals(cb.getCorpusType())){
|
||
corpus = ( corpusList == null || corpusList.size() <= 0 )
|
||
? new BigDecimal("0")
|
||
: new BigDecimal(corpusList.get(i));// 本金
|
||
}else if("three_1".equals(cb.getCorpusType())){
|
||
corpus = ( corpusList == null || corpusList.size() <= 0 )
|
||
? leaseAmt.subtract(condition.getEquipEndValue()).divide(new BigDecimal(incomeTimes/3), Scale.CORPUS_SCALE, BigDecimal.ROUND_HALF_EVEN)
|
||
: new BigDecimal(corpusList.get(i));// 本金
|
||
}else{
|
||
corpus = ( corpusList == null || corpusList.size() <= 0 )
|
||
? finalPayment.compareTo(BigDecimal.ONE) != 0 && "final_payment_method02".equals(cb.getFinalPaymentMethod()) ?
|
||
leaseAmt.subtract(condition.getEquipEndValue()).subtract(finalPayment).divide(new BigDecimal(incomeTimes), Scale.CORPUS_SCALE, BigDecimal.ROUND_HALF_EVEN)
|
||
: leaseAmt.subtract(condition.getEquipEndValue()).divide(new BigDecimal(incomeTimes), Scale.CORPUS_SCALE, BigDecimal.ROUND_HALF_EVEN)
|
||
: new BigDecimal(corpusList.get(i));// 本金
|
||
}
|
||
BigDecimal interest = BigDecimal.ZERO;
|
||
if(type.equalsIgnoreCase("issue")){
|
||
interest = leaseAmtRemain.multiply(issueRate).setScale(Scale.RATE_SCALE, BigDecimal.ROUND_HALF_UP);
|
||
}else{
|
||
//天数差
|
||
BigDecimal dayRate = RateTools.getPreDayRate(condition.getYearRate().toString(),type);
|
||
long days = 0;
|
||
if(i == 0){//第一期
|
||
days = DateTools.getDateDiff(DateUtil.getTimeByFormat(interestDateList.get(i),"yyyy/MM/dd") ,condition.getStartDate());
|
||
}else{
|
||
days = DateTools.getDateDiff(DateUtil.getTimeByFormat(interestDateList.get(i),"yyyy/MM/dd"),DateUtil.getTimeByFormat(interestDateList.get(i-1),"yyyy/MM/dd"));
|
||
}
|
||
interest = leaseAmtRemain.multiply(dayRate).multiply(new BigDecimal(days)).setScale(Scale.RATE_SCALE, BigDecimal.ROUND_HALF_UP);
|
||
}
|
||
|
||
if (i < condition.getGrace()) {
|
||
corpus = new BigDecimal(0);// 宽限期内只收利息
|
||
} else {
|
||
// 宽限期结束后第一期,如果是期初支付租金,不计利息
|
||
if (i == condition.getGrace()) {
|
||
if (condition.getPeriodType() == 0) {
|
||
interest = new BigDecimal("0");
|
||
}
|
||
}
|
||
}
|
||
|
||
if(i == 0){
|
||
interest = interest.add(getFirstInterestDiff(condition));
|
||
}
|
||
if("three_1".equals(cb.getCorpusType()) && (i + 1) % 3 != 0){
|
||
corpus = new BigDecimal("0");
|
||
}
|
||
if(i == interestDateList.size() -1){
|
||
corpus = leaseAmtRemain.subtract(condition.getEquipEndValue());
|
||
}
|
||
//租金圆整
|
||
BigDecimal rent=interest.add(corpus);//租金
|
||
if(condition.getRentRound().length()>0){
|
||
rent=new BigDecimal(NumberUtils.rentRound(rent.toString(),condition.getRentRound(),condition.getRentRoundType()));
|
||
}
|
||
rentList.add(rent.toString());
|
||
businessCorpusList.add(corpus.toString());
|
||
interestList.add(rent.subtract(corpus).toString());
|
||
|
||
leaseAmtRemain = leaseAmtRemain.subtract(corpus);// 剩余本金重置
|
||
}
|
||
frpb.setRentList(rentList);
|
||
frpb.setCorpusBusinessList(businessCorpusList);
|
||
frpb.setInterestBusinessList(interestList);
|
||
}
|
||
|
||
|
||
/**
|
||
* 根据租金计划和商务条件获取财务IRR,和财务收益率不一样
|
||
*
|
||
* @param rentPlan
|
||
* @param condition
|
||
* @return
|
||
* @throws ParseException
|
||
*/
|
||
public static BigDecimal getFinanceYearRate(CalculationCondition condition, List<RentPlanInfo> rentPlan) throws ParseException {
|
||
List<BigDecimal> inflowPour = new ArrayList<BigDecimal>();
|
||
for (int i = condition.getGrace(); i < rentPlan.size(); i++) {//财务irr的计算不算宽限期的
|
||
RentPlanInfo rpi = rentPlan.get(i);
|
||
inflowPour.add(rpi.getRent());
|
||
}
|
||
|
||
BigDecimal leaseAmt = new BigDecimal(-1).multiply(condition.getLeaseAmt());
|
||
// 起租类型 注意: 期初 数字 1 字符串 period_type_1 #分割线# 期末 数字0 字符串 period_type_0
|
||
if(condition.getPeriodType() <= 0){//期末的话把总本金加在数据列表的第一个,为了配合getIRR方法
|
||
inflowPour.add(0, leaseAmt);
|
||
} else {
|
||
inflowPour.set(0, inflowPour.get(0).add(leaseAmt));//期初的话与第一期租金合并
|
||
}
|
||
BigDecimal irr = IRRCalculateUtil.getIRR(inflowPour, condition);
|
||
return irr;
|
||
}
|
||
|
||
/**
|
||
* 根据商务条件和租金计划填充租金计划中的财务本金和财务利息
|
||
*
|
||
* @param condition 商务条件
|
||
* @param rentPlan 租金计划数据列表
|
||
* @throws ParseException
|
||
*/
|
||
public static void calculateFinacesPlan(CalculationCondition condition, List<RentPlanInfo> rentPlan) throws ParseException {
|
||
BigDecimal irr = getFinanceYearRate(condition, rentPlan);
|
||
BigDecimal issueRateFina = irr.divide(new BigDecimal(12 / condition.getIssueNumber()), Scale.RATE_SCALE, BigDecimal.ROUND_HALF_EVEN);
|
||
BigDecimal issueRate = condition.getIssueRate();// 期利率
|
||
BigDecimal leaseAmtRemain = condition.getLeaseAmt();// 剩余本金
|
||
for (int i = 0; i < rentPlan.size(); i++) {
|
||
RentPlanInfo rpi = rentPlan.get(i);
|
||
BigDecimal cwInterest = new BigDecimal(0);
|
||
BigDecimal cwCorpus = new BigDecimal(0);
|
||
if(i < condition.getGrace()){
|
||
cwInterest = leaseAmtRemain.multiply(issueRate).setScale(Scale.INTEREST_SCALE, BigDecimal.ROUND_HALF_UP);
|
||
} else {
|
||
cwInterest = leaseAmtRemain.multiply(issueRateFina).setScale(Scale.INTEREST_SCALE, BigDecimal.ROUND_HALF_UP);
|
||
if (i == condition.getGrace()) {
|
||
if (condition.getPeriodType() == 0) {
|
||
// 宽限期结束,期初付款第一期,不计利息
|
||
cwInterest = new BigDecimal("0");
|
||
}
|
||
}
|
||
}
|
||
if(i == 0 && condition.getGrace() > 0){
|
||
cwInterest = cwInterest.add(getFirstInterestDiff(condition));
|
||
}
|
||
|
||
cwCorpus = rpi.getRent().subtract(cwInterest);
|
||
|
||
rpi.setFinanceInterest(cwInterest);// 财务利息
|
||
rpi.setFinanceCorpus(cwCorpus);// 财务本金
|
||
rpi.setFinanceRemain(leaseAmtRemain);// 财务计息本金剩余
|
||
|
||
//****************************************************************
|
||
/**
|
||
* 临时的,有可能会永远的这样做:直接把业务的数据复制过来(保证数据准确,除均息法外)
|
||
* */
|
||
rpi.setFinanceInterest(rpi.getBusinessInterest());// 财务利息
|
||
rpi.setFinanceCorpus(rpi.getBusinessCorpus());// 财务本金
|
||
rpi.setFinanceRemain(rpi.getBusinessRemain());// 财务计息本金剩余
|
||
// 后边的568行最后一期处理也不需要了
|
||
//****************************************************************
|
||
|
||
leaseAmtRemain = leaseAmtRemain.subtract(cwCorpus);// 剩余本金重置
|
||
}
|
||
}
|
||
}
|