001 /** 002 * Copyright 2004-2013 The Kuali Foundation 003 * 004 * Licensed under the Educational Community License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/ecl2.php 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.kuali.hr.lm.balancetransfer.service; 017 018 import java.math.BigDecimal; 019 import java.sql.Date; 020 import java.util.ArrayList; 021 import java.util.HashMap; 022 import java.util.List; 023 import java.util.Map; 024 025 import org.apache.commons.collections.CollectionUtils; 026 import org.apache.commons.lang.StringUtils; 027 028 import org.kuali.hr.lm.LMConstants; 029 import org.kuali.hr.lm.accrual.AccrualCategory; 030 import org.kuali.hr.lm.accrual.AccrualCategoryRule; 031 import org.kuali.hr.lm.balancetransfer.BalanceTransfer; 032 import org.kuali.hr.lm.balancetransfer.dao.BalanceTransferDao; 033 import org.kuali.hr.lm.employeeoverride.EmployeeOverride; 034 import org.kuali.hr.lm.leaveblock.LeaveBlock; 035 import org.kuali.hr.lm.leaveblock.LeaveBlockHistory; 036 037 import org.kuali.hr.time.service.base.TkServiceLocator; 038 import org.kuali.hr.time.util.TKUtils; 039 040 import org.kuali.rice.kew.api.exception.WorkflowException; 041 import org.kuali.rice.kim.api.identity.principal.EntityNamePrincipalName; 042 import org.kuali.rice.kim.api.services.KimApiServiceLocator; 043 import org.kuali.rice.krad.maintenance.MaintenanceDocument; 044 import org.kuali.rice.krad.service.KRADServiceLocator; 045 import org.kuali.rice.krad.service.KRADServiceLocatorWeb; 046 import org.kuali.rice.krad.util.KRADConstants; 047 import org.kuali.rice.krad.util.ObjectUtils; 048 049 public class BalanceTransferServiceImpl implements BalanceTransferService { 050 051 private BalanceTransferDao balanceTransferDao; 052 053 @Override 054 public List<BalanceTransfer> getAllBalanceTransfersForPrincipalId( 055 String principalId) { 056 return balanceTransferDao.getAllBalanceTransfersForPrincipalId(principalId); 057 } 058 059 @Override 060 public List<BalanceTransfer> getAllBalanceTransferForPrincipalIdAsOfDate( 061 String principalId, Date effectiveDate) { 062 return balanceTransferDao.getAllBalanceTransferForPrincipalIdAsOfDate(principalId,effectiveDate); 063 } 064 065 @Override 066 public List<BalanceTransfer> getAllBalanceTransferByEffectiveDate( 067 Date effectiveDate) { 068 return balanceTransferDao.getAllBalanceTransferByEffectiveDate(effectiveDate); 069 } 070 071 @Override 072 public BalanceTransfer getBalanceTransferById(String balanceTransferId) { 073 return balanceTransferDao.getBalanceTransferById(balanceTransferId); 074 } 075 076 @Override 077 public BalanceTransfer initializeTransfer(String principalId, String accrualCategoryRule, BigDecimal accruedBalance, Date effectiveDate) { 078 //Initially, principals may be allowed to edit the transfer amount when prompted to submit this balance transfer, however, 079 //a base transfer amount together with a forfeited amount is calculated to bring the balance back to its limit in accordance 080 //with transfer limits. This "default" transfer object is used to adjust forfeiture when the user changes the transfer amount. 081 BalanceTransfer bt = null; 082 AccrualCategoryRule accrualRule = TkServiceLocator.getAccrualCategoryRuleService().getAccrualCategoryRule(accrualCategoryRule); 083 084 if(ObjectUtils.isNotNull(accrualRule) && ObjectUtils.isNotNull(accruedBalance)) { 085 bt = new BalanceTransfer(); 086 //These two objects are essential to balance transfers triggered when the employee submits their leave calendar for approval. 087 //Neither of these objects should be null, otherwise this method could not have been called. 088 AccrualCategory fromAccrualCategory = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualRule.getLmAccrualCategoryId()); 089 AccrualCategory toAccrualCategory = TkServiceLocator.getAccrualCategoryService().getAccrualCategory(accrualRule.getMaxBalanceTransferToAccrualCategory(),effectiveDate); 090 BigDecimal fullTimeEngagement = TkServiceLocator.getJobService().getFteSumForAllActiveLeaveEligibleJobs(principalId, effectiveDate); 091 092 BigDecimal transferConversionFactor = null; 093 if(ObjectUtils.isNotNull(accrualRule.getMaxBalanceTransferConversionFactor())) 094 transferConversionFactor = accrualRule.getMaxBalanceTransferConversionFactor(); 095 096 // AccrualRule.maxBalance == null -> no balance limit. No balance limit -> no accrual triggered transfer / payout / lose. 097 // execution point should not be here if max balance on accrualRule is null, unless there exists an employee override. 098 BigDecimal maxBalance = accrualRule.getMaxBalance(); 099 BigDecimal adjustedMaxBalance = maxBalance.multiply(fullTimeEngagement).setScale(2); 100 101 BigDecimal maxTransferAmount = null; 102 BigDecimal adjustedMaxTransferAmount = null; 103 if(ObjectUtils.isNotNull(accrualRule.getMaxTransferAmount())) { 104 maxTransferAmount = new BigDecimal(accrualRule.getMaxTransferAmount()); 105 adjustedMaxTransferAmount = maxTransferAmount.multiply(fullTimeEngagement).setScale(2); 106 } 107 else { 108 // no limit on transfer amount 109 maxTransferAmount = new BigDecimal(Long.MAX_VALUE); 110 adjustedMaxTransferAmount = maxTransferAmount; 111 } 112 113 BigDecimal maxCarryOver = null; 114 BigDecimal adjustedMaxCarryOver = null; 115 if(ObjectUtils.isNotNull(accrualRule.getMaxCarryOver())) { 116 maxCarryOver = new BigDecimal(accrualRule.getMaxCarryOver()); 117 adjustedMaxCarryOver = maxCarryOver.multiply(fullTimeEngagement).setScale(2); 118 } 119 else { 120 //no limit to carry over. 121 maxCarryOver = new BigDecimal(Long.MAX_VALUE); 122 adjustedMaxCarryOver = maxCarryOver; 123 } 124 125 List<EmployeeOverride> overrides = TkServiceLocator.getEmployeeOverrideService().getEmployeeOverrides(principalId, effectiveDate); 126 for(EmployeeOverride override : overrides) { 127 if(StringUtils.equals(override.getAccrualCategory(),fromAccrualCategory.getAccrualCategory())) { 128 //Do not pro-rate override values for FTE. 129 if(StringUtils.equals(override.getOverrideType(),"MB")) 130 adjustedMaxBalance = new BigDecimal(override.getOverrideValue()); 131 if(StringUtils.equals(override.getOverrideType(),"MTA")) 132 adjustedMaxTransferAmount = new BigDecimal(override.getOverrideValue()); 133 if(StringUtils.equals(override.getOverrideType(),"MAC")) 134 adjustedMaxCarryOver = new BigDecimal(override.getOverrideValue()); 135 } 136 } 137 138 BigDecimal transferAmount = accruedBalance.subtract(adjustedMaxBalance); 139 if(StringUtils.equals(accrualRule.getActionAtMaxBalance(),LMConstants.ACTION_AT_MAX_BAL.LOSE)) { 140 //Move all time in excess of employee's fte adjusted max balance to forfeiture. 141 bt.setForfeitedAmount(transferAmount); 142 //There is no transfer to another accrual category. 143 bt.setTransferAmount(BigDecimal.ZERO); 144 bt.setAmountTransferred(BigDecimal.ZERO); 145 // to accrual category is a required field on maintenance document. Set as from accrual category 146 // to remove validation errors when routing, approving, etc. 147 bt.setToAccrualCategory(fromAccrualCategory.getAccrualCategory()); 148 } 149 else { 150 // ACTION_AT_MAX_BAL = TRANSFER 151 bt.setToAccrualCategory(toAccrualCategory.getAccrualCategory()); 152 if(transferAmount.compareTo(adjustedMaxTransferAmount) > 0) { 153 //there's forfeiture. 154 //bring transfer amount down to the adjusted maximum transfer amount, and place excess in forfeiture. 155 //accruedBalance - adjustedMaxTransferAmount - adjustedMaxBalance = forfeiture. 156 //transferAmount = accruedBalance - adjustedMaxBalance; forfeiture = transferAmount - adjustedMaxTransferAmount. 157 BigDecimal forfeiture = transferAmount.subtract(adjustedMaxTransferAmount).setScale(2); 158 forfeiture = forfeiture.stripTrailingZeros(); 159 bt.setForfeitedAmount(forfeiture); 160 bt.setTransferAmount(adjustedMaxTransferAmount); 161 } 162 else { 163 bt.setTransferAmount(transferAmount); 164 bt.setForfeitedAmount(BigDecimal.ZERO); 165 } 166 } 167 168 assert(adjustedMaxBalance.compareTo(accruedBalance.subtract(bt.getTransferAmount().add(bt.getForfeitedAmount()))) == 0); 169 170 // Max Carry Over logic for Year End transfers. 171 if(StringUtils.equals(accrualRule.getMaxBalanceActionFrequency(),LMConstants.MAX_BAL_ACTION_FREQ.YEAR_END)) { 172 173 //At this point, transfer amount and forfeiture have been set so that the new accrued balance will be the 174 //adjusted max balance, so this amount is used to check against carry over. 175 if(adjustedMaxBalance.compareTo(adjustedMaxCarryOver) > 0) { 176 BigDecimal carryOverDiff = adjustedMaxBalance.subtract(adjustedMaxCarryOver); 177 178 if(StringUtils.equals(accrualRule.getActionAtMaxBalance(),LMConstants.ACTION_AT_MAX_BAL.LOSE)){ 179 //add carry over excess to forfeiture. 180 bt.setForfeitedAmount(bt.getForfeitedAmount().add(carryOverDiff)); 181 } 182 else { 183 //maximize the transfer amount. 184 BigDecimal potentialTransferAmount = bt.getTransferAmount().add(carryOverDiff); 185 186 //Can this amount be added to the transfer amount without exceeding adjusted max transfer amount?? 187 if(potentialTransferAmount.compareTo(adjustedMaxTransferAmount) <= 0) { 188 //yes 189 bt.setTransferAmount(bt.getTransferAmount().add(carryOverDiff)); 190 } 191 else { 192 //no 193 BigDecimal carryOverExcess = potentialTransferAmount.subtract(adjustedMaxTransferAmount); 194 //move excess to forfeiture 195 bt.setForfeitedAmount(bt.getForfeitedAmount().add(carryOverExcess)); 196 //the remainder (if any) can be added to the transfer amount. 197 bt.setTransferAmount(bt.getTransferAmount().add(carryOverDiff.subtract(carryOverExcess))); 198 199 assert(bt.getTransferAmount().compareTo(adjustedMaxTransferAmount)==0); 200 // assert that the new balance will be equal to the adjusted max carry over < adjusted max balance. 201 } 202 } 203 assert(adjustedMaxCarryOver.compareTo(accruedBalance.subtract(bt.getTransferAmount().add(bt.getForfeitedAmount()))) == 0); 204 } 205 //otherwise, given balance will be at or under the max annual carry over. 206 } 207 208 bt.setEffectiveDate(effectiveDate); 209 bt.setAccrualCategoryRule(accrualCategoryRule); 210 bt.setFromAccrualCategory(fromAccrualCategory.getAccrualCategory()); 211 bt.setPrincipalId(principalId); 212 if(ObjectUtils.isNotNull(transferConversionFactor)) 213 bt.setAmountTransferred(bt.getTransferAmount().multiply(transferConversionFactor).setScale(2)); 214 else 215 bt.setAmountTransferred(bt.getTransferAmount()); 216 } 217 return bt; 218 } 219 220 @Override 221 public BalanceTransfer transfer(BalanceTransfer balanceTransfer) { 222 if(ObjectUtils.isNull(balanceTransfer)) 223 throw new RuntimeException("did not supply a valid BalanceTransfer object."); 224 else { 225 List<LeaveBlock> leaveBlocks = new ArrayList<LeaveBlock>(); 226 BigDecimal transferAmount = balanceTransfer.getTransferAmount(); 227 LeaveBlock aLeaveBlock = null; 228 229 if(ObjectUtils.isNotNull(balanceTransfer.getAmountTransferred())) { 230 if(balanceTransfer.getAmountTransferred().compareTo(BigDecimal.ZERO) > 0 ) { 231 232 aLeaveBlock = new LeaveBlock(); 233 //Create a leave block that adds the adjusted transfer amount to the "transfer to" accrual category. 234 aLeaveBlock.setPrincipalId(balanceTransfer.getPrincipalId()); 235 aLeaveBlock.setLeaveDate(balanceTransfer.getEffectiveDate()); 236 aLeaveBlock.setEarnCode(balanceTransfer.getCreditedAccrualCategory().getEarnCode()); 237 aLeaveBlock.setAccrualCategory(balanceTransfer.getToAccrualCategory()); 238 aLeaveBlock.setDescription("Amount transferred"); 239 aLeaveBlock.setLeaveAmount(balanceTransfer.getAmountTransferred()); 240 aLeaveBlock.setAccrualGenerated(true); 241 aLeaveBlock.setTransactionDocId(balanceTransfer.getDocumentHeaderId()); 242 aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER); 243 aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED); 244 aLeaveBlock.setBlockId(0L); 245 246 //Want to store the newly created leave block id on this maintainable object 247 //when the status of the maintenance document encapsulating this maintainable changes 248 //the id will be used to fetch and update the leave block statuses. 249 aLeaveBlock = KRADServiceLocator.getBusinessObjectService().save(aLeaveBlock); 250 251 balanceTransfer.setAccruedLeaveBlockId(aLeaveBlock.getLmLeaveBlockId()); 252 // save history 253 LeaveBlockHistory lbh = new LeaveBlockHistory(aLeaveBlock); 254 lbh.setAction(LMConstants.ACTION.ADD); 255 TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh); 256 leaveBlocks.add(aLeaveBlock); 257 } 258 } 259 260 if(ObjectUtils.isNotNull(transferAmount)) { 261 if(transferAmount.compareTo(BigDecimal.ZERO) > 0) { 262 //Create leave block that removes the correct transfer amount from the originating accrual category. 263 aLeaveBlock = new LeaveBlock(); 264 aLeaveBlock.setPrincipalId(balanceTransfer.getPrincipalId()); 265 aLeaveBlock.setLeaveDate(balanceTransfer.getEffectiveDate()); 266 aLeaveBlock.setEarnCode(balanceTransfer.getDebitedAccrualCategory().getEarnCode()); 267 aLeaveBlock.setAccrualCategory(balanceTransfer.getFromAccrualCategory()); 268 aLeaveBlock.setDescription("Transferred amount"); 269 aLeaveBlock.setLeaveAmount(balanceTransfer.getTransferAmount().negate()); 270 aLeaveBlock.setAccrualGenerated(true); 271 aLeaveBlock.setTransactionDocId(balanceTransfer.getDocumentHeaderId()); 272 aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER); 273 aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED); 274 aLeaveBlock.setBlockId(0L); 275 276 //Want to store the newly created leave block id on this maintainable object. 277 //when the status of the maintenance document encapsulating this maintainable changes 278 //the id will be used to fetch and update the leave block statuses. 279 aLeaveBlock = KRADServiceLocator.getBusinessObjectService().save(aLeaveBlock); 280 281 balanceTransfer.setDebitedLeaveBlockId(aLeaveBlock.getLmLeaveBlockId()); 282 // save history 283 LeaveBlockHistory lbh = new LeaveBlockHistory(aLeaveBlock); 284 lbh.setAction(LMConstants.ACTION.ADD); 285 TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh); 286 287 leaveBlocks.add(aLeaveBlock); 288 } 289 } 290 291 BigDecimal forfeitedAmount = balanceTransfer.getForfeitedAmount(); 292 if(ObjectUtils.isNotNull(forfeitedAmount)) { 293 //Any amount forfeited must come out of the originating accrual category in order to bring balance back to max. 294 if(forfeitedAmount.compareTo(BigDecimal.ZERO) > 0) { 295 //for balance transfers with action = lose, transfer amount must be moved to forfeitedAmount 296 aLeaveBlock = new LeaveBlock(); 297 aLeaveBlock.setPrincipalId(balanceTransfer.getPrincipalId()); 298 aLeaveBlock.setLeaveDate(balanceTransfer.getEffectiveDate()); 299 aLeaveBlock.setEarnCode(balanceTransfer.getDebitedAccrualCategory().getEarnCode()); 300 aLeaveBlock.setAccrualCategory(balanceTransfer.getFromAccrualCategory()); 301 aLeaveBlock.setDescription("Forfeited balance transfer amount"); 302 aLeaveBlock.setLeaveAmount(forfeitedAmount.negate()); 303 aLeaveBlock.setAccrualGenerated(true); 304 aLeaveBlock.setTransactionDocId(balanceTransfer.getDocumentHeaderId()); 305 aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER); 306 aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED); 307 aLeaveBlock.setBlockId(0L); 308 309 //Want to store the newly created leave block id on this maintainable object 310 //when the status of the maintenance document encapsulating this maintainable changes 311 //the id will be used to fetch and update the leave block statuses. 312 aLeaveBlock = KRADServiceLocator.getBusinessObjectService().save(aLeaveBlock); 313 314 balanceTransfer.setForfeitedLeaveBlockId(aLeaveBlock.getLmLeaveBlockId()); 315 // save history 316 LeaveBlockHistory lbh = new LeaveBlockHistory(aLeaveBlock); 317 lbh.setAction(LMConstants.ACTION.ADD); 318 TkServiceLocator.getLeaveBlockHistoryService().saveLeaveBlockHistory(lbh); 319 320 leaveBlocks.add(aLeaveBlock); 321 } 322 } 323 324 return balanceTransfer; 325 } 326 } 327 328 public BalanceTransferDao getBalanceTransferDao() { 329 return balanceTransferDao; 330 } 331 332 public void setBalanceTransferDao(BalanceTransferDao balanceTransferDao) { 333 this.balanceTransferDao = balanceTransferDao; 334 } 335 336 @Override 337 public void submitToWorkflow(BalanceTransfer balanceTransfer) 338 throws WorkflowException { 339 340 //balanceTransfer.setStatus(TkConstants.ROUTE_STATUS.ENROUTE); 341 EntityNamePrincipalName principalName = null; 342 if (balanceTransfer.getPrincipalId() != null) { 343 principalName = KimApiServiceLocator.getIdentityService().getDefaultNamesForPrincipalId(balanceTransfer.getPrincipalId()); 344 } 345 346 MaintenanceDocument document = KRADServiceLocatorWeb.getMaintenanceDocumentService().setupNewMaintenanceDocument(BalanceTransfer.class.getName(), 347 "BalanceTransferDocumentType",KRADConstants.MAINTENANCE_NEW_ACTION); 348 349 String personName = (principalName != null && principalName.getDefaultName() != null) ? principalName.getDefaultName().getCompositeName() : StringUtils.EMPTY; 350 String date = TKUtils.formatDate(new java.sql.Date(balanceTransfer.getEffectiveDate().getTime())); 351 document.getDocumentHeader().setDocumentDescription(personName + " (" + balanceTransfer.getPrincipalId() + ") - " + date); 352 Map<String,String[]> params = new HashMap<String,String[]>(); 353 354 KRADServiceLocatorWeb.getMaintenanceDocumentService().setupMaintenanceObject(document, KRADConstants.MAINTENANCE_NEW_ACTION, params); 355 BalanceTransfer btObj = (BalanceTransfer) document.getNewMaintainableObject().getDataObject(); 356 357 btObj.setAccrualCategoryRule(balanceTransfer.getAccrualCategoryRule()); 358 btObj.setEffectiveDate(balanceTransfer.getEffectiveDate()); 359 btObj.setForfeitedAmount(balanceTransfer.getForfeitedAmount()); 360 btObj.setFromAccrualCategory(balanceTransfer.getFromAccrualCategory()); 361 btObj.setPrincipalId(balanceTransfer.getPrincipalId()); 362 btObj.setToAccrualCategory(balanceTransfer.getToAccrualCategory()); 363 btObj.setTransferAmount(balanceTransfer.getTransferAmount()); 364 btObj.setAmountTransferred(balanceTransfer.getAmountTransferred()); 365 btObj.setSstoId(balanceTransfer.getSstoId()); 366 btObj.setDocumentHeaderId(document.getDocumentHeader().getWorkflowDocument().getDocumentId()); 367 /* TkServiceLocator.getBalanceTransferService().saveOrUpdate(btObj); 368 document.getNewMaintainableObject().setDataObject(btObj);*/ 369 KRADServiceLocatorWeb.getDocumentService().saveDocument(document); 370 document.getDocumentHeader().getWorkflowDocument().saveDocument(""); 371 372 document.getDocumentHeader().getWorkflowDocument().route(""); 373 374 } 375 376 @Override 377 public BalanceTransfer transferSsto(BalanceTransfer balanceTransfer) { 378 if(ObjectUtils.isNull(balanceTransfer)) 379 throw new RuntimeException("did not supply a valid BalanceTransfer object."); 380 else { 381 List<LeaveBlock> sstoLbList = TkServiceLocator.getLeaveBlockService().getSSTOLeaveBlocks(balanceTransfer.getPrincipalId(), balanceTransfer.getSstoId(), balanceTransfer.getEffectiveDate()); 382 String leaveDocId = ""; 383 if(CollectionUtils.isNotEmpty(sstoLbList)) { 384 leaveDocId = sstoLbList.get(0).getDocumentId(); 385 } 386 List<LeaveBlock> lbList = new ArrayList<LeaveBlock>(); 387 // create a new leave block with transferred amount, make sure system scheduled timeoff id is added to it 388 LeaveBlock aLeaveBlock = new LeaveBlock(); 389 aLeaveBlock.setPrincipalId(balanceTransfer.getPrincipalId()); 390 aLeaveBlock.setLeaveDate(balanceTransfer.getEffectiveDate()); 391 aLeaveBlock.setEarnCode(balanceTransfer.getCreditedAccrualCategory().getEarnCode()); 392 aLeaveBlock.setAccrualCategory(balanceTransfer.getToAccrualCategory()); 393 aLeaveBlock.setDescription("System Scheduled Time off Amount transferred"); 394 aLeaveBlock.setLeaveAmount(balanceTransfer.getAmountTransferred()); 395 aLeaveBlock.setAccrualGenerated(false); 396 aLeaveBlock.setLeaveBlockType(LMConstants.LEAVE_BLOCK_TYPE.BALANCE_TRANSFER); 397 aLeaveBlock.setRequestStatus(LMConstants.REQUEST_STATUS.REQUESTED); 398 aLeaveBlock.setBlockId(0L); 399 aLeaveBlock.setScheduleTimeOffId(balanceTransfer.getSstoId()); 400 aLeaveBlock.setDocumentId(leaveDocId); 401 402 lbList.add(aLeaveBlock); 403 TkServiceLocator.getLeaveBlockService().saveLeaveBlocks(lbList); 404 405 balanceTransfer.setAccruedLeaveBlockId(aLeaveBlock.getLmLeaveBlockId()); 406 return balanceTransfer; 407 } 408 } 409 410 @Override 411 public List<BalanceTransfer> getBalanceTransfers(String viewPrincipal, 412 Date beginPeriodDate, Date endPeriodDate) { 413 // TODO Auto-generated method stub 414 return balanceTransferDao.getBalanceTransfers(viewPrincipal, beginPeriodDate, endPeriodDate); 415 } 416 417 @Override 418 public void saveOrUpdate(BalanceTransfer balanceTransfer) { 419 // TODO Auto-generated method stub 420 balanceTransferDao.saveOrUpdate(balanceTransfer); 421 } 422 }