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.cam.document.validation.impl;
017
018 import java.util.ArrayList;
019 import java.util.Date;
020 import java.util.HashMap;
021 import java.util.HashSet;
022 import java.util.List;
023 import java.util.Map;
024
025 import org.apache.commons.lang.StringUtils;
026 import org.kuali.kfs.coa.businessobject.Account;
027 import org.kuali.kfs.coa.businessobject.ObjectCode;
028 import org.kuali.kfs.coa.service.ObjectCodeService;
029 import org.kuali.kfs.integration.cam.CapitalAssetManagementModuleService;
030 import org.kuali.kfs.module.cam.CamsConstants;
031 import org.kuali.kfs.module.cam.CamsConstants.DocumentTypeName;
032 import org.kuali.kfs.module.cam.CamsKeyConstants;
033 import org.kuali.kfs.module.cam.CamsPropertyConstants;
034 import org.kuali.kfs.module.cam.businessobject.Asset;
035 import org.kuali.kfs.module.cam.businessobject.AssetGlobal;
036 import org.kuali.kfs.module.cam.businessobject.AssetGlobalDetail;
037 import org.kuali.kfs.module.cam.businessobject.AssetPaymentDetail;
038 import org.kuali.kfs.module.cam.businessobject.AssetType;
039 import org.kuali.kfs.module.cam.document.gl.AssetGlobalGeneralLedgerPendingEntrySource;
040 import org.kuali.kfs.module.cam.document.service.AssetAcquisitionTypeService;
041 import org.kuali.kfs.module.cam.document.service.AssetGlobalService;
042 import org.kuali.kfs.module.cam.document.service.AssetLocationService;
043 import org.kuali.kfs.module.cam.document.service.AssetLocationService.LocationField;
044 import org.kuali.kfs.module.cam.document.service.AssetPaymentService;
045 import org.kuali.kfs.module.cam.document.service.AssetService;
046 import org.kuali.kfs.module.cam.document.service.PaymentSummaryService;
047 import org.kuali.kfs.sys.KFSPropertyConstants;
048 import org.kuali.kfs.sys.businessobject.Building;
049 import org.kuali.kfs.sys.businessobject.FinancialSystemDocumentHeader;
050 import org.kuali.kfs.sys.businessobject.Room;
051 import org.kuali.kfs.sys.context.SpringContext;
052 import org.kuali.kfs.sys.document.authorization.FinancialSystemMaintenanceDocumentAuthorizerBase;
053 import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService;
054 import org.kuali.rice.kns.bo.Campus;
055 import org.kuali.rice.kns.bo.PersistableBusinessObject;
056 import org.kuali.rice.kns.document.Document;
057 import org.kuali.rice.kns.document.MaintenanceDocument;
058 import org.kuali.rice.kns.maintenance.rules.MaintenanceDocumentRuleBase;
059 import org.kuali.rice.kns.service.BusinessObjectService;
060 import org.kuali.rice.kns.service.DataDictionaryService;
061 import org.kuali.rice.kns.service.DateTimeService;
062 import org.kuali.rice.kns.service.KNSServiceLocator;
063 import org.kuali.rice.kns.service.KualiModuleService;
064 import org.kuali.rice.kns.service.ParameterEvaluator;
065 import org.kuali.rice.kns.service.ParameterService;
066 import org.kuali.rice.kns.util.GlobalVariables;
067 import org.kuali.rice.kns.util.KualiDecimal;
068 import org.kuali.rice.kns.util.ObjectUtils;
069 import org.kuali.rice.kns.util.RiceKeyConstants;
070 import org.kuali.rice.kns.web.format.CurrencyFormatter;
071
072 /**
073 * Rule implementation for Asset Global document.
074 */
075 public class AssetGlobalRule extends MaintenanceDocumentRuleBase {
076 private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AssetGlobalRule.class);
077 protected static final Map<LocationField, String> LOCATION_FIELD_MAP = new HashMap<LocationField, String>();
078 static {
079 LOCATION_FIELD_MAP.put(LocationField.CAMPUS_CODE, CamsPropertyConstants.AssetGlobalDetail.CAMPUS_CODE);
080 LOCATION_FIELD_MAP.put(LocationField.BUILDING_CODE, CamsPropertyConstants.AssetGlobalDetail.BUILDING_CODE);
081 LOCATION_FIELD_MAP.put(LocationField.ROOM_NUMBER, CamsPropertyConstants.AssetGlobalDetail.BUILDING_ROOM_NUMBER);
082 LOCATION_FIELD_MAP.put(LocationField.SUB_ROOM_NUMBER, CamsPropertyConstants.AssetGlobalDetail.BUILDING_SUB_ROOM_NUMBER);
083 LOCATION_FIELD_MAP.put(LocationField.CONTACT_NAME, CamsPropertyConstants.AssetGlobalDetail.OFF_CAMPUS_NAME);
084 LOCATION_FIELD_MAP.put(LocationField.STREET_ADDRESS, CamsPropertyConstants.AssetGlobalDetail.OFF_CAMPUS_ADDRESS);
085 LOCATION_FIELD_MAP.put(LocationField.CITY_NAME, CamsPropertyConstants.AssetGlobalDetail.OFF_CAMPUS_CITY_NAME);
086 LOCATION_FIELD_MAP.put(LocationField.STATE_CODE, CamsPropertyConstants.AssetGlobalDetail.OFF_CAMPUS_STATE_CODE);
087 LOCATION_FIELD_MAP.put(LocationField.ZIP_CODE, CamsPropertyConstants.AssetGlobalDetail.OFF_CAMPUS_ZIP_CODE);
088 LOCATION_FIELD_MAP.put(LocationField.COUNTRY_CODE, CamsPropertyConstants.AssetGlobalDetail.OFF_CAMPUS_COUNTRY_CODE);
089 }
090
091 /**
092 * This method checks reference fields when adding new payment into collection.
093 *
094 * @param assetGlobal
095 * @param assetPaymentDetail
096 * @return
097 */
098 protected boolean checkReferenceExists(AssetGlobal assetGlobal, AssetPaymentDetail assetPaymentDetail) {
099 boolean valid = true;
100
101 // validate required field for object code. Skip the error message because the maintenance DD 'defaultExistenceChecks' can
102 // generate the same error message. We won't duplicate it.
103 if (StringUtils.isBlank(assetPaymentDetail.getFinancialObjectCode())) {
104 valid = false;
105 }
106
107 // Validate Financial Posted date
108 if (assetPaymentDetail.getExpenditureFinancialDocumentPostedDate() != null) {
109 valid &= validatePostedDate(assetPaymentDetail);
110
111 }
112
113 if (valid) {
114 // Check for ObjectCode
115
116 if (StringUtils.isNotBlank(assetPaymentDetail.getFinancialObjectCode())) {
117 assetPaymentDetail.refreshReferenceObject(KFSPropertyConstants.OBJECT_CODE);
118 if (ObjectUtils.isNull(assetPaymentDetail.getObjectCode())) {
119 String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(AssetPaymentDetail.class.getName()).getAttributeDefinition(CamsPropertyConstants.AssetPaymentDetail.FINANCIAL_OBJECT_CODE).getLabel();
120 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.FINANCIAL_OBJECT_CODE, RiceKeyConstants.ERROR_EXISTENCE, label);
121 valid &= false;
122 }
123 }
124 }
125
126 if (StringUtils.isBlank(assetGlobal.getAcquisitionTypeCode()) || ObjectUtils.isNull(assetGlobal.getAcquisitionType())) {
127 putFieldError(CamsPropertyConstants.AssetGlobal.ACQUISITION_TYPE_CODE, CamsKeyConstants.AssetGlobal.ERROR_ACQUISITION_TYPE_CODE_REQUIRED);
128 valid &= false;
129 }
130
131 if (StringUtils.isBlank(assetGlobal.getInventoryStatusCode())) {
132 putFieldError(CamsPropertyConstants.AssetGlobal.INVENTORY_STATUS_CODE, CamsKeyConstants.AssetGlobal.ERROR_INVENTORY_STATUS_REQUIRED_FOR_PAYMENT);
133 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.SEQUENCE_NUMBER, CamsKeyConstants.AssetGlobal.ERROR_ASSET_PAYMENT_DEPENDENCY);
134 valid &= false;
135 }
136 if (StringUtils.isBlank(assetGlobal.getCapitalAssetTypeCode())) {
137 putFieldError(CamsPropertyConstants.AssetGlobal.CAPITAL_ASSET_TYPE_CODE, CamsKeyConstants.AssetGlobal.ERROR_ASSET_TYPE_REQUIRED);
138 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.SEQUENCE_NUMBER, CamsKeyConstants.AssetGlobal.ERROR_ASSET_PAYMENT_DEPENDENCY);
139 valid &= false;
140 }
141
142 // check for existence and active when not separating. This can't be done in the DD because we have a condition on it.
143 // Note: Even though on separate the payment lines can't be edited we still can't force the rules because data may have
144 // gone inactive since then.
145 if (!getAssetGlobalService().isAssetSeparate(assetGlobal)) {
146 assetPaymentDetail.refreshReferenceObject(CamsPropertyConstants.AssetPaymentDetail.ACCOUNT);
147 if (StringUtils.isBlank(assetPaymentDetail.getAccountNumber()) || isAccountInvalid(assetPaymentDetail.getAccount())) {
148 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.ACCOUNT_NUMBER, CamsKeyConstants.AssetGlobal.ERROR_PAYMENT_ACCT_NOT_VALID, new String[] { assetPaymentDetail.getChartOfAccountsCode(), assetPaymentDetail.getAccountNumber() });
149 valid &= false;
150 }
151 else if (!StringUtils.isBlank(assetPaymentDetail.getAccountNumber()) && !assetPaymentDetail.getAccount().isActive()) {
152 String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(AssetPaymentDetail.class.getName()).getAttributeDefinition(CamsPropertyConstants.AssetPaymentDetail.ACCOUNT_NUMBER).getLabel();
153 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.ACCOUNT_NUMBER, RiceKeyConstants.ERROR_INACTIVE, label);
154 valid &= false;
155 }
156
157 assetPaymentDetail.refreshReferenceObject(KFSPropertyConstants.SUB_ACCOUNT);
158 if (!StringUtils.isBlank(assetPaymentDetail.getSubAccountNumber()) && ObjectUtils.isNull(assetPaymentDetail.getSubAccount())) {
159 String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(AssetPaymentDetail.class.getName()).getAttributeDefinition(CamsPropertyConstants.AssetPaymentDetail.SUB_ACCOUNT_NUMBER).getLabel();
160 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.SUB_ACCOUNT_NUMBER, RiceKeyConstants.ERROR_EXISTENCE, label);
161 valid &= false;
162 }
163 else if (!StringUtils.isBlank(assetPaymentDetail.getSubAccountNumber()) && !assetPaymentDetail.getSubAccount().isActive()) {
164 String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(AssetPaymentDetail.class.getName()).getAttributeDefinition(CamsPropertyConstants.AssetPaymentDetail.SUB_ACCOUNT_NUMBER).getLabel();
165 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.SUB_ACCOUNT_NUMBER, RiceKeyConstants.ERROR_INACTIVE, label);
166 valid &= false;
167 }
168
169 assetPaymentDetail.refreshReferenceObject(KFSPropertyConstants.OBJECT_CODE);
170 if (!StringUtils.isBlank(assetPaymentDetail.getFinancialObjectCode()) && ObjectUtils.isNull(assetPaymentDetail.getObjectCode())) {
171 String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(AssetPaymentDetail.class.getName()).getAttributeDefinition(CamsPropertyConstants.AssetPaymentDetail.FINANCIAL_OBJECT_CODE).getLabel();
172 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.FINANCIAL_OBJECT_CODE, RiceKeyConstants.ERROR_EXISTENCE, label);
173 valid &= false;
174 }
175 else if (!StringUtils.isBlank(assetPaymentDetail.getFinancialObjectCode()) && !assetPaymentDetail.getObjectCode().isActive()) {
176 String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(AssetPaymentDetail.class.getName()).getAttributeDefinition(CamsPropertyConstants.AssetPaymentDetail.FINANCIAL_OBJECT_CODE).getLabel();
177 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.FINANCIAL_OBJECT_CODE, RiceKeyConstants.ERROR_INACTIVE, label);
178 valid &= false;
179 }
180
181 assetPaymentDetail.refreshReferenceObject(KFSPropertyConstants.SUB_OBJECT_CODE);
182 if (!StringUtils.isBlank(assetPaymentDetail.getFinancialSubObjectCode()) && ObjectUtils.isNull(assetPaymentDetail.getSubObjectCode())) {
183 String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(AssetPaymentDetail.class.getName()).getAttributeDefinition(CamsPropertyConstants.AssetPaymentDetail.SUB_OBJECT_CODE).getLabel();
184 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.SUB_OBJECT_CODE, RiceKeyConstants.ERROR_EXISTENCE, label);
185 valid &= false;
186 }
187 else if (!StringUtils.isBlank(assetPaymentDetail.getFinancialSubObjectCode()) && !assetPaymentDetail.getSubObjectCode().isActive()) {
188 String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(AssetPaymentDetail.class.getName()).getAttributeDefinition(CamsPropertyConstants.AssetPaymentDetail.SUB_OBJECT_CODE).getLabel();
189 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.SUB_OBJECT_CODE, RiceKeyConstants.ERROR_INACTIVE, label);
190 valid &= false;
191 }
192
193 assetPaymentDetail.refreshReferenceObject(KFSPropertyConstants.PROJECT);
194 if (!StringUtils.isBlank(assetPaymentDetail.getProjectCode()) && ObjectUtils.isNull(assetPaymentDetail.getProject())) {
195 String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(AssetPaymentDetail.class.getName()).getAttributeDefinition(CamsPropertyConstants.AssetPaymentDetail.PROJECT_CODE).getLabel();
196 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.PROJECT_CODE, RiceKeyConstants.ERROR_EXISTENCE, label);
197 valid &= false;
198 }
199 else if (!StringUtils.isBlank(assetPaymentDetail.getProjectCode()) && !assetPaymentDetail.getProject().isActive()) {
200 String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(AssetPaymentDetail.class.getName()).getAttributeDefinition(CamsPropertyConstants.AssetPaymentDetail.PROJECT_CODE).getLabel();
201 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.PROJECT_CODE, RiceKeyConstants.ERROR_INACTIVE, label);
202 valid &= false;
203 }
204
205 assetPaymentDetail.refreshReferenceObject(CamsPropertyConstants.AssetPaymentDetail.ORIGINATION);
206 if (!StringUtils.isBlank(assetPaymentDetail.getExpenditureFinancialSystemOriginationCode()) && ObjectUtils.isNull(assetPaymentDetail.getExpenditureFinancialSystemOrigination())) {
207 String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(AssetPaymentDetail.class.getName()).getAttributeDefinition(CamsPropertyConstants.AssetPaymentDetail.ORIGINATION_CODE).getLabel();
208 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.ORIGINATION_CODE, RiceKeyConstants.ERROR_EXISTENCE, label);
209 valid &= false;
210 }
211 else if (!StringUtils.isBlank(assetPaymentDetail.getExpenditureFinancialSystemOriginationCode()) && !assetPaymentDetail.getExpenditureFinancialSystemOrigination().isActive()) {
212 String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(AssetPaymentDetail.class.getName()).getAttributeDefinition(CamsPropertyConstants.AssetPaymentDetail.ORIGINATION_CODE).getLabel();
213 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.ORIGINATION_CODE, RiceKeyConstants.ERROR_INACTIVE, label);
214 valid &= false;
215 }
216
217 if (!StringUtils.isBlank(assetPaymentDetail.getExpenditureFinancialDocumentTypeCode()) && ObjectUtils.isNull(assetPaymentDetail.getExpenditureFinancialSystemDocumentTypeCode())) {
218 String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(AssetPaymentDetail.class.getName()).getAttributeDefinition(CamsPropertyConstants.AssetPaymentDetail.DOCUMENT_TYPE_CODE).getLabel();
219 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.DOCUMENT_TYPE_CODE, RiceKeyConstants.ERROR_EXISTENCE, label);
220 valid &= false;
221 return valid;
222 }
223 if (!StringUtils.isBlank(assetPaymentDetail.getExpenditureFinancialDocumentTypeCode()) && !assetPaymentDetail.getExpenditureFinancialSystemDocumentTypeCode().isActive()) {
224 String label = SpringContext.getBean(DataDictionaryService.class).getDataDictionary().getBusinessObjectEntry(AssetPaymentDetail.class.getName()).getAttributeDefinition(CamsPropertyConstants.AssetPaymentDetail.DOCUMENT_TYPE_CODE).getLabel();
225 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.DOCUMENT_TYPE_CODE, RiceKeyConstants.ERROR_INACTIVE, label);
226 valid &= false;
227 }
228 }
229 return valid;
230 }
231
232 /**
233 * This method checks reference fields when adding one shared location information into collection.
234 *
235 * @param assetGlobalDetail
236 * @return
237 */
238 protected boolean checkReferenceExists(AssetGlobalDetail assetGlobalDetail) {
239 boolean valid = true;
240 if (StringUtils.isNotBlank(assetGlobalDetail.getCampusCode())) {
241 Map<String, Object> criteria = new HashMap<String, Object>();
242 criteria.put(CamsPropertyConstants.Asset.CAMPUS_CODE, assetGlobalDetail.getCampusCode());
243 Campus campus = SpringContext.getBean(KualiModuleService.class).getResponsibleModuleService(Campus.class).getExternalizableBusinessObject(Campus.class, criteria);
244
245 if (ObjectUtils.isNull(campus)) {
246 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.CAMPUS_CODE, CamsKeyConstants.AssetLocation.ERROR_INVALID_CAMPUS_CODE, assetGlobalDetail.getCampusCode());
247 valid &= false;
248 }
249 else if (!campus.isActive()) {
250 String label = getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(Campus.class.getName()).getAttributeDefinition(KFSPropertyConstants.CAMPUS_CODE).getLabel();
251 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.CAMPUS_CODE, RiceKeyConstants.ERROR_INACTIVE, label);
252 valid &= false;
253 }
254 }
255
256 if (StringUtils.isNotBlank(assetGlobalDetail.getBuildingCode())) {
257 Map<String, String> objectKeys = new HashMap<String, String>();
258 objectKeys.put(CamsPropertyConstants.AssetGlobalDetail.CAMPUS_CODE, assetGlobalDetail.getCampusCode());
259 objectKeys.put(CamsPropertyConstants.AssetGlobalDetail.BUILDING_CODE, assetGlobalDetail.getBuildingCode());
260 Building building = (Building) SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(Building.class, objectKeys);
261
262 if (ObjectUtils.isNull(building)) {
263 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.BUILDING_CODE, CamsKeyConstants.AssetLocation.ERROR_INVALID_BUILDING_CODE, assetGlobalDetail.getBuildingCode(), assetGlobalDetail.getCampusCode());
264 valid &= false;
265 }
266 else if (!building.isActive()) {
267 String label = getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(Building.class.getName()).getAttributeDefinition(KFSPropertyConstants.BUILDING_CODE).getLabel();
268 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.BUILDING_CODE, RiceKeyConstants.ERROR_INACTIVE, label);
269 valid &= false;
270 }
271 }
272
273 if (StringUtils.isNotBlank(assetGlobalDetail.getBuildingRoomNumber())) {
274 Map<String, String> objectKeys = new HashMap<String, String>();
275 objectKeys.put(CamsPropertyConstants.AssetGlobalDetail.CAMPUS_CODE, assetGlobalDetail.getCampusCode());
276 objectKeys.put(CamsPropertyConstants.AssetGlobalDetail.BUILDING_CODE, assetGlobalDetail.getBuildingCode());
277 objectKeys.put(CamsPropertyConstants.AssetGlobalDetail.BUILDING_ROOM_NUMBER, assetGlobalDetail.getBuildingRoomNumber());
278 Room room = (Room) SpringContext.getBean(BusinessObjectService.class).findByPrimaryKey(Room.class, objectKeys);
279
280 if (ObjectUtils.isNull(room)) {
281 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.BUILDING_ROOM_NUMBER, CamsKeyConstants.AssetLocation.ERROR_INVALID_ROOM_NUMBER, assetGlobalDetail.getBuildingCode(), assetGlobalDetail.getBuildingRoomNumber(), assetGlobalDetail.getCampusCode());
282 valid &= false;
283 }
284 else if (!room.isActive()) {
285 String label = getDataDictionaryService().getDataDictionary().getBusinessObjectEntry(Room.class.getName()).getAttributeDefinition(KFSPropertyConstants.BUILDING_ROOM_NUMBER).getLabel();
286 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.BUILDING_ROOM_NUMBER, RiceKeyConstants.ERROR_INACTIVE, label);
287 valid &= false;
288 }
289 }
290 return valid;
291 }
292
293
294 protected boolean isCapitalStatus(AssetGlobal assetGlobal) {
295 return getParameterService().getParameterValues(Asset.class, CamsConstants.Parameters.CAPITAL_ASSET_STATUS_CODES).contains(assetGlobal.getInventoryStatusCode());
296 }
297
298 protected boolean isStatusCodeRetired(String statusCode) {
299 return getParameterService().getParameterValues(Asset.class, CamsConstants.Parameters.RETIRED_STATUS_CODES).contains(statusCode);
300 }
301
302 @Override
303 public boolean processCustomAddCollectionLineBusinessRules(MaintenanceDocument document, String collectionName, PersistableBusinessObject line) {
304 boolean success = super.processCustomAddCollectionLineBusinessRules(document, collectionName, line);
305 if (GlobalVariables.getMessageMap().hasErrors()) {
306 return false;
307 }
308
309 AssetGlobal assetGlobal = (AssetGlobal) document.getNewMaintainableObject().getBusinessObject();
310 List<AssetGlobalDetail> assetSharedDetails = assetGlobal.getAssetSharedDetails();
311 if (CamsPropertyConstants.AssetGlobal.ASSET_SHARED_DETAILS.equals(collectionName)) {
312 // handle location information
313 AssetGlobalDetail assetGlobalDetail = (AssetGlobalDetail) line;
314 success &= checkReferenceExists(assetGlobalDetail);
315 success &= validateLocation(assetGlobal, assetGlobalDetail);
316
317 // qty. of assets (unique) to be created
318 success &= validateLocationQuantity(line);
319 }
320 else if (StringUtils.isNotBlank(collectionName) && collectionName.contains(CamsPropertyConstants.AssetGlobalDetail.ASSET_UNIQUE_DETAILS)) {
321 // handle unique information
322 AssetGlobalDetail assetUniqueDetail = (AssetGlobalDetail) line;
323 String campusTagNumber = assetUniqueDetail.getCampusTagNumber();
324 if (StringUtils.isNotBlank(campusTagNumber)) {
325 success &= validateTagDuplication(assetSharedDetails, campusTagNumber);
326 }
327 }
328 else if (CamsPropertyConstants.AssetGlobal.ASSET_PAYMENT_DETAILS.equals(collectionName)) {
329 AssetPaymentDetail assetPaymentDetail = (AssetPaymentDetail) line;
330
331 if (success &= checkReferenceExists(assetGlobal, assetPaymentDetail)) {
332 success &= validatePaymentLine(document, assetGlobal, assetPaymentDetail);
333 success &= checkNegativeOrZeroPayment(document, assetPaymentDetail);
334 }
335 }
336
337
338 // only for "Asset Separate" document
339 if (getAssetGlobalService().isAssetSeparate(assetGlobal)) {
340 // total cost must be > 0
341 success &= validateTotalCostAmount(assetGlobal);
342 }
343 return success;
344 }
345
346 /**
347 * Validated the location quantity
348 *
349 * @param line
350 * @return boolean
351 */
352 protected boolean validateLocationQuantity(PersistableBusinessObject line) {
353 boolean success = true;
354 AssetGlobalDetail assetGlobalDetail = (AssetGlobalDetail) line;
355 if (assetGlobalDetail.getLocationQuantity() <= 0) {
356 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.LOCATION_QUANTITY, CamsKeyConstants.AssetSeparate.ERROR_ZERO_OR_NEGATIVE_LOCATION_QUANTITY, assetGlobalDetail.getLocationQuantity().toString());
357 success &= false;
358 }
359 return success;
360 }
361
362 protected boolean validateTagDuplication(List<AssetGlobalDetail> assetSharedDetails, String campusTagNumber) {
363 boolean success = true;
364 if (!campusTagNumber.equalsIgnoreCase(CamsConstants.Asset.NON_TAGGABLE_ASSET)) {
365 for (AssetGlobalDetail assetSharedDetail : assetSharedDetails) {
366 List<AssetGlobalDetail> assetGlobalUniqueDetails = assetSharedDetail.getAssetGlobalUniqueDetails();
367 for (AssetGlobalDetail assetSharedUniqueDetail : assetGlobalUniqueDetails) {
368 if (campusTagNumber.equalsIgnoreCase(assetSharedUniqueDetail.getCampusTagNumber())) {
369 success &= false;
370 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.CAMPUS_TAG_NUMBER, CamsKeyConstants.AssetGlobal.ERROR_CAMPUS_TAG_NUMBER_DUPLICATE, campusTagNumber);
371 }
372 }
373 }
374 if (success) {
375 List<Asset> tagMatches = getAssetService().findActiveAssetsMatchingTagNumber(campusTagNumber);
376 if (!tagMatches.isEmpty()) {
377 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.CAMPUS_TAG_NUMBER, CamsKeyConstants.AssetGlobal.ERROR_CAMPUS_TAG_NUMBER_DUPLICATE, campusTagNumber);
378 success &= false;
379 }
380 }
381 }
382 return success;
383 }
384
385 protected boolean validateTagDuplication(List<AssetGlobalDetail> assetSharedDetails) {
386 HashSet<String> assetTags = new HashSet<String>();
387 boolean success = true;
388 int parentIndex = -1;
389 int childIndex = -1;
390 for (AssetGlobalDetail assetSharedDetail : assetSharedDetails) {
391 parentIndex++;
392 List<AssetGlobalDetail> assetGlobalUniqueDetails = assetSharedDetail.getAssetGlobalUniqueDetails();
393 for (AssetGlobalDetail assetSharedUniqueDetail : assetGlobalUniqueDetails) {
394 childIndex++;
395 String campusTagNumber = assetSharedUniqueDetail.getCampusTagNumber();
396 if (StringUtils.isNotBlank(campusTagNumber) && !assetTags.add(campusTagNumber) && !campusTagNumber.equalsIgnoreCase(CamsConstants.Asset.NON_TAGGABLE_ASSET)) {
397 success &= false;
398 String errorPath = MAINTAINABLE_ERROR_PREFIX + CamsPropertyConstants.AssetGlobal.ASSET_SHARED_DETAILS + "[" + parentIndex + "]." + CamsPropertyConstants.AssetGlobalDetail.ASSET_UNIQUE_DETAILS + "[" + childIndex + "]";
399 GlobalVariables.getMessageMap().addToErrorPath(errorPath);
400 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.CAMPUS_TAG_NUMBER, CamsKeyConstants.AssetGlobal.ERROR_CAMPUS_TAG_NUMBER_DUPLICATE, campusTagNumber);
401 GlobalVariables.getMessageMap().removeFromErrorPath(errorPath);
402
403 }
404 }
405 childIndex = -1;
406 for (AssetGlobalDetail assetSharedUniqueDetail : assetGlobalUniqueDetails) {
407 childIndex++;
408 String campusTagNumber = assetSharedUniqueDetail.getCampusTagNumber();
409 if (StringUtils.isNotBlank(campusTagNumber) && !campusTagNumber.equalsIgnoreCase(CamsConstants.Asset.NON_TAGGABLE_ASSET)) {
410 List<Asset> tagMatches = getAssetService().findActiveAssetsMatchingTagNumber(campusTagNumber);
411 if (!tagMatches.isEmpty()) {
412 success &= false;
413 String errorPath = MAINTAINABLE_ERROR_PREFIX + CamsPropertyConstants.AssetGlobal.ASSET_SHARED_DETAILS + "[" + parentIndex + "]." + CamsPropertyConstants.AssetGlobalDetail.ASSET_UNIQUE_DETAILS + "[" + childIndex + "]";
414 GlobalVariables.getMessageMap().addToErrorPath(errorPath);
415 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.CAMPUS_TAG_NUMBER, CamsKeyConstants.AssetGlobal.ERROR_CAMPUS_TAG_NUMBER_DUPLICATE, campusTagNumber);
416 GlobalVariables.getMessageMap().removeFromErrorPath(errorPath);
417 }
418 }
419 }
420 }
421 return success;
422 }
423
424 protected boolean validatePaymentLine(MaintenanceDocument maintenanceDocument, AssetGlobal assetGlobal, AssetPaymentDetail assetPaymentDetail) {
425 boolean success = true;
426
427 // If Acquisition type is "New" or "non-capital", check required fields including Document number, Document type code,
428 // Posted date.
429
430 if (getAssetGlobalService().existsInGroup(getAssetGlobalService().getNewAcquisitionTypeCode(), assetGlobal.getAcquisitionTypeCode()) || !getAssetGlobalService().existsInGroup(getAssetGlobalService().getCapitalObjectAcquisitionCodeGroup(), assetGlobal.getAcquisitionTypeCode())) {
431 success &= checkRequiredFieldsForNewOrNonCapital(assetPaymentDetail);
432 }
433 else {
434 // Validate Financial Document Type Code
435 success &= validateDocumentTypeForNonNew(assetGlobal.getAcquisitionTypeCode(), assetPaymentDetail);
436 }
437
438 success &= validateObjectCode(assetPaymentDetail.getObjectCode(), assetGlobal);
439
440
441 return success;
442 }
443
444 /**
445 * "Add Negative Payment" permission check.
446 *
447 * @param maintenanceDocument
448 * @param assetPaymentDetail
449 * @return
450 */
451 protected boolean checkNegativeOrZeroPayment(MaintenanceDocument maintenanceDocument, AssetPaymentDetail assetPaymentDetail) {
452 boolean success = true;
453 FinancialSystemMaintenanceDocumentAuthorizerBase documentAuthorizer = (FinancialSystemMaintenanceDocumentAuthorizerBase) KNSServiceLocator.getDocumentHelperService().getDocumentAuthorizer(maintenanceDocument);
454 boolean isAuthorized = documentAuthorizer.isAuthorized(maintenanceDocument, CamsConstants.CAM_MODULE_CODE, CamsConstants.PermissionNames.ADD_NEGATIVE_PAYMENTS, GlobalVariables.getUserSession().getPerson().getPrincipalId());
455
456 if (!isAuthorized && assetPaymentDetail.getAmount() != null && assetPaymentDetail.getAmount().isNegative()) {
457 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.AMOUNT, CamsKeyConstants.AssetGlobal.ERROR_INVALID_PAYMENT_AMOUNT);
458 success = false;
459 }
460
461 // amount can not be zero for any user
462 if (assetPaymentDetail.getAmount().isZero()) {
463 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.AMOUNT, CamsKeyConstants.AssetGlobal.ERROR_INVALID_PAYMENT_AMOUNT);
464 success = false;
465 }
466 return success;
467 }
468
469 /**
470 * This method check the required fields for acquisition type New .
471 *
472 * @param assetPaymentDetail
473 * @return
474 */
475 protected boolean checkRequiredFieldsForNewOrNonCapital(AssetPaymentDetail assetPaymentDetail) {
476 boolean valid = true;
477
478 if (StringUtils.isBlank(assetPaymentDetail.getExpenditureFinancialDocumentNumber())) {
479 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.DOCUMENT_NUMBER, CamsKeyConstants.AssetGlobal.ERROR_EXPENDITURE_FINANCIAL_DOCUMENT_NUMBER_REQUIRED);
480 valid &= false;
481 }
482 if (assetPaymentDetail.getExpenditureFinancialDocumentPostedDate() == null) {
483 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.DOCUMENT_POSTING_DATE, CamsKeyConstants.AssetGlobal.ERROR_DOCUMENT_POSTING_DATE_REQUIRED);
484 valid &= false;
485 }
486 if (StringUtils.isBlank(assetPaymentDetail.getExpenditureFinancialDocumentTypeCode())) {
487 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.DOCUMENT_TYPE_CODE, CamsKeyConstants.AssetGlobal.ERROR_EXPENDITURE_FINANCIAL_DOCUMENT_TYPE_CODE_REQUIRED);
488 valid &= false;
489 }
490 return valid;
491 }
492
493 /**
494 * Validates the posted date payment posted date can't be a future date
495 *
496 * @param assetPaymentDetail
497 * @return boolean
498 */
499 protected boolean validatePostedDate(AssetPaymentDetail assetPaymentDetail) {
500 boolean valid = true;
501 Date currentDate = SpringContext.getBean(DateTimeService.class).getCurrentDate();
502
503 if (!getAssetPaymentService().extractPostedDatePeriod(assetPaymentDetail)) {
504 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.DOCUMENT_POSTING_FISCAL_YEAR, CamsKeyConstants.AssetGlobal.ERROR_UNIVERSITY_NOT_DEFINED_FOR_DATE, new String[] { assetPaymentDetail.getExpenditureFinancialDocumentPostedDate().toString() });
505 valid &= false;
506 }
507 else if (assetPaymentDetail.getExpenditureFinancialDocumentPostedDate().compareTo(currentDate) > 0) {
508 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.DOCUMENT_POSTING_DATE, CamsKeyConstants.Payment.ERROR_INVALID_DOC_POST_DATE);
509 valid = false;
510 }
511 return valid;
512 }
513
514 /**
515 * When acquisition type code is Capital (Gifts, Transfer-in, State excess, and Found), payment document type code will be
516 * assigned to AA for Add Asset Document.
517 *
518 * @param documentTypeCode
519 * @return
520 */
521 protected boolean validateDocumentTypeForNonNew(String acquisitionTypeCode, AssetPaymentDetail assetPaymentDetail) {
522 String documentTypeCode = assetPaymentDetail.getExpenditureFinancialDocumentTypeCode();
523
524 boolean valid = true;
525 if (StringUtils.isNotBlank(acquisitionTypeCode) && getAssetGlobalService().existsInGroup(getAssetGlobalService().getNonNewAcquisitionCodeGroup(), acquisitionTypeCode)) {
526
527 if (StringUtils.isNotBlank(documentTypeCode) && !CamsConstants.DocumentTypeName.ASSET_ADD_GLOBAL.equalsIgnoreCase(documentTypeCode)) {
528 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetPaymentDetail.DOCUMENT_TYPE_CODE, CamsKeyConstants.AssetGlobal.ERROR_DOCUMENT_TYPE_CODE_NOT_ALLOWED, documentTypeCode);
529 valid = false;
530 }
531 else {
532 // system set document type code as 'AA'
533 assetPaymentDetail.setExpenditureFinancialDocumentTypeCode(CamsConstants.DocumentTypeName.ASSET_ADD_GLOBAL);
534 }
535 }
536 return valid;
537 }
538
539 /**
540 * Check object code is set to capital only when the status is capital.
541 *
542 * @param assetGlobal
543 * @param assetPaymentDetail
544 * @return valid
545 */
546 protected boolean validateObjectCode(ObjectCode objectCode, AssetGlobal assetGlobal) {
547 boolean valid = true;
548
549 // The acquisition type code of (F, G, N, S, T) requires a capital object code.
550 ParameterService parameterService = SpringContext.getBean(ParameterService.class);
551 ParameterEvaluator parameterEvaluator = getParameterService().getParameterEvaluator(AssetGlobal.class, CamsConstants.Parameters.VALID_OBJECT_SUB_TYPES_BY_ACQUISITION_TYPE, CamsConstants.Parameters.INVALID_OBJECT_SUB_TYPES_BY_ACQUISITION_TYPE, assetGlobal.getAcquisitionTypeCode(), objectCode.getFinancialObjectSubTypeCode());
552 valid &= parameterEvaluator.evaluateAndAddError(ObjectCode.class, CamsPropertyConstants.Asset.FINANCIAL_OBJECT_SUB_TYP_CODE, CamsPropertyConstants.AssetPaymentDetail.FINANCIAL_OBJECT_CODE);
553
554 return valid;
555 }
556
557 protected boolean isAccountInvalid(Account account) {
558 return ObjectUtils.isNull(account) || account.isExpired();
559 }
560
561 @Override
562 protected boolean processCustomRouteDocumentBusinessRules(MaintenanceDocument document) {
563 AssetGlobal assetGlobal = (AssetGlobal) document.getNewMaintainableObject().getBusinessObject();
564 List<AssetGlobalDetail> assetSharedDetails = assetGlobal.getAssetSharedDetails();
565 boolean success = super.processCustomRouteDocumentBusinessRules(document);
566 if (GlobalVariables.getMessageMap().hasErrors()) {
567 return false;
568 }
569
570 // need at least one asset location
571 if (assetSharedDetails.isEmpty() || assetSharedDetails.get(0).getAssetGlobalUniqueDetails().isEmpty()) {
572 putFieldError(CamsPropertyConstants.AssetGlobal.ASSET_SHARED_DETAILS, CamsKeyConstants.AssetGlobal.MIN_ONE_ASSET_REQUIRED);
573 success &= false;
574 }
575
576 // Capital Asset must have payment zone.
577 if (isCapitalStatus(assetGlobal) && assetGlobal.getAssetPaymentDetails().isEmpty()) {
578 putFieldError(CamsPropertyConstants.AssetGlobal.ASSET_PAYMENT_DETAILS, CamsKeyConstants.AssetGlobal.MIN_ONE_PAYMENT_REQUIRED);
579 success &= false;
580 }
581 // check total amount for Asset Create
582 if (!getAssetGlobalService().isAssetSeparate(assetGlobal)) {
583 success &= validateAssetTotalAmount(document);
584 }
585 else {
586 // only for "Asset Separate" document
587 if (getAssetPaymentService().getAssetPaymentDetailQuantity(assetGlobal) >= 10) {
588 /*
589 * @TODO KULCAP-828 putFieldError(CamsPropertyConstants.AssetGlobal.ASSET_SHARED_DETAILS,
590 * CamsKeyConstants.AssetSeparate.ERROR_ASSET_SPLIT_MAX_LIMIT); success &= false;
591 */
592 }
593
594 // new source payments must be greater than the capital asset cost amount
595 KualiDecimal totalSeparateSourceAmount = getAssetGlobalService().getUniqueAssetsTotalAmount(assetGlobal);
596 if (totalSeparateSourceAmount.isGreaterThan(assetGlobal.getTotalCostAmount())) {
597 putFieldError(CamsPropertyConstants.AssetGlobal.ASSET_SHARED_DETAILS, CamsKeyConstants.AssetSeparate.ERROR_INVALID_TOTAL_SEPARATE_SOURCE_AMOUNT, new String[] { assetGlobal.getSeparateSourceCapitalAssetNumber().toString() });
598 success &= false;
599 }
600
601 // only active capital assets can be separated
602 assetGlobal.refreshReferenceObject(CamsPropertyConstants.AssetGlobal.SEPARATE_SOURCE_CAPITAL_ASSET);
603 if (ObjectUtils.isNotNull(assetGlobal.getSeparateSourceCapitalAsset())) {
604 if (!getAssetService().isCapitalAsset(assetGlobal.getSeparateSourceCapitalAsset())) {
605 if (StringUtils.isNotBlank(assetGlobal.getAcquisitionTypeCode())) {
606 putFieldError(CamsPropertyConstants.AssetGlobal.ACQUISITION_TYPE_CODE, CamsKeyConstants.AssetSeparate.ERROR_NON_CAPITAL_ASSET_SEPARATE_REQUIRED);
607 success &= false;
608 }
609 }
610 }
611
612 // validate required fields within "Asset Unique Information" tab
613 int sharedIndex = 0;
614 for (AssetGlobalDetail addLocationDetail : assetSharedDetails) {
615 int uniqueIndex = 0;
616 for (AssetGlobalDetail assetGlobalUniqueDetail : addLocationDetail.getAssetGlobalUniqueDetails()) {
617 String errorPath = MAINTAINABLE_ERROR_PREFIX + CamsPropertyConstants.AssetGlobal.ASSET_SHARED_DETAILS + "[" + sharedIndex + "]." + CamsPropertyConstants.AssetGlobalDetail.ASSET_GLOBAL_UNIQUE_DETAILS + "[" + uniqueIndex + "]";
618 GlobalVariables.getMessageMap().addToErrorPath(errorPath);
619 success &= validateCapitalAssetTypeCode(assetGlobalUniqueDetail);
620 success &= validateAssetType(assetGlobalUniqueDetail, sharedIndex, uniqueIndex);
621 success &= validateAssetDescription(assetGlobalUniqueDetail);
622 success &= validateManufacturer(assetGlobalUniqueDetail);
623 success &= validateSeparateSourceAmount(assetGlobalUniqueDetail, document);
624 GlobalVariables.getMessageMap().removeFromErrorPath(errorPath);
625 uniqueIndex++;
626 }
627 sharedIndex++;
628 }
629
630 // total cost must be > 0
631 success &= validateTotalCostAmount(assetGlobal);
632
633 success &= validateAssetTotalCostMatchesPaymentTotalCost(assetGlobal);
634
635 if (getAssetGlobalService().isAssetSeparateByPayment(assetGlobal)) {
636 AssetGlobalRule.validateAssetAlreadySeparated(assetGlobal.getSeparateSourceCapitalAssetNumber());
637 }
638 } // end ASEP
639
640 success &= validateLocationCollection(assetGlobal, assetSharedDetails);
641 success &= validateTagDuplication(assetSharedDetails);
642 return success;
643 }
644
645
646 /**
647 * Validate all separate source amount is above the capital asset threshold amount.
648 *
649 * @param document
650 * @return
651 */
652 protected boolean validateSeparateSourceAmountAboveThreshold(MaintenanceDocument document, AssetGlobalDetail assetGlobalUniqueDetail) {
653 boolean success = true;
654 String capitalizationThresholdAmount = this.getCapitalizationThresholdAmount();
655 KualiDecimal separateAmount = assetGlobalUniqueDetail.getSeparateSourceAmount();
656 // check for the minimal amount only among all separate source amount. if it's above the threshold, safe...
657 if (separateAmount != null && !getAssetService().isDocumentEnrouting(document) && !validateCapitalAssetAmountAboveThreshhold(document, separateAmount, capitalizationThresholdAmount)) {
658 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.SEPARATE_SOURCE_AMOUNT, CamsKeyConstants.AssetSeparate.ERROR_SEPARATE_ASSET_BELOW_THRESHOLD, new String[] { assetGlobalUniqueDetail.getCapitalAssetNumber().toString(), capitalizationThresholdAmount });
659 success = false;
660 }
661 return success;
662 }
663
664 /**
665 * check if amount is above threshold for capital assets for normal user. minTotalPaymentByAsset and maxTotalPaymentByAsset are
666 * used to check against threshold. Due to the decimal rounding, the asset total amount could have 1 cent difference with each
667 * other. We need to pick up the right value for different threshold check.
668 *
669 * @param document
670 * @return
671 */
672 protected boolean validateAssetTotalAmount(MaintenanceDocument document) {
673 boolean success = true;
674 AssetGlobal assetGlobal = (AssetGlobal) document.getNewMaintainableObject().getBusinessObject();
675
676 KualiDecimal minTotalPaymentByAsset = getAssetGlobalService().totalPaymentByAsset(assetGlobal, false);
677 KualiDecimal maxTotalPaymentByAsset = getAssetGlobalService().totalPaymentByAsset(assetGlobal, true);
678 if (minTotalPaymentByAsset.isGreaterThan(maxTotalPaymentByAsset)) {
679 // swap min and max
680 KualiDecimal totalPayment = minTotalPaymentByAsset;
681 minTotalPaymentByAsset = maxTotalPaymentByAsset;
682 maxTotalPaymentByAsset = totalPayment;
683 }
684
685 // Disallow FO change asset total amount during routing. Asset total amount is derived from asset payments total and the
686 // quantity of assets
687 if (getAssetService().isDocumentEnrouting(document) && (!minTotalPaymentByAsset.equals(assetGlobal.getMinAssetTotalAmount()) || !maxTotalPaymentByAsset.equals(assetGlobal.getMaxAssetTotalAmount()))) {
688 putFieldError(CamsPropertyConstants.AssetGlobal.ASSET_PAYMENT_DETAILS, CamsKeyConstants.AssetGlobal.ERROR_CHANGE_ASSET_TOTAL_AMOUNT_DISALLOW, !minTotalPaymentByAsset.equals(assetGlobal.getMinAssetTotalAmount()) ? new String[] { (String) new CurrencyFormatter().format(assetGlobal.getMinAssetTotalAmount()), (String) new CurrencyFormatter().format(minTotalPaymentByAsset) } : new String[] { (String) new CurrencyFormatter().format(assetGlobal.getMaxAssetTotalAmount()), (String) new CurrencyFormatter().format(maxTotalPaymentByAsset) });
689 success = false;
690 }
691
692 // run threshold checking before routing
693 if (!getAssetService().isDocumentEnrouting(document)) {
694 String capitalizationThresholdAmount = getCapitalizationThresholdAmount();
695 if (isCapitalStatus(assetGlobal)) {
696 // check if amount is above threshold for capital asset.
697 if (!validateCapitalAssetAmountAboveThreshhold(document, minTotalPaymentByAsset, capitalizationThresholdAmount)) {
698 putFieldError(CamsPropertyConstants.AssetGlobal.INVENTORY_STATUS_CODE, CamsKeyConstants.AssetGlobal.ERROR_CAPITAL_ASSET_PAYMENT_AMOUNT_MIN, capitalizationThresholdAmount);
699 success = false;
700 }
701 }
702 else {
703 // check if amount is less than threshold for non-capital assets for all users
704 success &= validateNonCapitalAssetAmountBelowThreshold(maxTotalPaymentByAsset, capitalizationThresholdAmount);
705 }
706
707 }
708 return success;
709 }
710
711 /**
712 * Get the capitalization threshold amount from the system parameter setting.
713 *
714 * @return
715 */
716 protected String getCapitalizationThresholdAmount() {
717 return getParameterService().getParameterValue(AssetGlobal.class, CamsConstants.Parameters.CAPITALIZATION_LIMIT_AMOUNT);
718 }
719
720 /**
721 * Validate Capital Asset Amount above the threshold or below the amount for authorized user only.
722 *
723 * @param document
724 * @param assetAmount
725 * @param capitalizationThresholdAmount
726 * @return
727 */
728 protected boolean validateCapitalAssetAmountAboveThreshhold(MaintenanceDocument document, KualiDecimal assetAmount, String capitalizationThresholdAmount) {
729 boolean success = true;
730 FinancialSystemMaintenanceDocumentAuthorizerBase documentAuthorizer = (FinancialSystemMaintenanceDocumentAuthorizerBase) KNSServiceLocator.getDocumentHelperService().getDocumentAuthorizer(document);
731 boolean isOverrideAuthorized = documentAuthorizer.isAuthorized(document, CamsConstants.CAM_MODULE_CODE, CamsConstants.PermissionNames.OVERRIDE_CAPITALIZATION_LIMIT_AMOUNT, GlobalVariables.getUserSession().getPerson().getPrincipalId());
732
733 if (assetAmount.isLessThan(new KualiDecimal(capitalizationThresholdAmount)) && !isOverrideAuthorized) {
734 success = false;
735 }
736 return success;
737 }
738
739 /**
740 * Validate non-capital asset amount below the threshold.
741 *
742 * @param assetAmount
743 * @param capitalizationThresholdAmount
744 * @return
745 */
746 protected boolean validateNonCapitalAssetAmountBelowThreshold(KualiDecimal assetAmount, String capitalizationThresholdAmount) {
747 boolean success = true;
748 if (assetAmount.isGreaterEqual(new KualiDecimal(capitalizationThresholdAmount))) {
749 putFieldError(CamsPropertyConstants.AssetGlobal.INVENTORY_STATUS_CODE, CamsKeyConstants.AssetGlobal.ERROR_NON_CAPITAL_ASSET_PAYMENT_AMOUNT_MAX, capitalizationThresholdAmount);
750 success &= false;
751 }
752 return success;
753 }
754
755 /**
756 * Validate that the total cost of the source asset is not zero or a negative amount.
757 *
758 * @param assetGlobal
759 * @return boolean
760 */
761 protected boolean validateTotalCostAmount(AssetGlobal assetGlobal) {
762 boolean success = true;
763 if (!assetGlobal.getTotalCostAmount().isGreaterThan(KualiDecimal.ZERO)) {
764 GlobalVariables.getMessageMap().putErrorForSectionId(CamsConstants.AssetGlobal.SECTION_ID_ASSET_INFORMATION, CamsKeyConstants.AssetSeparate.ERROR_ZERO_OR_NEGATIVE_DOLLAR_AMOUNT);
765 success &= false;
766 }
767 return success;
768 }
769
770 /**
771 * Validates the capital asset type code. This only checks for the existence of some contents, not whether the contents are valid.
772 *
773 * @param uniqueLocationDetails
774 * @return boolean
775 */
776 protected boolean validateCapitalAssetTypeCode(AssetGlobalDetail uniqueLocationDetails) {
777 boolean success = true;
778 if (StringUtils.isEmpty(uniqueLocationDetails.getCapitalAssetTypeCode())) {
779 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.CAPITAL_ASSET_TYPE_CODE, CamsKeyConstants.AssetSeparate.ERROR_CAPITAL_ASSET_TYPE_CODE_REQUIRED, uniqueLocationDetails.getCapitalAssetTypeCode());
780 success &= false;
781 }
782 return success;
783 }
784
785 /**
786 * Validates the asset description.
787 *
788 * @param uniqueLocationDetails
789 * @return boolean
790 */
791 protected boolean validateAssetDescription(AssetGlobalDetail uniqueLocationDetails) {
792 boolean success = true;
793 if (StringUtils.isEmpty(uniqueLocationDetails.getCapitalAssetDescription())) {
794 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.CAPITAL_ASSET_DESCRIPTION, CamsKeyConstants.AssetSeparate.ERROR_ASSET_DESCRIPTION_REQUIRED, uniqueLocationDetails.getCapitalAssetTypeCode());
795 success &= false;
796 }
797 return success;
798 }
799
800 /**
801 * Validates the manufacturer.
802 *
803 * @param uniqueLocationDetails
804 * @return boolean
805 */
806 protected boolean validateManufacturer(AssetGlobalDetail uniqueLocationDetails) {
807 boolean success = true;
808 if (StringUtils.isEmpty(uniqueLocationDetails.getManufacturerName())) {
809 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.MANUFACTURER_NAME, CamsKeyConstants.AssetSeparate.ERROR_MANUFACTURER_REQUIRED, uniqueLocationDetails.getCapitalAssetTypeCode());
810 success &= false;
811 }
812 return success;
813 }
814
815 /**
816 * Validates the separate source amount.
817 *
818 * @param uniqueLocationDetails
819 * @return boolean
820 */
821 protected boolean validateSeparateSourceAmount(AssetGlobalDetail uniqueLocationDetail, MaintenanceDocument document) {
822 boolean success = true;
823 KualiDecimal separateSourceAmount = uniqueLocationDetail.getSeparateSourceAmount();
824 if (separateSourceAmount == null || separateSourceAmount.isLessEqual(KualiDecimal.ZERO)) {
825 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.SEPARATE_SOURCE_AMOUNT, CamsKeyConstants.AssetSeparate.ERROR_TOTAL_SEPARATE_SOURCE_AMOUNT_REQUIRED);
826 success &= false;
827 }
828 else {
829 // for capital asset separate, validate the minimal separate source amount above the threshold
830 success &= validateSeparateSourceAmountAboveThreshold(document, uniqueLocationDetail);
831 }
832 return success;
833 }
834
835 protected boolean validateLocationCollection(AssetGlobal assetGlobal, List<AssetGlobalDetail> assetSharedDetails) {
836 boolean success = true;
837 // for each shared location info, validate
838 boolean isCapitalAsset = isCapitalStatus(assetGlobal);
839 int index = 0;
840 for (AssetGlobalDetail assetLocationDetail : assetSharedDetails) {
841 String errorPath = MAINTAINABLE_ERROR_PREFIX + CamsPropertyConstants.AssetGlobal.ASSET_SHARED_DETAILS + "[" + index + "]";
842 GlobalVariables.getMessageMap().addToErrorPath(errorPath);
843 success &= SpringContext.getBean(AssetLocationService.class).validateLocation(LOCATION_FIELD_MAP, assetLocationDetail, isCapitalAsset, assetGlobal.getCapitalAssetType());
844 GlobalVariables.getMessageMap().removeFromErrorPath(errorPath);
845 index++;
846 }
847 return success;
848 }
849
850 @Override
851 protected boolean processCustomSaveDocumentBusinessRules(MaintenanceDocument document) {
852 AssetGlobal assetGlobal = (AssetGlobal) document.getNewMaintainableObject().getBusinessObject();
853 boolean success = true;
854 success &= super.processCustomSaveDocumentBusinessRules(document);
855 if (GlobalVariables.getMessageMap().hasErrors()) {
856 return false;
857 }
858
859 String acquisitionTypeCode = assetGlobal.getAcquisitionTypeCode();
860 String statusCode = assetGlobal.getInventoryStatusCode();
861
862 // no need to validate specific fields if document is "Asset Separate"
863 if (!getAssetGlobalService().isAssetSeparate(assetGlobal)) {
864 success &= validateAccount(assetGlobal);
865 if (StringUtils.isNotBlank(acquisitionTypeCode) && StringUtils.isNotBlank(statusCode)) {
866 // check if status code and acquisition type code combination is valid
867 success &= SpringContext.getBean(ParameterService.class).getParameterEvaluator(AssetGlobal.class, CamsConstants.Parameters.VALID_ASSET_STATUSES_BY_ACQUISITION_TYPE, CamsConstants.Parameters.INVALID_ASSET_STATUSES_BY_ACQUISITION_TYPE, acquisitionTypeCode, statusCode).evaluateAndAddError(AssetGlobal.class, CamsPropertyConstants.AssetGlobal.INVENTORY_STATUS_CODE, MAINTAINABLE_ERROR_PREFIX + CamsPropertyConstants.AssetGlobal.INVENTORY_STATUS_CODE);
868 }
869 success &= validateAssetType(assetGlobal);
870 if (isCapitalStatus(assetGlobal)) {
871 success &= validateVendorAndManufacturer(assetGlobal);
872 }
873
874 success &= validatePaymentCollection(document, assetGlobal);
875 }
876 else {
877 // append doc type to existing doc header description
878 if (!document.getDocumentHeader().getDocumentDescription().toLowerCase().contains(CamsConstants.AssetSeparate.SEPARATE_AN_ASSET_DESCRIPTION.toLowerCase())) {
879 document.getDocumentHeader().setDocumentDescription(CamsConstants.AssetSeparate.SEPARATE_AN_ASSET_DESCRIPTION + " " + document.getDocumentHeader().getDocumentDescription());
880 }
881 }
882
883 // System shall only generate GL entries if we have an incomeAssetObjectCode for this acquisitionTypeCode and the statusCode
884 // is for capital assets
885 if ((success && super.processCustomSaveDocumentBusinessRules(document)) && getAssetAcquisitionTypeService().hasIncomeAssetObjectCode(acquisitionTypeCode) && this.isCapitalStatus(assetGlobal)) {
886 if (success &= validateAcquisitionIncomeObjectCode(assetGlobal)) {
887 // create poster
888 AssetGlobalGeneralLedgerPendingEntrySource assetGlobalGlPoster = new AssetGlobalGeneralLedgerPendingEntrySource((FinancialSystemDocumentHeader) document.getDocumentHeader());
889 // create postables
890 getAssetGlobalService().createGLPostables(assetGlobal, assetGlobalGlPoster);
891
892 if (SpringContext.getBean(GeneralLedgerPendingEntryService.class).generateGeneralLedgerPendingEntries(assetGlobalGlPoster)) {
893 assetGlobal.setGeneralLedgerPendingEntries(assetGlobalGlPoster.getPendingEntries());
894 }
895 else {
896 assetGlobalGlPoster.getPendingEntries().clear();
897 }
898 }
899 }
900
901 return success;
902 }
903
904 /**
905 * Locking on separate source asset number
906 *
907 * @param document
908 * @param assetGlobal
909 * @return
910 */
911 protected boolean setAssetLock(MaintenanceDocument document, AssetGlobal assetGlobal) {
912 List<Long> capitalAssetNumbers = new ArrayList<Long>();
913 if (assetGlobal.getSeparateSourceCapitalAssetNumber() != null) {
914 capitalAssetNumbers.add(assetGlobal.getSeparateSourceCapitalAssetNumber());
915 }
916
917 return SpringContext.getBean(CapitalAssetManagementModuleService.class).storeAssetLocks(capitalAssetNumbers, document.getDocumentNumber(), DocumentTypeName.ASSET_SEPARATE, null);
918 }
919
920 /**
921 * Validate offset object code
922 *
923 * @param assetGlobal
924 * @return
925 */
926 protected boolean validateAcquisitionIncomeObjectCode(AssetGlobal assetGlobal) {
927 boolean valid = true;
928 for (AssetPaymentDetail assetPaymentDetail : assetGlobal.getAssetPaymentDetails()) {
929 // check offset object code existence
930 ObjectCode objectCode = SpringContext.getBean(ObjectCodeService.class).getByPrimaryId(assetPaymentDetail.getPostingYear(), assetPaymentDetail.getChartOfAccountsCode(), assetGlobal.getAcquisitionType().getIncomeAssetObjectCode());
931 if (ObjectUtils.isNull(objectCode)) {
932 putFieldError(CamsPropertyConstants.AssetGlobal.ACQUISITION_TYPE_CODE, CamsKeyConstants.AssetGlobal.ERROR_INVALID_ACQUISITION_INCOME_OBJECT_CODE, new String[] { assetGlobal.getAcquisitionType().getIncomeAssetObjectCode(), assetPaymentDetail.getPostingYear().toString(), assetPaymentDetail.getChartOfAccountsCode() });
933 valid = false;
934 }
935 // check Object Code active
936 else if (!objectCode.isActive()) {
937 putFieldError(CamsPropertyConstants.AssetGlobal.ACQUISITION_TYPE_CODE, CamsKeyConstants.GLPosting.ERROR_OBJECT_CODE_FROM_ASSET_OBJECT_CODE_INACTIVE, new String[] { CamsConstants.GLPostingObjectCodeType.INCOME, assetGlobal.getAcquisitionType().getIncomeAssetObjectCode(), assetPaymentDetail.getChartOfAccountsCode() });
938 valid = false;
939 }
940 }
941 return valid;
942 }
943
944 protected boolean validatePaymentCollection(MaintenanceDocument maintenanceDocument, AssetGlobal assetGlobal) {
945 boolean success = true;
946 int index = 0;
947 for (AssetPaymentDetail assetPaymentDetail : assetGlobal.getAssetPaymentDetails()) {
948 String errorPath = MAINTAINABLE_ERROR_PREFIX + CamsPropertyConstants.AssetGlobal.ASSET_PAYMENT_DETAILS + "[" + index + "]";
949 GlobalVariables.getMessageMap().addToErrorPath(errorPath);
950 success &= validatePaymentLine(maintenanceDocument, assetGlobal, assetPaymentDetail);
951 GlobalVariables.getMessageMap().removeFromErrorPath(errorPath);
952 index++;
953 }
954 return success;
955 }
956
957 protected boolean validateVendorAndManufacturer(AssetGlobal assetGlobal) {
958 boolean success = true;
959 if (StringUtils.isBlank(assetGlobal.getVendorName())) {
960 putFieldError(CamsPropertyConstants.AssetGlobal.VENDOR_NAME, CamsKeyConstants.AssetGlobal.ERROR_VENDOR_NAME_REQUIRED);
961 success &= false;
962 }
963 if (StringUtils.isBlank(assetGlobal.getManufacturerName())) {
964 putFieldError(CamsPropertyConstants.AssetGlobal.MFR_NAME, CamsKeyConstants.AssetGlobal.ERROR_MFR_NAME_REQUIRED);
965 success &= false;
966 }
967 return success;
968 }
969
970 @Override
971 public boolean processSaveDocument(Document document) {
972 boolean valid = true;
973 MaintenanceDocument maintenanceDocument = (MaintenanceDocument) document;
974 AssetGlobal assetGlobal = (AssetGlobal) maintenanceDocument.getNewMaintainableObject().getBusinessObject();
975
976 List<AssetGlobalDetail> assetSharedDetails = assetGlobal.getAssetSharedDetails();
977 int index = 0;
978 for (AssetGlobalDetail assetLocationDetail : assetSharedDetails) {
979 String errorPath = MAINTAINABLE_ERROR_PREFIX + CamsPropertyConstants.AssetGlobal.ASSET_SHARED_DETAILS + "[" + index + "]";
980 GlobalVariables.getMessageMap().addToErrorPath(errorPath);
981 valid &= checkReferenceExists(assetLocationDetail);
982 GlobalVariables.getMessageMap().removeFromErrorPath(errorPath);
983
984 // checks that all of the asset types entered in the collection of Unique Details actually exist in persistent storage
985 int indexUniqueDetails = 0;
986 for (AssetGlobalDetail assetGlobalUniqueDetails : assetLocationDetail.getAssetGlobalUniqueDetails()) {
987 valid &= validateAssetType(assetGlobalUniqueDetails, index, indexUniqueDetails);
988 indexUniqueDetails++;
989 }
990 index++;
991 }
992
993
994 // Creates locking representation for this global document. The locking is only applicable for assets that are being split.
995 // The assets that are being created do not need to be locked since they don't exist yet.
996 if (valid && getAssetGlobalService().isAssetSeparate(assetGlobal)) {
997 valid &= setAssetLock(maintenanceDocument, assetGlobal);
998 }
999
1000 return valid && super.processSaveDocument(document);
1001 }
1002
1003 protected boolean validateAccount(AssetGlobal assetGlobal) {
1004 boolean success = true;
1005 assetGlobal.refreshReferenceObject(CamsPropertyConstants.AssetGlobal.ORGANIZATION_OWNER_ACCOUNT);
1006 Account organizationOwnerAccount = assetGlobal.getOrganizationOwnerAccount();
1007
1008 boolean skipAccountAvailiablityCheck;
1009 if (StringUtils.isBlank(assetGlobal.getOrganizationOwnerChartOfAccountsCode()) || StringUtils.isBlank(assetGlobal.getOrganizationOwnerAccountNumber())) {
1010 skipAccountAvailiablityCheck = true;
1011 }
1012 else {
1013 skipAccountAvailiablityCheck = isOrgOwnerAccountFromCab(assetGlobal);
1014 }
1015
1016 // when check if organizationOwnerAccount is existing, use ObjectUtils rather than comparing with 'null' since OJB proxy
1017 // object is used here.
1018 if (!skipAccountAvailiablityCheck && (ObjectUtils.isNull(organizationOwnerAccount) || !organizationOwnerAccount.isActive() || organizationOwnerAccount.isExpired())) {
1019 putFieldError(CamsPropertyConstants.AssetGlobal.ORGANIZATION_OWNER_ACCOUNT_NUMBER, CamsKeyConstants.AssetGlobal.ERROR_OWNER_ACCT_NOT_ACTIVE, new String[] { assetGlobal.getOrganizationOwnerChartOfAccountsCode(), assetGlobal.getOrganizationOwnerAccountNumber() });
1020 success &= false;
1021 }
1022 return success;
1023 }
1024
1025
1026 /**
1027 * Check if organization owner account is set from CAB. We honor all accounting lines from CAB are valid payments even thougth
1028 * they are expired.
1029 *
1030 * @param assetGlobal
1031 * @return
1032 */
1033 protected boolean isOrgOwnerAccountFromCab(AssetGlobal assetGlobal) {
1034 String orgOwnerChartCode = assetGlobal.getOrganizationOwnerChartOfAccountsCode();
1035 String orgOwnerAccountNbr = assetGlobal.getOrganizationOwnerAccountNumber();
1036
1037 if (StringUtils.isBlank(assetGlobal.getOrganizationOwnerChartOfAccountsCode()) || StringUtils.isBlank(assetGlobal.getOrganizationOwnerAccountNumber())) {
1038 return true;
1039 }
1040
1041 if (assetGlobal.isCapitalAssetBuilderOriginIndicator()) {
1042 // If CAB submits, allow use of inactive accounting line from the payments
1043 for (AssetPaymentDetail assetPaymentDetail : assetGlobal.getAssetPaymentDetails()) {
1044 if (orgOwnerChartCode.equalsIgnoreCase(assetPaymentDetail.getChartOfAccountsCode()) && orgOwnerAccountNbr.equalsIgnoreCase(assetPaymentDetail.getAccountNumber())) {
1045 return true;
1046 }
1047 }
1048 }
1049
1050 return false;
1051 }
1052
1053 /**
1054 * Validate location
1055 *
1056 * @param assetGlobal
1057 * @return boolean
1058 */
1059 protected boolean validateLocation(AssetGlobal assetGlobal, AssetGlobalDetail assetGlobalDetail) {
1060 boolean success = true;
1061 if (StringUtils.isBlank(assetGlobal.getInventoryStatusCode())) {
1062 putFieldError(CamsPropertyConstants.AssetGlobal.INVENTORY_STATUS_CODE, CamsKeyConstants.AssetGlobal.ERROR_INVENTORY_STATUS_REQUIRED);
1063 success &= false;
1064 }
1065 success = validateAssetType(assetGlobal);
1066 if (success) {
1067 boolean isCapitalAsset = isCapitalStatus(assetGlobal);
1068 success &= SpringContext.getBean(AssetLocationService.class).validateLocation(LOCATION_FIELD_MAP, assetGlobalDetail, isCapitalAsset, assetGlobal.getCapitalAssetType());
1069 }
1070 else {
1071 putFieldError(CamsPropertyConstants.AssetGlobal.CAPITAL_ASSET_TYPE_CODE, CamsKeyConstants.AssetGlobal.ERROR_ASSET_LOCATION_DEPENDENCY);
1072 }
1073 return success;
1074 }
1075
1076 /**
1077 * Validate asset type at the AssetGlobal level. Only checks that there are contents in the object.
1078 *
1079 * @param assetGlobal
1080 * @return boolean
1081 */
1082 protected boolean validateAssetType(AssetGlobal assetGlobal) {
1083 boolean success = true;
1084 assetGlobal.refreshReferenceObject(CamsPropertyConstants.AssetGlobal.CAPITAL_ASSET_TYPE);
1085 if (ObjectUtils.isNull(assetGlobal.getCapitalAssetType())) {
1086 putFieldError(CamsPropertyConstants.AssetGlobalDetail.ASSET_UNIQUE_DETAILS, CamsKeyConstants.AssetGlobal.ERROR_ASSET_TYPE_REQUIRED);
1087 success &= false;
1088 }
1089
1090 return success;
1091 }
1092
1093 /**
1094 * Validate asset type in the AssetGlobalUniqueDetails level, and ensures the value is in the list of valid types.
1095 * The incoming indices are for creation of the errorPath for the global variable message map, which will determine
1096 * what text field to mark as having a problem. This was written to be called within a loop.
1097 *
1098 * @param assetGlobalUniqueDetails
1099 * @param sharedIndex the index of the shared details within the AssetGlobal
1100 * @param uniqueIndex the index of the unique details within the shared details
1101 * @return boolean
1102 */
1103 protected boolean validateAssetType(AssetGlobalDetail assetGlobalUniqueDetails, Integer sharedIndex, Integer uniqueIndex) {
1104 boolean success = true;
1105 int sharedInd = 0;
1106 int uniqueInd = 0;
1107
1108 // In hindsite, maybe this should have been written to perform the loop-within-loop fully contained within this
1109 // method, and not used within the typical double loop structure of processSaveDocument() and
1110 // processCustomRouteDecumentBusinessRules.
1111
1112 // don't change the value of the incoming param!
1113 if (sharedIndex != null) {
1114 sharedInd = sharedIndex;
1115 }
1116 if (uniqueIndex != null) {
1117 uniqueInd = uniqueIndex;
1118 }
1119
1120 if (ObjectUtils.isNull(assetGlobalUniqueDetails)) {
1121 putFieldError(CamsPropertyConstants.AssetGlobalDetail.ASSET_UNIQUE_DETAILS, CamsKeyConstants.AssetGlobal.ERROR_ASSET_LOCATION_DEPENDENCY);
1122 success &= false;
1123 }
1124
1125 // validate asset type
1126 if (StringUtils.isNotBlank(assetGlobalUniqueDetails.getCapitalAssetTypeCode())) {
1127 AssetType assetType = SpringContext.getBean(BusinessObjectService.class).findBySinglePrimaryKey(AssetType.class, assetGlobalUniqueDetails.getCapitalAssetTypeCode());
1128 if (assetType == null || StringUtils.isBlank(assetType.getCapitalAssetTypeCode())) {
1129 String errorPath = MAINTAINABLE_ERROR_PREFIX + CamsPropertyConstants.AssetGlobal.ASSET_SHARED_DETAILS + "[" + sharedInd + "]." + CamsPropertyConstants.AssetGlobalDetail.ASSET_UNIQUE_DETAILS + "[" + uniqueInd + "]";
1130 GlobalVariables.getMessageMap().addToErrorPath(errorPath);
1131 GlobalVariables.getMessageMap().putError(CamsPropertyConstants.AssetGlobalDetail.CAPITAL_ASSET_TYPE_CODE, CamsKeyConstants.AssetSeparate.ERROR_CAPITAL_ASSET_TYPE_CODE_INVALID, assetGlobalUniqueDetails.getCapitalAssetTypeCode());
1132 GlobalVariables.getMessageMap().removeFromErrorPath(errorPath);
1133 success &= false;
1134 }
1135 }
1136
1137 return success;
1138 }
1139
1140 /**
1141 * Give an error if this asset can't be separated due to mismatching amount on asset and AssetPayment records
1142 *
1143 * @param assetGlobal
1144 * @return validation success of failure
1145 */
1146 public static boolean validateAssetTotalCostMatchesPaymentTotalCost(AssetGlobal assetGlobal) {
1147 PaymentSummaryService paymentSummaryService = SpringContext.getBean(PaymentSummaryService.class);
1148 assetGlobal.refreshReferenceObject(CamsPropertyConstants.AssetGlobal.SEPARATE_SOURCE_CAPITAL_ASSET);
1149 KualiDecimal assetTotalCost = ObjectUtils.isNull(assetGlobal.getSeparateSourceCapitalAsset().getTotalCostAmount()) ? new KualiDecimal(0) : assetGlobal.getSeparateSourceCapitalAsset().getTotalCostAmount();
1150 KualiDecimal paymentTotalCost = paymentSummaryService.calculatePaymentTotalCost(assetGlobal.getSeparateSourceCapitalAsset());
1151 if (!paymentTotalCost.equals(assetTotalCost)) {
1152 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(MAINTAINABLE_ERROR_PREFIX + CamsPropertyConstants.AssetGlobal.SEPARATE_SOURCE_CAPITAL_ASSET_NUMBER, CamsKeyConstants.AssetGlobal.ERROR_SEPARATE_ASSET_TOTAL_COST_NOT_MATCH_PAYMENT_TOTAL_COST);
1153
1154 return false;
1155 }
1156
1157 return true;
1158 }
1159
1160 /**
1161 * Give an error if this asset has already been separated
1162 *
1163 * @param assetGlobal
1164 * @return validation success of failure
1165 */
1166 public static boolean validateAssetAlreadySeparated(Long separateSourceCapitalAssetNumber) {
1167 AssetService assetService = SpringContext.getBean(AssetService.class);
1168
1169 List<String> documentNumbers = assetService.getDocumentNumbersThatSeparatedThisAsset(separateSourceCapitalAssetNumber);
1170
1171 if (!documentNumbers.isEmpty()) {
1172 GlobalVariables.getMessageMap().putErrorWithoutFullErrorPath(MAINTAINABLE_ERROR_PREFIX + CamsPropertyConstants.AssetGlobal.SEPARATE_SOURCE_CAPITAL_ASSET_NUMBER, CamsKeyConstants.AssetGlobal.ERROR_SEPARATE_ASSET_ALREADY_SEPARATED, new String[] { documentNumbers.toString() });
1173
1174 return false;
1175 }
1176
1177 return true;
1178 }
1179
1180 protected ParameterService getParameterService() {
1181 return SpringContext.getBean(ParameterService.class);
1182 }
1183
1184 protected AssetService getAssetService() {
1185 return SpringContext.getBean(AssetService.class);
1186 }
1187
1188 protected AssetPaymentService getAssetPaymentService() {
1189 return SpringContext.getBean(AssetPaymentService.class);
1190 }
1191
1192 protected AssetAcquisitionTypeService getAssetAcquisitionTypeService() {
1193 return SpringContext.getBean(AssetAcquisitionTypeService.class);
1194 }
1195
1196 protected AssetGlobalService getAssetGlobalService() {
1197 return SpringContext.getBean(AssetGlobalService.class);
1198 }
1199 }