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 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 result = new ArrayList(); // // 计算期数 // 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 getEmptyPlanModel(CalculationCondition condition) throws Exception { Date startDate = condition.getStartDate();// 起租日期,计算最后一期的还款日期 Date firstPlanDate = condition.getFirstPlanDate();// 计划第1期日期 Date secondPlanDate = condition.getSecondPlanDate();// 计划第2期日期 List planDateList = new ArrayList(); 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 result = new ArrayList(); 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 corpusList,String type,List interestDateList,FundRentPlanBean frpb) throws Exception { List rentList=new ArrayList(); List businessCorpusList=new ArrayList(); List interestList=new ArrayList(); 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 rentPlan) throws ParseException { List inflowPour = new ArrayList(); 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 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);// 剩余本金重置 } } }