001 /* 002 * Copyright 2011 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.kfs.module.endow.document; 017 018 import java.math.BigDecimal; 019 import java.sql.Date; 020 import java.util.Map; 021 022 import org.kuali.kfs.fp.batch.service.impl.ProcurementCardCreateDocumentServiceImpl; 023 import org.kuali.kfs.module.endow.EndowParameterKeyConstants; 024 import org.kuali.kfs.module.endow.businessobject.PooledFundValue; 025 import org.kuali.kfs.module.endow.businessobject.Security; 026 import org.kuali.kfs.module.endow.document.service.SecurityService; 027 import org.kuali.kfs.sys.context.SpringContext; 028 import org.kuali.rice.kew.exception.WorkflowException; 029 import org.kuali.rice.kns.bo.DocumentHeader; 030 import org.kuali.rice.kns.document.MaintenanceDocument; 031 import org.kuali.rice.kns.maintenance.KualiMaintainableImpl; 032 import org.kuali.rice.kns.service.BusinessObjectService; 033 import org.kuali.rice.kns.service.DocumentService; 034 import org.kuali.rice.kns.service.ParameterService; 035 import org.kuali.rice.kns.util.KNSConstants; 036 import org.kuali.rice.kns.util.ObjectUtils; 037 import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument; 038 039 public class PooledFundValueMaintainableImpl extends KualiMaintainableImpl { 040 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ProcurementCardCreateDocumentServiceImpl.class); 041 042 private PooledFundValue newPooledFundValue = null; 043 private PooledFundValue oldPooledFundValue = null; 044 private Security theSecurity = null; 045 046 /** 047 * Overrides the parent method to check the status of the Pooled Fund Value Maintenance document. Business Rule 1: Saving a new 048 * or changed unit value will result in changes to the Security unit values and the associated holding market values. Once the 049 * document has been approved, If it is in the edit-maintenance doc mode, compare unit value between old and new pooled fund 050 * value BOs. If it is in the create-new or copy maintenance doc mode, compare unit value in the new pooled fund value BO with 051 * unit value in the referred Security record. When saving a new or changed unit value 1) Copy current Security Unit Value to 052 * Previous Unit Value 2) Copy current Security Price Date to Previous Price dAte 3) Copy PooledFundValue.POOL_FND_UNIT_VAL to 053 * current Security Unit Price 4) Copy PooledFundValue.VAL_EFF_DT to current Security Price Date 5) Update Security.Security 054 * Source to Pooled Fund Value Business Rule 2:When the DSTRB_AMT is updated, the amount of the distribution is multiplied by 055 * the number of times per year that the institution will distribute income to the account holders (this is an institution 056 * specified parameter: DISTRIBUTION_TIMES_PER_YEAR stored in the KFS parameter table) and the result is copied to END_SEC_T: 057 * SEC_RT for the Pooled Fund SEC_ID. 058 * 059 * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#doRouteStatusChange(org.kuali.rice.kns.bo.DocumentHeader) 060 */ 061 @Override 062 public void doRouteStatusChange(DocumentHeader header) { 063 super.doRouteStatusChange(header); 064 KualiWorkflowDocument workflowDoc = header.getWorkflowDocument(); 065 066 DocumentService documentService = SpringContext.getBean(DocumentService.class); 067 try { 068 MaintenanceDocument maintDoc = (MaintenanceDocument) documentService.getByDocumentHeaderId(header.getDocumentNumber()); 069 070 initializeAttributes(maintDoc); 071 072 // Use the stateIsFinal() method so this code is only executed when the final approval occurs 073 if (workflowDoc.stateIsFinal()) { 074 BigDecimal oldUnitValue = null; 075 BigDecimal oldDistributionPerUnit = null; 076 BigDecimal newUnitValue = newPooledFundValue.getUnitValue(); 077 BigDecimal newDistributionPerUnit = newPooledFundValue.getIncomeDistributionPerUnit(); 078 BigDecimal oldIncomeDistributionPerUnit = oldPooledFundValue.getIncomeDistributionPerUnit(); 079 BigDecimal newIncomeDistributionPerUnit = newPooledFundValue.getIncomeDistributionPerUnit(); 080 Date newValueDate = newPooledFundValue.getValueEffectiveDate(); 081 String newUnitValueSource = EndowParameterKeyConstants.POOLED_FUND_VALUE; 082 String pooledSecurityID = newPooledFundValue.getPooledSecurityID(); 083 084 // Why the following method didn't work? 085 // newPooledFundValue.refreshNonUpdateableReferences(); 086 // Security theSecurity = newPooledFundValue.getPooledFundControl().getSecurity(); 087 SecurityService securityService = SpringContext.getBean(SecurityService.class); 088 Security theSecurity = securityService.getByPrimaryKey(pooledSecurityID); 089 090 ParameterService parameterService = SpringContext.getBean(ParameterService.class); 091 BigDecimal numOfDistributions = new BigDecimal(new Double(parameterService.getParameterValue(PooledFundValue.class, EndowParameterKeyConstants.DISTRIBUTION_TIMES_PER_YEAR)).doubleValue()); 092 BigDecimal interestRate = newDistributionPerUnit.multiply(numOfDistributions); 093 094 /* 095 * Note: There is an assumption here that the unitValue in the oldPooledFundValue is always equal to the unitValue 096 * in the referred Security object. A business rule has to be added for Security impl -- when a Security has the 097 * class code type equals to Pooled Investment, the unit value can't be modify directly through Security maintenance 098 * doc. 099 */ 100 if (KNSConstants.MAINTENANCE_EDIT_ACTION.equals(getMaintenanceAction())) { 101 oldUnitValue = oldPooledFundValue.getUnitValue(); 102 oldDistributionPerUnit = oldPooledFundValue.getIncomeDistributionPerUnit(); 103 if (newUnitValue.compareTo(oldUnitValue) != 0) { 104 compareAndUpdateUnitValue(oldUnitValue, newUnitValue, theSecurity, newValueDate, newUnitValueSource); 105 } 106 if (newDistributionPerUnit.compareTo(oldDistributionPerUnit) != 0) { 107 updateInterestRate(theSecurity, interestRate); 108 } 109 if (newIncomeDistributionPerUnit.compareTo(oldIncomeDistributionPerUnit) != 0) { 110 updateIncomeChangeDate(theSecurity); 111 } 112 } 113 else if (KNSConstants.MAINTENANCE_COPY_ACTION.equals(getMaintenanceAction()) || KNSConstants.MAINTENANCE_NEW_ACTION.equals(getMaintenanceAction())) { 114 oldUnitValue = theSecurity.getUnitValue(); 115 compareAndUpdateUnitValue(oldUnitValue, newUnitValue, theSecurity, newValueDate, newUnitValueSource); 116 updateInterestRate(theSecurity, interestRate); 117 updateIncomeChangeDate(theSecurity); 118 } 119 } 120 } 121 catch (WorkflowException we) { 122 LOG.error("encountered workflow exception while attempting to retrieve PooledFundValueMaintenanceDocument: " + header.getDocumentNumber() + " " + we); 123 throw new RuntimeException(we.getMessage()); 124 } 125 126 } 127 128 private void compareAndUpdateUnitValue(BigDecimal oldUnitValue, BigDecimal newUnitValue, Security theSecurity, Date newValueDate, String newUnitValueSource) { 129 SecurityService securityService = SpringContext.getBean(SecurityService.class); 130 if (newUnitValue.compareTo(oldUnitValue) != 0) { 131 Security security = securityService.updateUnitValue(theSecurity, newUnitValue, newValueDate, newUnitValueSource); 132 SpringContext.getBean(BusinessObjectService.class).save(security); 133 } 134 } 135 136 private void updateInterestRate(Security theSecurity, BigDecimal interestRate) { 137 SecurityService securityService = SpringContext.getBean(SecurityService.class); 138 Security security = securityService.updateInterestRate(theSecurity, interestRate); 139 SpringContext.getBean(BusinessObjectService.class).save(security); 140 } 141 142 /** 143 * Changes IncomeChangeDate in Security to the current date 144 * 145 * @param theSecurity 146 */ 147 private void updateIncomeChangeDate(Security theSecurity) { 148 SecurityService securityService = SpringContext.getBean(SecurityService.class); 149 Security security = securityService.updateIncomeChangeDate(theSecurity); 150 SpringContext.getBean(BusinessObjectService.class).save(security); 151 } 152 153 /** 154 * @see org.kuali.rice.kns.maintenance.KualiMaintainableImpl#processAfterCopy(org.kuali.rice.kns.document.MaintenanceDocument, 155 * java.util.Map) 156 */ 157 @Override 158 public void processAfterCopy(MaintenanceDocument document, Map<String, String[]> parameters) { 159 super.processAfterCopy(document, parameters); 160 initializeAttributes(document); 161 162 // clear out all fields except unit value and income distribution per unit 163 newPooledFundValue.setDistributeIncomeOnDate(null); 164 newPooledFundValue.setDistributeLongTermGainLossOnDate(null); 165 newPooledFundValue.setDistributeShortTermGainLossOnDate(null); 166 newPooledFundValue.setIncomeDistributionComplete(false); 167 newPooledFundValue.setLongTermGainLossDistributionComplete(false); 168 newPooledFundValue.setLongTermGainLossDistributionPerUnit(BigDecimal.ZERO); 169 newPooledFundValue.setShortTermGainLossDistributionComplete(false); 170 newPooledFundValue.setShortTermGainLossDistributionPerUnit(BigDecimal.ZERO); 171 newPooledFundValue.setValuationDate(null); 172 newPooledFundValue.setValueEffectiveDate(null); 173 174 } 175 176 177 /** 178 * Initializes newSecurity and oldSecurity. 179 * 180 * @param document 181 */ 182 private void initializeAttributes(MaintenanceDocument document) { 183 if (newPooledFundValue == null) { 184 newPooledFundValue = (PooledFundValue) document.getNewMaintainableObject().getBusinessObject(); 185 } 186 if (oldPooledFundValue == null) { 187 oldPooledFundValue = (PooledFundValue) document.getOldMaintainableObject().getBusinessObject(); 188 } 189 } 190 }