Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package com.inkubator.hrm.service.impl; import java.io.IOException; import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.stream.Collectors; import javax.jms.JMSException; import javax.jms.Message; import javax.jms.Session; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.time.DateUtils; import org.hamcrest.Matchers; import org.hibernate.Query; import org.hibernate.criterion.Order; import org.hibernate.transform.Transformers; import org.primefaces.json.JSONException; import org.primefaces.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.jms.core.MessageCreator; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import ch.lambdaj.Lambda; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.inkubator.common.CommonUtilConstant; import com.inkubator.common.util.DateTimeUtil; import com.inkubator.common.util.RandomNumberUtil; import com.inkubator.exception.BussinessException; import com.inkubator.hrm.HRMConstant; import com.inkubator.hrm.dao.ApprovalActivityDao; import com.inkubator.hrm.dao.EmpDataDao; import com.inkubator.hrm.dao.HrmUserDao; import com.inkubator.hrm.dao.LoanNewApplicationDao; import com.inkubator.hrm.dao.LoanNewApplicationInstallmentDao; import com.inkubator.hrm.dao.LoanNewCancelationDao; import com.inkubator.hrm.dao.LoanNewSchemaDao; import com.inkubator.hrm.dao.LoanNewSchemaListOfEmpDao; import com.inkubator.hrm.dao.LoanNewTypeDao; import com.inkubator.hrm.dao.TransactionCodeficationDao; import com.inkubator.hrm.entity.ApprovalActivity; import com.inkubator.hrm.entity.ApprovalDefinition; import com.inkubator.hrm.entity.ApprovalDefinitionLoan; import com.inkubator.hrm.entity.EmpData; import com.inkubator.hrm.entity.HrmUser; import com.inkubator.hrm.entity.LoanNewApplication; import com.inkubator.hrm.entity.LoanNewApplicationInstallment; import com.inkubator.hrm.entity.LoanNewCancelation; import com.inkubator.hrm.entity.LoanNewSchema; import com.inkubator.hrm.entity.LoanNewSchemaListOfEmp; import com.inkubator.hrm.entity.LoanNewType; import com.inkubator.hrm.entity.LoanPaymentDetail; import com.inkubator.hrm.entity.TransactionCodefication; import com.inkubator.hrm.json.util.JsonUtil; import com.inkubator.hrm.service.LoanNewApplicationService; import com.inkubator.hrm.util.HRMFinanceLib; import com.inkubator.hrm.util.JadwalPembayaran; import com.inkubator.hrm.util.KodefikasiUtil; import com.inkubator.hrm.util.LoanPayment; import com.inkubator.hrm.util.ResourceBundleUtil; import com.inkubator.hrm.web.model.LoanHistoryViewModel; import com.inkubator.hrm.web.model.LoanNewApplicationBoxViewModel; import com.inkubator.hrm.web.model.LoanNewApplicationStatusViewModel; import com.inkubator.hrm.web.model.LoanNewCancellationFormModel; import com.inkubator.hrm.web.search.LoanNewApplicationBoxSearchParameter; import com.inkubator.hrm.web.search.LoanNewSearchParameter; import com.inkubator.hrm.web.search.LoanStatusSearchParameter; import com.inkubator.securitycore.util.UserInfoUtil; import com.inkubator.webcore.util.FacesUtil; import com.inkubator.webcore.util.MessagesResourceUtil; /** * * @author Ahmad Mudzakkir Amal */ @Service(value = "loanNewApplicationService") @Lazy public class LoanNewApplicationServiceImpl extends BaseApprovalServiceImpl implements LoanNewApplicationService { @Autowired private LoanNewApplicationDao loanNewApplicationDao; @Autowired private EmpDataDao empDataDao; @Autowired private LoanNewTypeDao loanNewTypeDao; @Autowired private LoanNewSchemaDao loanNewSchemaDao; @Autowired private LoanNewSchemaListOfEmpDao loanNewSchemaListOfEmpDao; @Autowired private HrmUserDao hrmUserDao; @Autowired private ApprovalActivityDao approvalActivityDao; @Autowired private LoanNewCancelationDao LoanNewCancelationDao; @Autowired private LoanNewApplicationInstallmentDao loanNewApplicationInstallmentDao; @Autowired private TransactionCodeficationDao transactionCodeficationDao; @Override public LoanNewApplication getEntiyByPK(String string) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public LoanNewApplication getEntiyByPK(Integer intgr) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public LoanNewApplication getEntiyByPK(Long l) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void save(LoanNewApplication t) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void update(LoanNewApplication t) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void saveOrUpdate(LoanNewApplication t) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public LoanNewApplication saveData(LoanNewApplication t) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public LoanNewApplication updateData(LoanNewApplication t) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public LoanNewApplication saveOrUpdateData(LoanNewApplication t) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public LoanNewApplication getEntityByPkIsActive(String string, Integer intgr) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public LoanNewApplication getEntityByPkIsActive(String string, Byte b) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public LoanNewApplication getEntityByPkIsActive(String string, Boolean bln) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public LoanNewApplication getEntityByPkIsActive(Integer intgr, Integer intgr1) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public LoanNewApplication getEntityByPkIsActive(Integer intgr, Byte b) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public LoanNewApplication getEntityByPkIsActive(Integer intgr, Boolean bln) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public LoanNewApplication getEntityByPkIsActive(Long l, Integer intgr) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public LoanNewApplication getEntityByPkIsActive(Long l, Byte b) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public LoanNewApplication getEntityByPkIsActive(Long l, Boolean bln) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void delete(LoanNewApplication t) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public void softDelete(LoanNewApplication t) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public Long getTotalData() throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public Long getTotalDataIsActive(Boolean bln) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public Long getTotalDataIsActive(Integer intgr) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public Long getTotalDataIsActive(Byte b) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public List<LoanNewApplication> getAllData() throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public List<LoanNewApplication> getAllData(Boolean bln) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public List<LoanNewApplication> getAllData(Integer intgr) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public List<LoanNewApplication> getAllData(Byte b) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public List<LoanNewApplication> getAllDataPageAble(int i, int i1, Order order) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public List<LoanNewApplication> getAllDataPageAbleIsActive(int i, int i1, Order order, Boolean bln) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public List<LoanNewApplication> getAllDataPageAbleIsActive(int i, int i1, Order order, Integer intgr) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override public List<LoanNewApplication> getAllDataPageAbleIsActive(int i, int i1, Order order, Byte b) throws Exception { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } @Override @Transactional(readOnly = true, isolation = Isolation.REPEATABLE_READ, propagation = Propagation.SUPPORTS, timeout = 50) public List<LoanNewApplicationInstallment> getAllDataLoanNewApplicationInstallment(Double interestRate, Integer termin, Date loanPaymentDate, Double nominalPrincipal, Integer typeOfInterest) throws Exception { return calculateLoanNewApplicationInstallment(interestRate, termin, loanPaymentDate, nominalPrincipal, typeOfInterest); } private List<LoanNewApplicationInstallment> calculateLoanNewApplicationInstallment(Double interestRate, Integer termin, Date loanPaymentDate, Double nominalPrincipal, Integer typeOfInterest) { //set parameter for calculating jadwalPembayaran LoanPayment loanPayment = new LoanPayment(); loanPayment.setBungaPertahun(interestRate); loanPayment.setLamaPinjaman(termin); loanPayment.setPaymentPeriod(1); loanPayment.setTanggalBayar(loanPaymentDate); loanPayment.setTotalPinjaman(nominalPrincipal); //calculate jadwalPembayaran if (typeOfInterest.equals(HRMConstant.ANNUITY)) { loanPayment = HRMFinanceLib.getLoanPaymentAnuitas(loanPayment); } else if (typeOfInterest.equals(HRMConstant.FLAT)) { loanPayment = HRMFinanceLib.getLoanPaymentFlateMode(loanPayment); } else if (typeOfInterest.equals(HRMConstant.FLOATING)) { loanPayment = HRMFinanceLib.getLoanPaymentEffectiveMode(loanPayment); } List<JadwalPembayaran> listjadwalPembayaran = loanPayment.getJadwalPembayarans(); List<LoanNewApplicationInstallment> listLoanNewApplicationInstallments = new ArrayList<>(); //convert jadwalPembayaran to loanNewApplicationInstallment for (JadwalPembayaran jadwalPembayaran : listjadwalPembayaran) { int index = listjadwalPembayaran.indexOf(jadwalPembayaran); LoanNewApplicationInstallment loanNewApplicationInstallment = new LoanNewApplicationInstallment(); loanNewApplicationInstallment.setInstallmentDate(jadwalPembayaran.getTanggalPembayaran()); loanNewApplicationInstallment.setInterestNominal(jadwalPembayaran.getBunga()); loanNewApplicationInstallment.setBasicNominal(jadwalPembayaran.getPokok()); loanNewApplicationInstallment.setTotalPayment(jadwalPembayaran.getAngsuran()); loanNewApplicationInstallment.setRemainingBasic(jadwalPembayaran.getSisaUtang()); loanNewApplicationInstallment.setNumOfInstallment(index + 1); listLoanNewApplicationInstallments.add(loanNewApplicationInstallment); } return listLoanNewApplicationInstallments; } @Override protected void sendingApprovalNotification(ApprovalActivity appActivity) throws Exception { //send sms notification to approver if need approval OR //send sms notification to requester if need revision super.sendApprovalSmsnotif(appActivity); //initialization Gson gson = JsonUtil.getHibernateEntityGsonBuilder().create(); SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MMMM-yyyy", new Locale(appActivity.getLocale())); DecimalFormat decimalFormat = new DecimalFormat("###,###"); //get all sendCC email address on status approve OR reject List<String> ccEmailAddresses = new ArrayList<String>(); if ((appActivity.getApprovalStatus() == HRMConstant.APPROVAL_STATUS_APPROVED) || (appActivity.getApprovalStatus() == HRMConstant.APPROVAL_STATUS_REJECTED)) { ccEmailAddresses = super.getCcEmailAddressesOnApproveOrReject(appActivity); } //parsing object data to json, for email purpose LoanNewApplication loanNewApplication = gson.fromJson(appActivity.getPendingData(), LoanNewApplication.class); List<LoanPaymentDetail> loanPaymentDetails = calculateLoanPaymentDetails( loanNewApplication.getLoanNewType().getInterest().doubleValue(), loanNewApplication.getTermin(), loanNewApplication.getDibursementDate(), loanNewApplication.getNominalPrincipal(), loanNewApplication.getLoanNewType().getInterestMethod()); double nominalInstallment = 0.0; double interestInstallment = 0.0; double totalNominalInstallment = 0.0; for (LoanPaymentDetail loanPaymentDetail : loanPaymentDetails) { //angsuran pertama nominalInstallment = loanPaymentDetail.getPrincipal(); interestInstallment = loanPaymentDetail.getInterest(); totalNominalInstallment = nominalInstallment + interestInstallment; break; } final JSONObject jsonObj = new JSONObject(); try { jsonObj.put("approvalActivityId", appActivity.getId()); jsonObj.put("ccEmailAddresses", ccEmailAddresses); jsonObj.put("locale", appActivity.getLocale()); jsonObj.put("proposeDate", dateFormat.format(loanNewApplication.getCreatedOn())); jsonObj.put("loanSchemaName", loanNewApplication.getLoanNewSchema().getLoanSchemaName()); jsonObj.put("nominalPrincipal", decimalFormat.format(loanNewApplication.getNominalPrincipal())); jsonObj.put("interestRate", loanNewApplication.getLoanNewType().getInterest() + " %"); jsonObj.put("nominalInstallment", decimalFormat.format(nominalInstallment)); jsonObj.put("interestInstallment", decimalFormat.format(interestInstallment)); jsonObj.put("totalNominalInstallment", decimalFormat.format(totalNominalInstallment)); jsonObj.put("urlLinkToApprove", FacesUtil.getRequest().getContextPath() + "" + HRMConstant.LOAN_NEW_APPROVAL_PAGE + "" + "?faces-redirect=true&execution=e" + appActivity.getId()); } catch (JSONException e) { LOGGER.error("Error when create json Object ", e); } //send messaging, to trigger sending email super.jmsTemplateApproval.send(new MessageCreator() { @Override public Message createMessage(Session session) throws JMSException { return session.createTextMessage(jsonObj.toString()); } }); } private List<LoanPaymentDetail> calculateLoanPaymentDetails(Double interestRate, Integer termin, Date loanPaymentDate, Double nominalPrincipal, Integer typeOfInterest) { //set parameter for calculating jadwalPembayaran LoanPayment loanPayment = new LoanPayment(); loanPayment.setBungaPertahun(interestRate); loanPayment.setLamaPinjaman(termin); loanPayment.setPaymentPeriod(1); loanPayment.setTanggalBayar(loanPaymentDate); loanPayment.setTotalPinjaman(nominalPrincipal); //calculate jadwalPembayaran if (typeOfInterest.equals(HRMConstant.ANNUITY)) { loanPayment = HRMFinanceLib.getLoanPaymentAnuitas(loanPayment); } else if (typeOfInterest.equals(HRMConstant.FLAT)) { loanPayment = HRMFinanceLib.getLoanPaymentFlateMode(loanPayment); } else if (typeOfInterest.equals(HRMConstant.FLOATING)) { loanPayment = HRMFinanceLib.getLoanPaymentEffectiveMode(loanPayment); } //convert jadwalPembayaran to loanPaymentDetails List<LoanPaymentDetail> listLoanPaymentDetails = new ArrayList<LoanPaymentDetail>(); for (JadwalPembayaran jadwalPembayaran : loanPayment.getJadwalPembayarans()) { LoanPaymentDetail lpDetail = new LoanPaymentDetail(); lpDetail.setDueDate(jadwalPembayaran.getTanggalPembayaran()); lpDetail.setTotalPayment(jadwalPembayaran.getAngsuran()); lpDetail.setInterest(jadwalPembayaran.getBunga()); lpDetail.setPrincipal(jadwalPembayaran.getPokok()); lpDetail.setRemainingPrincipal(jadwalPembayaran.getSisaUtang()); listLoanPaymentDetails.add(lpDetail); } return listLoanPaymentDetails; } @Override @Transactional(readOnly = false, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void approved(long approvalActivityId, String pendingDataUpdate, String comment) throws Exception { Map<String, Object> result = super.approvedAndCheckNextApproval(approvalActivityId, pendingDataUpdate, comment); ApprovalActivity appActivity = (ApprovalActivity) result.get("approvalActivity"); if (StringUtils.equals((String) result.get("isEndOfApprovalProcess"), "true")) { /** * kalau status akhir sudah di approved dan tidak ada next approval, * berarti langsung insert ke database */ LoanNewApplication entity = this.convertJsonToEntity(appActivity.getPendingData()); entity.setLoanStatus(HRMConstant.LOAN_UNDISBURSED); // set default loan application status entity.setApprovalActivityNumber(appActivity.getActivityNumber()); //set approval activity number, for history approval purpose /** * saving to DB */ this.save(entity, Boolean.TRUE, null); } //if there is no error, then sending the email notification sendingApprovalNotification(appActivity); } private LoanNewApplication convertJsonToEntity(String json) { Gson gson = JsonUtil.getHibernateEntityGsonBuilder().create(); LoanNewApplication entity = gson.fromJson(json, LoanNewApplication.class); return entity; } @Override @Transactional(readOnly = false, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void rejected(long approvalActivityId, String comment) throws Exception { Map<String, Object> result = super.rejectedAndCheckNextApproval(approvalActivityId, comment); ApprovalActivity appActivity = (ApprovalActivity) result.get("approvalActivity"); if (StringUtils.equals((String) result.get("isEndOfApprovalProcess"), "true")) { /** * kalau status akhir sudah di reject dan tidak ada next approval, * berarti langsung insert ke database */ LoanNewApplication entity = this.convertJsonToEntity(appActivity.getPendingData()); entity.setLoanStatus(HRMConstant.LOAN_REJECTED); //set rejected application status entity.setApprovalActivityNumber(appActivity.getActivityNumber()); //set approval activity number, for history approval purpose /** * saving to DB */ this.save(entity, Boolean.TRUE, null); } //if there is no error, then sending the email notification sendingApprovalNotification(appActivity); } @Override @Transactional(readOnly = false, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void diverted(long approvalActivityId) throws Exception { Map<String, Object> result = super.divertedAndCheckNextApproval(approvalActivityId); ApprovalActivity appActivity = (ApprovalActivity) result.get("approvalActivity"); if (StringUtils.equals((String) result.get("isEndOfApprovalProcess"), "true")) { /** * kalau status akhir sudah di approved dan tidak ada next approval, * berarti langsung insert ke database */ Gson gson = JsonUtil.getHibernateEntityGsonBuilder().create(); String pendingData = appActivity.getPendingData(); LoanNewApplication loan = gson.fromJson(pendingData, LoanNewApplication.class); loan.setApprovalActivityNumber(appActivity.getActivityNumber()); //set approval activity number, for history approval purpose this.save(loan, Boolean.TRUE, null); } //if there is no error, then sending the email notification sendingApprovalNotification(appActivity); } @Override @Transactional(readOnly = true, isolation = Isolation.REPEATABLE_READ, propagation = Propagation.SUPPORTS, timeout = 50) public List<EmpData> getListApproverByListAppDefintion(List<ApprovalDefinition> listAppDef, Long empDataId) throws Exception { HrmUser requester = hrmUserDao.getByEmpDataId(empDataId); return super.getListApproverByListAppDef(listAppDef, requester.getUserId()); } @Override @Transactional(readOnly = true, isolation = Isolation.REPEATABLE_READ, propagation = Propagation.SUPPORTS, timeout = 30) public Long getCurrentMaxId() throws Exception { return this.loanNewApplicationDao.getCurrentMaxId(); } @Override @Transactional(readOnly = false, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public String saveWithApproval(LoanNewApplication entity) throws Exception { String result = isLoanAllowed(entity.getLoanNewType().getId(), entity.getLoanNewSchema().getId(), entity.getEmpData(), entity.getNominalPrincipal(), Boolean.FALSE, null); if (!StringUtils.equals("yes", result)) { throw new BussinessException(result); } entity.setLoanStatus(HRMConstant.LOAN_UNDISBURSED);//Default status loan undisbursed return this.save(entity, Boolean.FALSE, null); } @Override @Transactional(readOnly = false, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public String saveWithRevised(LoanNewApplication entity, Long approvalActivityId, String activityNumber) throws Exception { String message = "error"; String result = isLoanAllowed(entity.getLoanNewType().getId(), entity.getLoanNewSchema().getId(), entity.getEmpData(), entity.getNominalPrincipal(), Boolean.TRUE, activityNumber); if (!StringUtils.equals("yes", result)) { throw new BussinessException(result); } /** * proceed of revising data */ String pendingData = this.getJsonPendingData(entity); this.revised(approvalActivityId, pendingData); message = "success_need_approval"; return message; } private String save(LoanNewApplication entity, Boolean isBypassApprovalChecking, Long revisedApprActivityId) throws Exception { String result = "error"; EmpData empData = empDataDao.getEntiyByPK(entity.getEmpData().getId()); LoanNewType loanNewType = loanNewTypeDao.getEntiyByPK(entity.getLoanNewType().getId()); String createdBy = StringUtils.isEmpty(entity.getCreatedBy()) ? UserInfoUtil.getUserName() : entity.getCreatedBy(); Date createdOn = entity.getCreatedOn() == null ? new Date() : entity.getCreatedOn(); entity.setEmpData(empData); entity.setLoanNewType(loanNewType); entity.setCreatedBy(createdBy); entity.setCreatedOn(createdOn); // HrmUser requestUser = hrmUserDao.getByEmpDataId(empData.getId()); ApprovalActivity approvalActivity = this.checkApprovalIfAny(entity.getEmpData(), isBypassApprovalChecking); //ApprovalActivity approvalActivity = isBypassApprovalChecking ? null : super.checkApprovalProcess(HRMConstant.LOAN, requestUser.getUserId()); if (approvalActivity == null) { entity.setId(Integer.parseInt(RandomNumberUtil.getRandomNumber(9))); //generate number of code String nomor = this.generateLoanNumber(); entity.setNomor(nomor); loanNewApplicationDao.save(entity); result = "success_without_approval"; } else { approvalActivity.setPendingData(getJsonPendingData(entity)); approvalActivity.setTypeSpecific(entity.getLoanNewType().getId()); approvalActivityDao.save(approvalActivity); result = "success_need_approval"; //sending email notification this.sendingApprovalNotification(approvalActivity); } return result; } private String generateLoanNumber() { /** generate number form codification, from loan module */ TransactionCodefication transactionCodefication = transactionCodeficationDao .getEntityByModulCode(HRMConstant.LOAN); Long currentMaxId = loanNewApplicationDao.getCurrentMaxId(); currentMaxId = currentMaxId != null ? currentMaxId : 0; String nomor = KodefikasiUtil.getKodefikasi(((int) currentMaxId.longValue()), transactionCodefication.getCode()); return nomor; } private ApprovalActivity checkApprovalIfAny(EmpData empData, Boolean isBypassApprovalChecking) throws Exception { /** check approval process if any, * return null if no need approval process */ HrmUser requestUser = hrmUserDao.getByEmpDataId(empData.getId()); LoanNewSchema loanNewSchema = loanNewSchemaListOfEmpDao.getEntityByEmpDataId(empData.getId()) .getLoanNewSchema(); //RmbsSchema rmbsSchema = rmbsSchemaListOfEmpDao.getAllDataByEmpDataId(empData.getId()).get(0).getRmbsSchema(); List<ApprovalDefinition> appDefs = Lambda.extract(loanNewSchema.getApprovalDefinitionLoanSchemas(), Lambda.on(ApprovalDefinitionLoan.class).getApprovalDefinition()); return isBypassApprovalChecking ? null : super.checkApprovalProcess(appDefs, requestUser.getUserId()); } private String getJsonPendingData(LoanNewApplication entity) throws IOException { //parsing object to json Gson gson = JsonUtil.getHibernateEntityGsonBuilder().create(); JsonParser parser = new JsonParser(); JsonObject jsonObject = (JsonObject) parser.parse(gson.toJson(entity)); jsonObject.addProperty(HRMConstant.CONTEXT_PATH, FacesUtil.getRequest().getContextPath()); return gson.toJson(jsonObject); } /* Loan validation allow if and only if : - no previos loan with same loanType which still in approval process, or still have outstanding. - Total loan amount from current loan application and previos loans not exceed maximum loan amount of selected employee loan schema. */ private String isLoanAllowed(Long loanNewTypeId, Long loanNewSchemaId, EmpData empData, Double loanPrincipal, Boolean isRevised, String activityNumber) { HrmUser hrmUser = hrmUserDao.getByEmpDataId(empData.getId()); LoanNewSchema loanNewSchema = loanNewSchemaDao.getEntiyByPK(loanNewSchemaId); //Dapatkan semua List pending Approval Activity dari Karyawan yang sedang mengajukan pinjaman List<ApprovalActivity> listPendingRequest = approvalActivityDao.getPendingRequest(hrmUser.getUserId()); //filter hanya yang datang dari LOAN listPendingRequest = Lambda.select(listPendingRequest, Lambda.having(Lambda.on(ApprovalActivity.class).getApprovalDefinition().getName(), Matchers.equalTo(HRMConstant.LOAN))); // Cek Type Specificnya, jika sama dengan tipe yang sedang diajukan, return key exception for (ApprovalActivity pendingApprovalActivity : listPendingRequest) { if (pendingApprovalActivity.getTypeSpecific().equals(loanNewTypeId)) { return "loan.error_loan_with_same_type_found"; } } // Dapatkan List Loan yang status <> HRMConstant.LOAN_PAID List<LoanNewApplication> listPreviosUnpaidLoanWithSameLoanTypeId = loanNewApplicationDao .getListUnpaidLoanByEmpDataIdAndLoanNewTypeId(empData.getId(), loanNewTypeId); // filter jadi dua list : listUndisbursedLoan (List Loan yang belum di cairkan), dan listdisbursedLoan (List Loan yang sudah di cairkan) List<LoanNewApplication> listUndisbursedLoan = Lambda.select(listPreviosUnpaidLoanWithSameLoanTypeId, Lambda.having(Lambda.on(LoanNewApplication.class).getLoanStatus(), Matchers.equalTo(HRMConstant.LOAN_UNDISBURSED))); List<LoanNewApplication> listdisbursedLoan = Lambda.select(listPreviosUnpaidLoanWithSameLoanTypeId, Lambda.having(Lambda.on(LoanNewApplication.class).getLoanStatus(), Matchers.equalTo(HRMConstant.LOAN_DISBURSED))); // Jika di list loan yang belum di cairkan, sudah ada loan yang tipe nya sama dengan yang sedang di ajukan, return key exception if (!listUndisbursedLoan.isEmpty()) { return "loan.error_loan_with_same_type_found"; } // jika tidak ada di list yang belum dicairkan, lanjut cek ke list yang sudah di cairkan, cek satu persatu, // dapatkan lastInstallmentDate (Tanggal terakhir cicilan) dari loan tsb, bandingkan dengan tgl hari ini, jika tgl hari ini sebelum tgl cicilan terakhir, berarti belum lunas // return key exception for (LoanNewApplication loanNewApplication : listdisbursedLoan) { List<LoanNewApplicationInstallment> listInstallments = loanNewApplicationInstallmentDao .getListByLoanNewApplicationId(loanNewApplication.getId()); if (!listInstallments.isEmpty()) { Date lastInstallment = Lambda.max(listInstallments, Lambda.on(LoanNewApplicationInstallment.class).getInstallmentDate()); if (new Date().before(lastInstallment)) { return "loan.error_loan_with_same_type_found"; } } } /* Mulai Hitung Total loan dari pengajuan yang sedang berlangsung dan pengajuan sebelumnya*/ Double totalLoanByUser = loanPrincipal; for (ApprovalActivity pendingApprovalActivity : listPendingRequest) { //calculate only from different activity number if (!StringUtils.equals(activityNumber, pendingApprovalActivity.getActivityNumber())) { String pendingData = pendingApprovalActivity.getPendingData(); Gson gson = JsonUtil.getHibernateEntityGsonBuilder().create(); LoanNewApplication loanNewApplication = gson.fromJson(pendingData, LoanNewApplication.class); totalLoanByUser += loanNewApplication.getNominalPrincipal(); } } if (!listUndisbursedLoan.isEmpty()) { for (LoanNewApplication prevLoan : listUndisbursedLoan) { totalLoanByUser += prevLoan.getNominalPrincipal(); } } if (!listdisbursedLoan.isEmpty()) { for (LoanNewApplication prevLoan : listdisbursedLoan) { totalLoanByUser += prevLoan.getNominalPrincipal(); } } /* Akhir perhitungan */ //if total Nominal melebihi maksimum pinjaman dari skema pinjaman karyawan terpilih, return Exception Message if (totalLoanByUser > loanNewSchema.getTotalMaximumLoan()) { return "loan.error_total_loan_exceed_max_loan_on_loan_scheme"; } // jika tidak ada pelanggaran, return yes return "yes"; } @Override @Transactional(readOnly = true, isolation = Isolation.REPEATABLE_READ, propagation = Propagation.SUPPORTS, timeout = 50) public List<LoanNewApplication> getListLoanDisbursedOrPaidByEmpDataIdAndLoanNewSchemaId(Long empDataId, Long loanNewSchemaId) throws Exception { return this.loanNewApplicationDao.getListLoanDisbursedOrPaidByEmpDataIdAndLoanNewSchemaId(empDataId, loanNewSchemaId); } @Override @Transactional(readOnly = false, isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED, rollbackFor = Exception.class) public void cancelLoanApplicationAndSaveToLoanNewCancellation( LoanNewCancellationFormModel loanNewCancellationFormModel) throws Exception { Long actvityId = loanNewCancellationFormModel.getLoanPendingActivity(); ApprovalActivity approvalActivity = approvalActivityDao.getEntityByPkWithDetail(actvityId); //check if there is already approved by someone(approver), then it shouldn't be cancelled if (approvalActivityDao.isAlreadyHaveApprovedStatus(approvalActivity.getActivityNumber())) { throw new BussinessException("approval.error_cancelled_already_approved"); } String activityNumber = approvalActivity.getActivityNumber(); /** saving entity LoanApplication to DB */ LoanNewApplication application = this.convertJsonToEntity(approvalActivity.getPendingData()); application.setLoanStatus(HRMConstant.LOAN_CANCELED); // set cancelled application status application.setApprovalActivityNumber(activityNumber); //set approval activity number, for history approval purpose this.save(application, Boolean.TRUE, null); //Save LoanNewCancellation log LoanNewCancelation loanNewCancelation = new LoanNewCancelation(); loanNewCancelation.setId(Long.parseLong(RandomNumberUtil.getRandomNumber(9))); loanNewCancelation.setApprovalActivityNumber(activityNumber); loanNewCancelation.setLoanNewApplication(application); loanNewCancelation.setEmpData(empDataDao.getEntiyByPK(loanNewCancellationFormModel.getEmpData().getId())); loanNewCancelation.setLoanNewSchema( loanNewSchemaDao.getEntiyByPK(loanNewCancellationFormModel.getLoanNewSchema().getId())); loanNewCancelation .setLoanNewType(loanNewTypeDao.getEntiyByPK(loanNewCancellationFormModel.getLoanNewType().getId())); loanNewCancelation.setLoanCancellationNumber(this.generateLoanCancelationNumber()); loanNewCancelation.setLoanNumber(loanNewCancellationFormModel.getLoanNumber()); loanNewCancelation.setReason(loanNewCancellationFormModel.getReasonCancellation()); loanNewCancelation.setCancelationDate(loanNewCancellationFormModel.getLoanCancellationDate()); loanNewCancelation.setCreatedOn(new Date()); loanNewCancelation.setCreatedBy(UserInfoUtil.getUserName()); LoanNewCancelationDao.save(loanNewCancelation); /** cancel this approval activity and saving log approver history */ LoanNewSchemaListOfEmp loanNewSchemaListOfEmp = loanNewSchemaListOfEmpDao .getEntityWithDetailByEmpDataId(application.getEmpData().getId()); LoanNewSchema loanNewSchema = loanNewSchemaDao .getEntityByPkFetchApprovalDefinition(loanNewSchemaListOfEmp.getLoanNewSchema().getId()); List<ApprovalDefinition> appDefs = Lambda.extract(loanNewSchema.getApprovalDefinitionLoanSchemas(), Lambda.on(ApprovalDefinitionLoan.class).getApprovalDefinition()); super.cancelled(actvityId, loanNewCancellationFormModel.getReasonCancellation(), appDefs); } private String generateLoanCancelationNumber() { /** generate number form codification, from loan module */ TransactionCodefication transactionCodefication = transactionCodeficationDao .getEntityByModulCode(HRMConstant.LOAN_CANCELLATION_KODE); Long currentMaxId = LoanNewCancelationDao.getCurrentMaxId(); currentMaxId = currentMaxId != null ? currentMaxId : 0; String nomor = KodefikasiUtil.getKodefikasi(((int) currentMaxId.longValue()), transactionCodefication.getCode()); return nomor; } @Override @Transactional(readOnly = true, isolation = Isolation.REPEATABLE_READ, propagation = Propagation.SUPPORTS, timeout = 50) public List<LoanNewApplication> getByParamByStatusUndisbursed(LoanNewSearchParameter parameter, int firstResult, int maxResults, Order orderable) throws Exception { return this.loanNewApplicationDao.getByParamByStatusUndisbursed(parameter, firstResult, maxResults, orderable); } @Override @Transactional(readOnly = true, isolation = Isolation.REPEATABLE_READ, propagation = Propagation.SUPPORTS, timeout = 30) public Long getTotalByParamByStatusUndisbursed(LoanNewSearchParameter parameter) throws Exception { return this.loanNewApplicationDao.getTotalByParamByStatusUndisbursed(parameter); } @Override @Transactional(readOnly = true, isolation = Isolation.REPEATABLE_READ, propagation = Propagation.SUPPORTS, timeout = 50) public List<LoanNewApplicationBoxViewModel> getUndisbursedActivityByParam( LoanNewApplicationBoxSearchParameter parameter, int firstResult, int maxResults, Order orderable) throws Exception { List<LoanNewApplicationBoxViewModel> listUndisbursedModel = this.loanNewApplicationDao .getUndisbursedActivityByParam(parameter, firstResult, maxResults, orderable); setUndisbursedLoanComplexData(listUndisbursedModel); //Khusus filter status pencairan tidak bisa di level dao dikarenakan field tersebut hanya ada di table loan_new_application, //jika activity nya masih pending, content data pinjaman masih berupa json text, //oleh karena itu di filter di level service menggunakan lambda java 8 //setelah terlebih dahulu di set status nya di method setUndisbursedLoanComplexData if (StringUtils.isNotBlank(parameter.getDisbursementStatus())) { listUndisbursedModel = listUndisbursedModel.stream().filter(model -> StringUtils .equals(String.valueOf(model.getDisbursementStatus()), parameter.getDisbursementStatus())) .collect(Collectors.toList()); } return listUndisbursedModel; } private void setUndisbursedLoanComplexData(List<LoanNewApplicationBoxViewModel> listLoanUdisbursedModel) { for (LoanNewApplicationBoxViewModel loanModel : listLoanUdisbursedModel) { ApprovalActivity approvalActivity = approvalActivityDao .getEntiyByPK(loanModel.getApprovalActivityId().longValue()); loanModel.setApprovalDate(approvalActivity.getApprovalTime()); if (loanModel.getLoanNewApplicationId() == null) { Gson gson = JsonUtil.getHibernateEntityGsonBuilder().create(); LoanNewApplication loanNewApplicationTemp = gson.fromJson(loanModel.getJsonData(), LoanNewApplication.class); loanModel.setLoanNumber(loanNewApplicationTemp.getNomor()); loanModel.setNominalPrincipal(new BigDecimal(loanNewApplicationTemp.getNominalPrincipal())); loanModel.setApplicationDate(loanNewApplicationTemp.getApplicationDate()); loanModel.setDisbursementStatus(HRMConstant.LOAN_UNDISBURSED); } else { LoanNewApplication loanNewApplication = loanNewApplicationDao .getEntiyByPK(loanModel.getLoanNewApplicationId()); loanModel.setLoanNumber(loanNewApplication.getNomor()); loanModel.setNominalPrincipal(new BigDecimal(loanNewApplication.getNominalPrincipal())); loanModel.setApplicationDate(loanNewApplication.getApplicationDate()); loanModel.setDisbursementStatus(loanNewApplication.getLoanStatus()); } } } @Override @Transactional(readOnly = true, isolation = Isolation.REPEATABLE_READ, propagation = Propagation.SUPPORTS, timeout = 30) public Long getTotalUndisbursedActivityByParam(LoanNewApplicationBoxSearchParameter parameter) throws Exception { return this.loanNewApplicationDao.getTotalUndisbursedActivityByParam(parameter); } @Override protected String getDetailSmsContentOfActivity(ApprovalActivity appActivity) { DecimalFormat decimalFormat = new DecimalFormat("###,###"); StringBuffer detail = new StringBuffer(); HrmUser requester = hrmUserDao.getByUserId(appActivity.getRequestBy()); LoanNewApplication entity = this.convertJsonToEntity(appActivity.getPendingData()); detail.append("Pengajuan pinjaman oleh " + requester.getEmpData().getBioData().getFullName() + ". "); detail.append("Jenis: " + entity.getLoanNewType().getLoanTypeName() + ". "); detail.append("Sejumlah Rp. " + decimalFormat.format(entity.getNominalPrincipal()) + ", dengan termin " + entity.getTermin() + " kali"); return detail.toString(); } @Override @Transactional(readOnly = true, isolation = Isolation.REPEATABLE_READ, propagation = Propagation.SUPPORTS, timeout = 50) public List<LoanHistoryViewModel> getListLoanHistoryByEmpDataId(Long empDataId) throws Exception { List<LoanHistoryViewModel> listLoanHistory = loanNewApplicationDao.getListLoanHistoryByEmpDataId(empDataId); for (LoanHistoryViewModel loanHistModel : listLoanHistory) { List<LoanNewApplicationInstallment> listInstallment = calculateLoanNewApplicationInstallment( loanHistModel.getLoanInterestRate().doubleValue(), loanHistModel.getTotalNumberOfInstallment(), DateTimeUtil.getDateFrom(loanHistModel.getLoanPaymentDate(), loanHistModel.getBuffer(), CommonUtilConstant.DATE_FORMAT_MONTH), loanHistModel.getLoanNominal().doubleValue(), loanHistModel.getTypeOfInterest()); LoanNewApplicationInstallment installmentMax = Lambda.selectMax(listInstallment, Lambda.on(LoanNewApplicationInstallment.class).getTotalPayment()); LoanNewApplicationInstallment lastInstallment = Lambda.selectMax(listInstallment, Lambda.on(LoanNewApplicationInstallment.class).getInstallmentDate()); loanHistModel.setInstallmentNominal(installmentMax.getTotalPayment()); loanHistModel.setLastPaymentDate(lastInstallment.getInstallmentDate()); Long totalAlreadyPaidInstallment = loanNewApplicationInstallmentDao .getTotalInstallmentByLoanNewApplicationId(loanHistModel.getLoanNewApplicationId()); loanHistModel.setTotalAlreadyPaidInstallment( totalAlreadyPaidInstallment == null ? 0 : totalAlreadyPaidInstallment.intValue()); } return listLoanHistory; } @Override @Transactional(readOnly = true, isolation = Isolation.REPEATABLE_READ, propagation = Propagation.SUPPORTS, timeout = 50) public List<LoanNewApplicationStatusViewModel> getAllDataLoanNewApplicationStatus( LoanStatusSearchParameter parameter, int firstResult, int maxResults, Order orderable) throws Exception { List<LoanNewApplicationStatusViewModel> listAllData = loanNewApplicationDao .getAllDataLoanNewApplicationStatus(parameter, firstResult, maxResults, orderable); for (LoanNewApplicationStatusViewModel loanNewApplicationStatusViewModel : listAllData) { loanNewApplicationStatusViewModel.setIsRendered(Boolean.FALSE); //kondisi kalo ditunda if (loanNewApplicationStatusViewModel .getApprovalStatus() == HRMConstant.APPROVAL_STATUS_WAITING_APPROVAL || loanNewApplicationStatusViewModel .getApprovalStatus() == HRMConstant.APPROVAL_STATUS_WAITING_REVISED) { if (loanNewApplicationStatusViewModel.getLoanSchemaName() == null) { Gson gson = JsonUtil.getHibernateEntityGsonBuilder().create(); LoanNewApplication loanNewApplicationTemp = gson .fromJson(loanNewApplicationStatusViewModel.getJsonData(), LoanNewApplication.class); loanNewApplicationStatusViewModel .setLoanSchemaName(loanNewApplicationTemp.getLoanNewSchema().getLoanSchemaName()); loanNewApplicationStatusViewModel .setNominalPrincipal(loanNewApplicationTemp.getNominalPrincipal()); loanNewApplicationStatusViewModel.setTotalTermin(loanNewApplicationTemp.getTermin()); loanNewApplicationStatusViewModel .setApprovalMessage(ResourceBundleUtil.getAsString("loanStatusMessage.pending")); } } else if (loanNewApplicationStatusViewModel .getApprovalStatus() == HRMConstant.APPROVAL_STATUS_APPROVED) { if (loanNewApplicationStatusViewModel.getLoanStatus() == HRMConstant.LOAN_DISBURSED && !loanNewApplicationStatusViewModel.getTermin() .equals(loanNewApplicationStatusViewModel.getTotalTermin())) { loanNewApplicationStatusViewModel .setApprovalMessage(ResourceBundleUtil.getAsString("loanStatusMessage.withdraw")); loanNewApplicationStatusViewModel.setIsRendered(Boolean.TRUE); } else if (loanNewApplicationStatusViewModel.getLoanStatus() == HRMConstant.LOAN_DISBURSED && loanNewApplicationStatusViewModel.getTermin() .equals(loanNewApplicationStatusViewModel.getTotalTermin())) { loanNewApplicationStatusViewModel .setApprovalMessage(ResourceBundleUtil.getAsString("loanStatusMessage.paid")); } else if (loanNewApplicationStatusViewModel.getLoanStatus() == HRMConstant.LOAN_UNDISBURSED) { loanNewApplicationStatusViewModel .setApprovalMessage(ResourceBundleUtil.getAsString("loanStatusMessage.withdraw")); loanNewApplicationStatusViewModel.setIsRendered(Boolean.TRUE); } } else if (loanNewApplicationStatusViewModel .getApprovalStatus() == HRMConstant.APPROVAL_STATUS_REJECTED) { loanNewApplicationStatusViewModel .setApprovalMessage(ResourceBundleUtil.getAsString("loanStatusMessage.reject")); } else if (loanNewApplicationStatusViewModel .getApprovalStatus() == HRMConstant.APPROVAL_STATUS_CANCELLED) { loanNewApplicationStatusViewModel .setApprovalMessage(ResourceBundleUtil.getAsString("approvalactivity.cancelled")); } } return listAllData; } @Override @Transactional(readOnly = true, isolation = Isolation.REPEATABLE_READ, propagation = Propagation.SUPPORTS, timeout = 30) public Long getTotalDataLoanNewApplicationStatus(LoanStatusSearchParameter parameter) throws Exception { return loanNewApplicationDao.getTotalDataLoanNewApplicationStatus(parameter); } @Override @Transactional(readOnly = true, isolation = Isolation.READ_COMMITTED, propagation = Propagation.SUPPORTS, timeout = 30) public LoanNewApplication getEntityWithDetailByActivityNumber(String activityNumber) throws Exception { return loanNewApplicationDao.getEntityWithDetailByActivityNumber(activityNumber); } }