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 }