apzl_leasing/calc/com/tenwa/reckon/util/RentCalculateUtil.java
2018-07-25 16:29:22 +08:00

396 lines
14 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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);// 剩余本金重置
}
}
}